Hi, this is a cleanup of shell32/shlexec.c to remove potential buffer overruns. I've also tried to make parts of the code a little easier to follow, without affecting the functionality.
Index: dlls/shell32/shlexec.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/shlexec.c,v retrieving revision 1.23 diff -u -r1.23 shlexec.c --- dlls/shell32/shlexec.c 26 Sep 2003 04:35:01 -0000 1.23 +++ dlls/shell32/shlexec.c 11 Dec 2003 17:30:27 -0000 @@ -53,6 +53,58 @@ WINE_DEFAULT_DEBUG_CHANNEL(exec); + +/*********************************************************************** + * Append the string 'append' onto the string 'appendTo'. 'appendTo' will be + * allocated from the heap, and will be automatically updated on reallocation. + * 'appendLen' can be used to constrain the length of 'append'. Set to -1 to + * append everything. + */ +static BOOL SHELL_heapStrAppend(char** appendTo, const char* append, int appendLen) { + char* tmp; + int len = 1; // need +1 for the null + + // calculate length of data to append + if (*appendTo != NULL) { + len += strlen(*appendTo); + } + + if (append != NULL) { + if (appendLen == -1) { + appendLen = strlen(append); + } + len += appendLen; + } + + // allocate/reallocate the string + tmp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len); + if (tmp == NULL) { + return FALSE; + } + + // update + if (*appendTo != NULL) { + strcpy(tmp, *appendTo); + } + HeapFree(GetProcessHeap(), 0, *appendTo); + *appendTo = tmp; + + // concatenate the strings together + if (append != NULL) strncat(*appendTo, append, appendLen); + + // success! + return TRUE; +} + +/*********************************************************************** + * Free a supplied heap-allocated string. + */ +static void SHELL_heapStrFree(char** appendTo) { + if ((!appendTo) || (!*appendTo)) return; + HeapFree(GetProcessHeap(), 0, *appendTo); + *appendTo = NULL; +} + /*********************************************************************** * this function is supposed to expand the escape sequences found in the registry * some diving reported that the following were used: @@ -65,58 +117,67 @@ * %L seems to be %1 as long filename followed by the 8+3 variation * %S ??? * %* all following parameters (see batfile) + * + * Returns: 0 for error, 1 if completed successfully, 2 if successful, AND lpFile was used */ -static BOOL argify(char* res, int len, const char* fmt, const char* lpFile) +static int SHELL_argify(char** res, const char* fmt, const char* lpFile) { char xlpFile[1024]; - BOOL done = FALSE; + int result = 1; while (*fmt) { - if (*fmt == '%') - { - switch (*++fmt) - { - case '\0': - case '%': - *res++ = '%'; + if (*fmt == '%') { + fmt++; + + switch (*fmt) { + case '\0': // string ended with a single % - copy it across + case '%': // %% - i.e. a single % character + if (!SHELL_heapStrAppend(res, "%", -1)) return 0; break; + case '1': case '*': - if (!done || (*fmt == '1')) - { - if (SearchPathA(NULL, lpFile, ".exe", sizeof(xlpFile), xlpFile, NULL)) - { - strcpy(res, xlpFile); - res += strlen(xlpFile); - } - else - { - strcpy(res, lpFile); - res += strlen(lpFile); + if ((result == 1) || (*fmt == '1')) { + if (SearchPathA(NULL, lpFile, ".exe", sizeof(xlpFile), xlpFile, NULL)) { + if (!SHELL_heapStrAppend(res, xlpFile, -1)) return 0; + } else { + if (!SHELL_heapStrAppend(res, lpFile, -1)) return 0; } } + result = 2; break; + /* - * IE uses this alot for activating things such as windows media - * player. This is not verified to be fully correct but it appears - * to work just fine. - */ + * IE uses this alot for activating things such as windows media + * player. This is not verified to be fully correct but it appears + * to work just fine. + */ case 'L': - strcpy(res,lpFile); - res += strlen(lpFile); + if (!SHELL_heapStrAppend(res, lpFile, -1)) return 0; + result = 2; break; - default: FIXME("Unknown escape sequence %%%c\n", *fmt); + default: + FIXME("Unknown escape sequence %%%c\n", *fmt); + break; } + fmt++; - done = TRUE; } - else - *res++ = *fmt++; + else { // any other character + char* ptr = strchr(fmt, '%'); + if (ptr == NULL) { + if (!SHELL_heapStrAppend(res, fmt, -1)) return 0; + break; + } else { + if (!SHELL_heapStrAppend(res, fmt, ptr - fmt)) return 0; + fmt += (ptr - fmt); + } + } } - *res = '\0'; - return done; + + return result; } /************************************************************************* @@ -228,35 +289,45 @@ static BOOL SHELL_TryAppPath( LPCSTR szName, LPSTR lpResult, void**env) { HKEY hkApp = 0; - char buffer[256]; + char* regKey = NULL; + char value[1024]; LONG len; LONG res; BOOL found = FALSE; if (env) *env = NULL; - sprintf(buffer, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\%s", szName); - res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hkApp); + + // open the key + if (!SHELL_heapStrAppend(®Key, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\", -1)) goto end; + if (!SHELL_heapStrAppend(®Key, szName, -1)) goto end; + res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, regKey, 0, KEY_READ, &hkApp); if (res) goto end; + // read the default value len = MAX_PATH; res = RegQueryValueA(hkApp, NULL, lpResult, &len); if (res) goto end; found = TRUE; + // build the environment if (env) { - DWORD count = sizeof(buffer); - if (!RegQueryValueExA(hkApp, "Path", NULL, NULL, buffer, &count) && buffer[0]) - *env = build_env( buffer ); + DWORD count = sizeof(value); + if (!RegQueryValueExA(hkApp, "Path", NULL, NULL, value, &count)) { + if (value[0]) { + *env = build_env( value ); + } + } } end: + SHELL_heapStrFree(®Key); if (hkApp) RegCloseKey(hkApp); return found; } /************************************************************************* - * SHELL_FindExecutable [Internal] + * SHELL_FindExecutable [Internal] * * Utility for code sharing between FindExecutable and ShellExecute * in: @@ -274,14 +345,16 @@ { char *extension = NULL; /* pointer to file extension */ char tmpext[5]; /* local copy to mung as we please */ - char filetype[256]; /* registry name for this filetype */ + char filetype[257]; /* registry name for this filetype */ LONG filetypelen = 256; /* length of above */ - char command[256]; /* command from registry */ + char command[257]; /* command from registry */ LONG commandlen = 256; /* This is the most DOS can handle :) */ char buffer[256]; /* Used to GetProfileString */ UINT retval = 31; /* default - 'No association was found' */ char *tok; /* token pointer */ char xlpFile[256] = ""; /* result of SearchPath */ + char* regKey = NULL; + char* tmpResult = NULL; TRACE("%s\n", (lpFile != NULL) ? lpFile : "-"); @@ -315,7 +388,7 @@ /* .\FILE.EXE :( */ TRACE("xlpFile=%s,extension=%s\n", xlpFile, extension); - if ((extension == NULL) || (extension == &xlpFile[strlen(xlpFile)])) + if ((extension == NULL) || ((extension - xlpFile) == strlen(xlpFile))) { WARN("Returning 31 - No association\n"); return 31; /* no association */ @@ -355,10 +428,10 @@ TRACE("found %s\n", lpResult); return 33; - /* Greater than 32 to indicate success FIXME According to the - * docs, I should be returning a handle for the - * executable. Does this mean I'm supposed to open the - * executable file or something? More RTFM, I guess... */ + /* Greater than 32 to indicate success FIXME According to the + * docs, I should be returning a handle for the + * executable. Does this mean I'm supposed to open the + * executable file or something? More RTFM, I guess... */ } tok = strtok(NULL, " \t"); } @@ -368,51 +441,54 @@ if (RegQueryValueA(HKEY_CLASSES_ROOT, tmpext, filetype, &filetypelen) == ERROR_SUCCESS) { - filetype[filetypelen] = '\0'; - TRACE("File type: %s\n", filetype); + filetype[filetypelen] = '\0'; + TRACE("File type: %s\n", filetype); - /* Looking for ...buffer\shell\lpOperation\command */ - strcat(filetype, "\\shell\\"); - strcat(filetype, lpOperation); - strcat(filetype, "\\command"); - - if (RegQueryValueA(HKEY_CLASSES_ROOT, filetype, command, - &commandlen) == ERROR_SUCCESS) - { - if (key) strcpy(key, filetype); + /* Looking for ...buffer\shell\lpOperation\command */ + if (!SHELL_heapStrAppend(®Key, filetype, -1)) goto end; + if (!SHELL_heapStrAppend(®Key, "\\shell\\", -1)) goto end; + if (!SHELL_heapStrAppend(®Key, lpOperation, -1)) goto end; + if (!SHELL_heapStrAppend(®Key, "\\command", -1)) goto end; + + if (RegQueryValueA(HKEY_CLASSES_ROOT, regKey, command, + &commandlen) == ERROR_SUCCESS) + { + if (key) strcpy(key, regKey); #if 0 LPSTR tmp; char param[256]; - LONG paramlen = 256; + LONG paramlen = 256; /* FIXME: it seems all Windows version don't behave the same here. * the doc states that this ddeexec information can be found after * the exec names. * on Win98, it doesn't appear, but I think it does on Win2k */ - /* Get the parameters needed by the application - from the associated ddeexec key */ - tmp = strstr(filetype, "command"); - tmp[0] = '\0'; - strcat(filetype, "ddeexec"); + /* Get the parameters needed by the application + from the associated ddeexec key */ + tmp = strstr(filetype, "command"); + tmp[0] = '\0'; + strcat(filetype, "ddeexec"); - if (RegQueryValueA(HKEY_CLASSES_ROOT, filetype, param, ¶mlen) == ERROR_SUCCESS) - { + if (RegQueryValueA(HKEY_CLASSES_ROOT, filetype, param, ¶mlen) == ERROR_SUCCESS) + { strcat(command, " "); strcat(command, param); commandlen += paramlen; - } + } #endif - command[commandlen] = '\0'; - argify(lpResult, sizeof(lpResult), command, xlpFile); - retval = 33; /* FIXME see above */ - } + command[commandlen] = '\0'; + if (!SHELL_argify(&tmpResult, command, xlpFile)) goto end; + strncpy(lpResult, tmpResult, MAX_PATH-1); + lpResult[MAX_PATH-1] = 0; + retval = 33; /* FIXME see above */ + } } else /* Check win.ini */ { - /* Toss the leading dot */ - extension++; - if (GetProfileStringA("extensions", extension, "", command, + /* Toss the leading dot */ + extension++; + if (GetProfileStringA("extensions", extension, "", command, sizeof(command)) > 0) { if (strlen(command) != 0) @@ -434,6 +510,9 @@ } } +end: + SHELL_heapStrFree(&tmpResult); + SHELL_heapStrFree(®Key); TRACE("returning %s\n", lpResult); return retval; } @@ -463,8 +542,7 @@ const char* lpFile, void *env, LPSHELLEXECUTEINFOA sei, SHELL_ExecuteA1632 execfunc) { - char* endkey = key + strlen(key); - char app[256], topic[256], ifexec[256], res[256]; + char app[256], topic[256], ifexec[256]; LONG applen, topiclen, ifexeclen; char* exec; DWORD ddeInst = 0; @@ -472,25 +550,32 @@ HSZ hszApp, hszTopic; HCONV hConv; unsigned ret = 31; + char* regKey = NULL; + char* cmd = NULL; - strcpy(endkey, "\\application"); + if (!SHELL_heapStrAppend(®Key, key, -1)) goto end; + if (!SHELL_heapStrAppend(®Key, "\\application", -1)) goto end; applen = sizeof(app); - if (RegQueryValueA(HKEY_CLASSES_ROOT, key, app, &applen) != ERROR_SUCCESS) + if (RegQueryValueA(HKEY_CLASSES_ROOT, regKey, app, &applen) != ERROR_SUCCESS) { - FIXME("default app name NIY %s\n", key); - return 2; + FIXME("default app name NIY %s\n", regKey); + ret = 2; + goto end; } - strcpy(endkey, "\\topic"); + SHELL_heapStrFree(®Key); + if (!SHELL_heapStrAppend(®Key, key, -1)) goto end; + if (!SHELL_heapStrAppend(®Key, "\\topic", -1)) goto end; topiclen = sizeof(topic); - if (RegQueryValueA(HKEY_CLASSES_ROOT, key, topic, &topiclen) != ERROR_SUCCESS) + if (RegQueryValueA(HKEY_CLASSES_ROOT, regKey, topic, &topiclen) != ERROR_SUCCESS) { strcpy(topic, "System"); } if (DdeInitializeA(&ddeInst, dde_cb, APPCMD_CLIENTONLY, 0L) != DMLERR_NO_ERROR) { - return 2; + ret = 2; + goto end; } hszApp = DdeCreateStringHandleA(ddeInst, app, CP_WINANSI); @@ -505,30 +590,36 @@ if (ret < 32) { TRACE("Couldn't launch\n"); - goto error; + goto end; } hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL); if (!hConv) { TRACE("Couldn't connect. ret=%d\n", ret); ret = 30; /* whatever */ - goto error; + goto end; } - strcpy(endkey, "\\ifexec"); + + SHELL_heapStrFree(®Key); + SHELL_heapStrAppend(®Key, key, -1); + SHELL_heapStrAppend(®Key, "\\ifexec", -1); ifexeclen = sizeof(ifexec); - if (RegQueryValueA(HKEY_CLASSES_ROOT, key, ifexec, &ifexeclen) == ERROR_SUCCESS) + if (RegQueryValueA(HKEY_CLASSES_ROOT, regKey, ifexec, &ifexeclen) == ERROR_SUCCESS) { exec = ifexec; } } - argify(res, sizeof(res), exec, lpFile); - TRACE("%s %s => %s\n", exec, lpFile, res); + if (!SHELL_argify(&cmd, exec, lpFile)) goto end; + TRACE("%s %s => %s\n", exec, lpFile, cmd); - ret = (DdeClientTransaction(res, strlen(res) + 1, hConv, 0L, 0, + ret = (DdeClientTransaction(cmd, strlen(cmd) + 1, hConv, 0L, 0, XTYP_EXECUTE, 10000, &tid) != DMLERR_NO_ERROR) ? 31 : 33; DdeDisconnect(hConv); - error: + +end: + SHELL_heapStrFree(&cmd); + SHELL_heapStrFree(®Key); DdeUninitialize(ddeInst); return ret; } @@ -542,6 +633,8 @@ char cmd[1024] = ""; LONG cmdlen = sizeof(cmd); UINT retval = 31; + char* regKey = NULL; + char* tmpStr = NULL; /* Get the application for the registry */ if (RegQueryValueA(HKEY_CLASSES_ROOT, key, cmd, &cmdlen) == ERROR_SUCCESS) @@ -552,25 +645,29 @@ /* Get the parameters needed by the application from the associated ddeexec key */ - tmp = strstr(key, "command"); - assert(tmp); + if (!SHELL_heapStrAppend(®Key, key, -1)) goto end; + tmp = strstr(regKey, "command"); + if (tmp == NULL) goto end; strcpy(tmp, "ddeexec"); - if (RegQueryValueA(HKEY_CLASSES_ROOT, key, param, ¶mlen) == ERROR_SUCCESS) + if (RegQueryValueA(HKEY_CLASSES_ROOT, regKey, param, ¶mlen) == ERROR_SUCCESS) { - TRACE("Got ddeexec %s => %s\n", key, param); - retval = dde_connect(key, cmd, param, lpFile, env, sei, execfunc); + TRACE("Got ddeexec %s => %s\n", regKey, param); + retval = dde_connect(regKey, cmd, param, lpFile, env, sei, execfunc); } else { /* Is there a replace() function anywhere? */ cmd[cmdlen] = '\0'; - argify(param, sizeof(param), cmd, lpFile); - retval = execfunc(param, env, sei, FALSE); + if (!SHELL_argify(&tmpStr, cmd, lpFile)) goto end; + retval = execfunc(tmpStr, env, sei, FALSE); } } - else TRACE("ooch\n"); + else TRACE("Failed to find application\n"); +end: + SHELL_heapStrFree(®Key); + SHELL_heapStrFree(&tmpStr); return retval; } @@ -591,7 +688,7 @@ if ((lpFile == NULL) || (lpResult == NULL)) { /* FIXME - should throw a warning, perhaps! */ - return (HINSTANCE)2; /* File not found. Close enough, I guess. */ + return (HINSTANCE)2; /* File not found. Close enough, I guess. */ } if (lpDirectory) @@ -622,15 +719,15 @@ */ BOOL WINAPI ShellExecuteExA32 (LPSHELLEXECUTEINFOA sei, SHELL_ExecuteA1632 execfunc) { - CHAR szApplicationName[MAX_PATH],szCommandline[MAX_PATH],szPidl[20],fileName[MAX_PATH]; - LPSTR pos; - void *env; - int gap, len; - char lpstrProtocol[256]; - LPCSTR lpFile,lpOperation; + LPCSTR lpOperation; + void* env = NULL; + char* szApplicationName = NULL; + char* szCommandLine = NULL; + char* szCmd = NULL; + CHAR tmpString[MAX_PATH*2]; + CHAR tmpString2[MAX_PATH*2]; UINT retval = 31; - char cmd[1024]; - BOOL done; + BOOL success = FALSE; TRACE("mask=0x%08lx hwnd=%p verb=%s file=%s parm=%s dir=%s show=0x%08x class=%s\n", sei->fMask, sei->hwnd, debugstr_a(sei->lpVerb), @@ -639,13 +736,6 @@ (sei->fMask & SEE_MASK_CLASSNAME) ? debugstr_a(sei->lpClass) : "not used"); sei->hProcess = NULL; - ZeroMemory(szApplicationName,MAX_PATH); - if (sei->lpFile) - strcpy(szApplicationName, sei->lpFile); - - ZeroMemory(szCommandline,MAX_PATH); - if (sei->lpParameters) - strcpy(szCommandline, sei->lpParameters); if (sei->fMask & (SEE_MASK_INVOKEIDLIST | SEE_MASK_ICON | SEE_MASK_HOTKEY | SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT | @@ -658,60 +748,100 @@ /* process the IDList */ if ( (sei->fMask & SEE_MASK_INVOKEIDLIST) == SEE_MASK_INVOKEIDLIST) /*0x0c*/ { - SHGetPathFromIDListA (sei->lpIDList,szApplicationName); + SHGetPathFromIDListA(sei->lpIDList, tmpString); + if (!SHELL_heapStrAppend(&szApplicationName, tmpString, -1)) goto end; + if (!SHELL_heapStrAppend(&szCommandLine, sei->lpParameters, -1)) goto end; TRACE("-- idlist=%p (%s)\n", sei->lpIDList, szApplicationName); } else { + if (!SHELL_heapStrAppend(&szApplicationName, sei->lpFile, -1)) goto end; + if (sei->fMask & SEE_MASK_IDLIST ) { - pos = strstr(szCommandline, "%I"); - if (pos) - { + char* pos = strstr(sei->lpParameters, "%I"); + if (pos) { + // get the PID LPVOID pv; HGLOBAL hmem = SHAllocShared ( sei->lpIDList, ILGetSize(sei->lpIDList), 0); pv = SHLockShared(hmem,0); - sprintf(szPidl,":%p",pv ); + sprintf(tmpString, ":%p", pv); SHUnlockShared(pv); - gap = strlen(szPidl); - len = strlen(pos)-2; - memmove(pos+gap,pos+2,len); - memcpy(pos,szPidl,gap); + // replace the %I with the PID + if (!SHELL_heapStrAppend(&szCommandLine, sei->lpParameters, pos - sei->lpParameters)) goto end; + if (!SHELL_heapStrAppend(&szCommandLine, tmpString, -1)) goto end; + if (!SHELL_heapStrAppend(&szCommandLine, pos+2, -1)) goto end; } } + + if (szCommandLine == NULL) { + if (!SHELL_heapStrAppend(&szCommandLine, sei->lpParameters, -1)) goto end; + } } if (sei->fMask & (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY)) { - /* launch a document by fileclass like 'WordPad.Document.1' */ + int argFlag; + + /* launch a document by fileclass like 'WordPad.Document.1' */ /* the Commandline contains 'c:\Path\wordpad.exe "%1"' */ - /* FIXME: szCommandline should not be of a fixed size. Plus MAX_PATH is way too short! */ if (sei->fMask & SEE_MASK_CLASSKEY) HCR_GetExecuteCommandEx(sei->hkeyClass, - (sei->fMask & SEE_MASK_CLASSNAME) ? sei->lpClass: NULL, - (sei->lpVerb) ? sei->lpVerb : "open", szCommandline, sizeof(szCommandline)); + (sei->fMask & SEE_MASK_CLASSNAME) ? sei->lpClass : NULL, + (sei->lpVerb) ? sei->lpVerb : "open", + tmpString, sizeof(tmpString)); else if (sei->fMask & SEE_MASK_CLASSNAME) - HCR_GetExecuteCommandA(sei->lpClass, (sei->lpVerb) ? sei->lpVerb : - "open", szCommandline, sizeof(szCommandline)); + HCR_GetExecuteCommandA(sei->lpClass, + (sei->lpVerb) ? sei->lpVerb : "open", + tmpString, sizeof(tmpString)); /* FIXME: get the extension of lpFile, check if it fits to the lpClass */ - TRACE("SEE_MASK_CLASSNAME->'%s', doc->'%s'\n", szCommandline, szApplicationName); + TRACE("SEE_MASK_CLASSNAME->'%s', doc->'%s'\n", tmpString, szApplicationName); - cmd[0] = '\0'; - done = argify(cmd, sizeof(cmd), szCommandline, szApplicationName); - if (!done && szApplicationName[0]) - { - strcat(cmd, " "); - strcat(cmd, szApplicationName); + // try and execute it + SHELL_heapStrFree(&szCmd); + argFlag = SHELL_argify(&szCmd, tmpString, szApplicationName); + if (!argFlag) goto end; + if ((argFlag == 1) && szApplicationName[0]) { + if (!SHELL_heapStrAppend(&szCmd, " ", -1)) goto end; + if (!SHELL_heapStrAppend(&szCmd, szApplicationName, -1)) goto end; } - retval = execfunc(cmd, NULL, sei, FALSE); - if (retval > 32) - return TRUE; - else - return FALSE; + + TRACE("%s\n", szCmd); + + retval = execfunc(szCmd, NULL, sei, FALSE); + + // deal with the results + if (retval > 32) { + success = TRUE; + } else { + success = FALSE; + } + goto end; } + + /* OK, try just executing it first of all */ + SHELL_heapStrFree(&szCmd); + if (!SHELL_heapStrAppend(&szCmd, "\"", -1)) goto end; + if (!SHELL_heapStrAppend(&szCmd, szApplicationName, -1)) goto end; + if (!SHELL_heapStrAppend(&szCmd, "\"", -1)) goto end; + if (szCommandLine[0]) { + if (!SHELL_heapStrAppend(&szCmd, " ", -1)) goto end; + if (!SHELL_heapStrAppend(&szCmd, szCommandLine, -1)) goto end; + } + + + TRACE("%s\n", szCmd); + + retval = execfunc(szCmd, NULL, sei, FALSE); + if (retval > 32) { + success = TRUE; + goto end; + } + + /* We set the default to open, and that should generally work. But that is not really the way the MS docs say to do it. */ if (sei->lpVerb == NULL) @@ -719,85 +849,96 @@ else lpOperation = sei->lpVerb; - /* Else, try to execute the filename */ - TRACE("execute:'%s','%s'\n",szApplicationName, szCommandline); - strcpy(fileName, szApplicationName); - lpFile = fileName; - if (szCommandline[0]) { - strcat(szApplicationName, " "); - strcat(szApplicationName, szCommandline); - } + TRACE("%s/%s/%s\n", sei->lpDirectory, szApplicationName, lpOperation); - retval = execfunc(szApplicationName, NULL, sei, FALSE); - if (retval > 32) - return TRUE; /* Else, try to find the executable */ - cmd[0] = '\0'; - retval = SHELL_FindExecutable(sei->lpDirectory, lpFile, lpOperation, cmd, lpstrProtocol, &env); + retval = SHELL_FindExecutable(sei->lpDirectory, szApplicationName, lpOperation, tmpString, tmpString2, &env); if (retval > 32) /* Found */ { - CHAR szQuotedCmd[MAX_PATH+2]; - /* Must quote to handle case where cmd contains spaces, - * else security hole if malicious user creates executable file "C:\\Program" - */ - if (szCommandline[0]) - sprintf(szQuotedCmd, "\"%s\" %s", cmd, szCommandline); - else - sprintf(szQuotedCmd, "\"%s\"", cmd); - TRACE("%s/%s => %s/%s\n", szApplicationName, lpOperation, szQuotedCmd, lpstrProtocol); - if (*lpstrProtocol) - retval = execute_from_key(lpstrProtocol, szApplicationName, env, sei, execfunc); - else - retval = execfunc(szQuotedCmd, env, sei, FALSE); - if (env) HeapFree( GetProcessHeap(), 0, env ); + // execute it + if (tmpString2[0]) { + TRACE("%s/%s\n", szApplicationName, tmpString2); + + retval = execute_from_key(tmpString2, szApplicationName, env, sei, execfunc); + } else { + // OK, it has found a path to an executable + SHELL_heapStrFree(&szCmd); + if (!SHELL_heapStrAppend(&szCmd, "\"", -1)) goto end; + if (!SHELL_heapStrAppend(&szCmd, tmpString, -1)) goto end; + if (!SHELL_heapStrAppend(&szCmd, "\"", -1)) goto end; + if (szCommandLine[0]) { + if (!SHELL_heapStrAppend(&szCmd, " ", -1)) goto end; + if (!SHELL_heapStrAppend(&szCmd, szCommandLine, -1)) goto end; + } + + TRACE("%s\n", szCmd); + + // execute it + retval = execfunc(szCmd, env, sei, FALSE); + } } - else if (PathIsURLA((LPSTR)lpFile)) /* File not found, check for URL */ + else if (PathIsURLA(szApplicationName)) /* File not found, check for URL */ { - LPSTR lpstrRes; - INT iSize; + int copySize; + int offset = 0; + char* pos; - lpstrRes = strchr(lpFile, ':'); - if (lpstrRes) - iSize = lpstrRes - lpFile; - else - iSize = strlen(lpFile); + // get the protocol of the URL + pos = strchr(szApplicationName, ':'); + if (pos) { + copySize = pos - szApplicationName; + } else { + copySize = -1; + } - TRACE("Got URL: %s\n", lpFile); - /* Looking for ...protocol\shell\lpOperation\command */ - strncpy(lpstrProtocol, lpFile, iSize); - lpstrProtocol[iSize] = '\0'; - strcat(lpstrProtocol, "\\shell\\"); - strcat(lpstrProtocol, lpOperation); - strcat(lpstrProtocol, "\\command"); + /* build up the registry key we need */ + SHELL_heapStrFree(&szCmd); + if (!SHELL_heapStrAppend(&szCmd, szApplicationName, copySize)) goto end; + if (!SHELL_heapStrAppend(&szCmd, "\\shell\\", -1)) goto end; + if (!SHELL_heapStrAppend(&szCmd, lpOperation, -1)) goto end; + if (!SHELL_heapStrAppend(&szCmd, "\\command", -1)) goto end; - /* Remove File Protocol from lpFile */ + /* Remove File Protocol from szApplication */ /* In the case file://path/file */ - if (!strncasecmp(lpFile, "file", iSize)) - { - lpFile += iSize; - while (*lpFile == ':') lpFile++; + if (!strncasecmp(szApplicationName, "file", 4)) { + offset = 4; + while (szApplicationName[offset] == ':') offset++; } - retval = execute_from_key(lpstrProtocol, lpFile, NULL, sei, execfunc); + + TRACE("%s/%s\n", szCmd, szApplicationName+offset); + + // execute it + retval = execute_from_key(szCmd, szApplicationName + offset, NULL, sei, execfunc); } /* Check if file specified is in the form www.??????.*** */ - else if (!strncasecmp(lpFile, "www", 3)) + else if (!strncasecmp(szApplicationName, "www", 3)) { - /* if so, append lpFile http:// and call ShellExecute */ - char lpstrTmpFile[256] = "http://" ; - strcat(lpstrTmpFile, lpFile); - retval = (UINT)ShellExecuteA(sei->hwnd, lpOperation, lpstrTmpFile, NULL, NULL, 0); + SHELL_heapStrFree(&szCmd); + if (!SHELL_heapStrAppend(&szCmd, "http://", -1)) goto end; + if (!SHELL_heapStrAppend(&szCmd, szApplicationName, -1)) goto end; + + TRACE("%s/%s\n", szCmd, lpOperation); + + retval = (UINT)ShellExecuteA(sei->hwnd, lpOperation, szCmd, NULL, NULL, 0); } - if (retval <= 32) - { + /* decide on the result */ + if (retval <= 32) { sei->hInstApp = (HINSTANCE)retval; - return FALSE; + success = FALSE; + } else { + sei->hInstApp = (HINSTANCE)33; + success = TRUE; } - sei->hInstApp = (HINSTANCE)33; - return TRUE; +end: + if (env) HeapFree( GetProcessHeap(), 0, env ); + SHELL_heapStrFree(&szCmd); + SHELL_heapStrFree(&szApplicationName); + SHELL_heapStrFree(&szCommandLine); + return success; } /************************************************************************* @@ -835,7 +976,7 @@ BOOL WINAPI ShellExecuteExAW (LPVOID sei) { if (SHELL_OsIsUnicode()) - return ShellExecuteExW (sei); + return ShellExecuteExW (sei); return ShellExecuteExA32 (sei, SHELL_ExecuteA); }