/*++ Copyright (c) 2000-2002 Microsoft Corporation Module Name: ParseDde.cpp Abstract: Useful routines for parsing DDE commands. History: 08/14/2001 robkenny Moved code inside the ShimLib namespace. 03/12/2002 robkenny Security review. --*/ // // This code was copied from: // \\index1\src\shell\shell32\unicpp\dde.cpp // with minimal processing. // #include "Windows.h" #include "StrSafe.h" #include "ParseDDE.h" #include namespace ShimLib { //-------------------------------------------------------------------------- // Returns a pointer to the first non-whitespace character in a string. LPSTR SkipWhite(LPSTR lpsz) { /* prevent sign extension in case of DBCS */ while (*lpsz && (UCHAR)*lpsz <= ' ') lpsz++; return(lpsz); } LPSTR GetCommandName(LPSTR lpCmd, const char * lpsCommands[], UINT *lpW) { CHAR chT; UINT iCmd = 0; LPSTR lpT; /* Eat any white space. */ lpCmd = SkipWhite(lpCmd); lpT = lpCmd; /* Find the end of the token. */ while (IsCharAlpha(*lpCmd)) lpCmd = CharNextA(lpCmd); /* Temporarily NULL terminate it. */ chT = *lpCmd; *lpCmd = 0; /* Look up the token in a list of commands. */ *lpW = (UINT)-1; while (*lpsCommands) { const char * knownCommand = *lpsCommands; if (!_strcmpi(knownCommand, lpT)) { *lpW = iCmd; break; } iCmd++; ++lpsCommands; } *lpCmd = chT; return(lpCmd); } //-------------------------------------------------------------------------- // Reads a parameter out of a string removing leading and trailing whitespace. // Terminated by , or ). ] [ and ( are not allowed. Exception: quoted // strings are treated as a whole parameter and may contain []() and ,. // Places the offset of the first character of the parameter into some place // and NULL terminates the parameter. // If fIncludeQuotes is false it is assumed that quoted strings will contain single // commands (the quotes will be removed and anything following the quotes will // be ignored until the next comma). If fIncludeQuotes is TRUE, the contents of // the quoted string will be ignored as before but the quotes won't be // removed and anything following the quotes will remain. LPSTR GetOneParameter(LPCSTR lpCmdStart, LPSTR lpCmd, UINT *lpW, BOOL fIncludeQuotes) { LPSTR lpT; switch (*lpCmd) { case ',': *lpW = (UINT) (lpCmd - lpCmdStart); // compute offset *lpCmd++ = 0; /* comma: becomes a NULL string */ break; case '"': if (fIncludeQuotes) { //TraceMsg(TF_DDE, "GetOneParameter: Keeping quotes."); // quoted string... don't trim off " *lpW = (UINT) (lpCmd - lpCmdStart); // compute offset ++lpCmd; while (*lpCmd && *lpCmd != '"') lpCmd = CharNextA(lpCmd); if (!*lpCmd) return(NULL); lpT = lpCmd; ++lpCmd; goto skiptocomma; } else { // quoted string... trim off " ++lpCmd; *lpW = (UINT) (lpCmd - lpCmdStart); // compute offset while (*lpCmd && *lpCmd != '"') lpCmd = CharNextA(lpCmd); if (!*lpCmd) return(NULL); *lpCmd++ = 0; lpCmd = SkipWhite(lpCmd); // If there's a comma next then skip over it, else just go on as // normal. if (*lpCmd == ',') lpCmd++; } break; case ')': return(lpCmd); /* we ought not to hit this */ case '(': case '[': case ']': return(NULL); /* these are illegal */ default: lpT = lpCmd; *lpW = (UINT) (lpCmd - lpCmdStart); // compute offset skiptocomma: while (*lpCmd && *lpCmd != ',' && *lpCmd != ')') { /* Check for illegal characters. */ if (*lpCmd == ']' || *lpCmd == '[' || *lpCmd == '(' ) return(NULL); /* Remove trailing whitespace */ /* prevent sign extension */ if (*lpCmd > ' ') lpT = lpCmd; lpCmd = CharNextA(lpCmd); } /* Eat any trailing comma. */ if (*lpCmd == ',') lpCmd++; /* NULL terminator after last nonblank character -- may write over * terminating ')' but the caller checks for that because this is * a hack. */ #ifdef UNICODE lpT[1] = 0; #else lpT[IsDBCSLeadByte(*lpT) ? 2 : 1] = 0; #endif break; } // Return next unused character. return(lpCmd); } // Extracts an alphabetic string and looks it up in a list of possible // commands, returning a pointer to the character after the command and // sticking the command index somewhere. UINT* GetDDECommands(LPSTR lpCmd, const char * lpsCommands[], BOOL fLFN) { UINT cParm, cCmd = 0; UINT *lpW; UINT *lpRet; LPCSTR lpCmdStart = lpCmd; BOOL fIncludeQuotes = FALSE; if (lpCmd == NULL) return NULL; lpRet = lpW = (UINT*)GlobalAlloc(GPTR, 512L); if (!lpRet) return 0; while (*lpCmd) { /* Skip leading whitespace. */ lpCmd = SkipWhite(lpCmd); /* Are we at a NULL? */ if (!*lpCmd) { /* Did we find any commands yet? */ if (cCmd) goto GDEExit; else goto GDEErrExit; } /* Each command should be inside square brackets. */ if (*lpCmd != '[') goto GDEErrExit; lpCmd++; /* Get the command name. */ lpCmd = GetCommandName(lpCmd, lpsCommands, lpW); if (*lpW == (UINT)-1) goto GDEErrExit; // We need to leave quotes in for the first param of an AddItem. if (fLFN && *lpW == 2) { //TraceMsg(TF_DDE, "GetDDECommands: Potential LFN AddItem command..."); fIncludeQuotes = TRUE; } lpW++; /* Start with zero parms. */ cParm = 0; lpCmd = SkipWhite(lpCmd); /* Check for opening '(' */ if (*lpCmd == '(') { lpCmd++; /* Skip white space and then find some parameters (may be none). */ lpCmd = SkipWhite(lpCmd); while (*lpCmd != ')') { if (!*lpCmd) goto GDEErrExit; // Only the first param of the AddItem command needs to // handle quotes from LFN guys. if (fIncludeQuotes && (cParm != 0)) fIncludeQuotes = FALSE; /* Get the parameter. */ lpCmd = GetOneParameter(lpCmdStart, lpCmd, lpW + (++cParm), fIncludeQuotes); if (!lpCmd) goto GDEErrExit; /* HACK: Did GOP replace a ')' with a NULL? */ if (!*lpCmd) break; /* Find the next one or ')' */ lpCmd = SkipWhite(lpCmd); } // Skip closing bracket. lpCmd++; /* Skip the terminating stuff. */ lpCmd = SkipWhite(lpCmd); } /* Set the count of parameters and then skip the parameters. */ *lpW++ = cParm; lpW += cParm; /* We found one more command. */ cCmd++; /* Commands must be in square brackets. */ if (*lpCmd != ']') goto GDEErrExit; lpCmd++; } GDEExit: /* Terminate the command list with -1. */ *lpW = (UINT)-1; return lpRet; GDEErrExit: GlobalFree(lpW); return(0); } BOOL SHTestTokenMembership (HANDLE hToken, ULONG ulRID) { static SID_IDENTIFIER_AUTHORITY sSystemSidAuthority = SECURITY_NT_AUTHORITY; BOOL fResult; PSID pSIDLocalGroup; fResult = FALSE; if (AllocateAndInitializeSid(&sSystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, ulRID, 0, 0, 0, 0, 0, 0, &pSIDLocalGroup) != FALSE) { if (CheckTokenMembership(hToken, pSIDLocalGroup, &fResult) == FALSE) { //TraceMsg(TF_WARNING, "shell32: SHTestTokenMembership call to advapi32!CheckTokenMembership failed with error %d", GetLastError()); fResult = FALSE; } (void*)FreeSid(pSIDLocalGroup); } return(fResult); } BOOL IsUserAnAdmin() { return(SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_ADMINS)); } // Map the group name to a proper path taking care of the startup group and // app hacks on the way. void GetGroupPath(LPCSTR pszName, CString & csPath, DWORD /*dwFlags*/, INT iCommonGroup) { BOOL bCommonGroup; if (IsUserAnAdmin()) { if (iCommonGroup == 0) { bCommonGroup = FALSE; } else if (iCommonGroup == 1) { bCommonGroup = TRUE; } else { // // Administrators get common groups created by default // when the setup application doesn't specificly state // what kind of group to create. This feature can be // turned off in the cabinet state flags. // //CABINETSTATE cs; //ReadCabinetState(&cs, sizeof(cs)); //if (cs.fAdminsCreateCommonGroups) { // bFindPersonalGroup = TRUE; // bCommonGroup = FALSE; // This might get turned on later // // if find is unsuccessful //} else { // bCommonGroup = FALSE; //} bCommonGroup = TRUE; } } else { // // Regular users can't create common group items. // bCommonGroup = FALSE; } // Build a path to the directory if (bCommonGroup) { SHGetSpecialFolderPathW(csPath, CSIDL_COMMON_PROGRAMS, NULL); } else { SHGetSpecialFolderPathW(csPath, CSIDL_PROGRAMS, NULL); } CString csName(pszName); csPath.AppendPath(csName); } }; // end of namespace ShimLib