/********************************************************************* *********************************************************************/ #include #include #include #include #include #include #include const WCHAR* g_wszRegKey = L"Software\\Microsoft\\WebChangelistEditor"; BOOL IsNewOrPendingChangelist(WCHAR *wszFilename); BOOL CalculateHash(WCHAR *wszFilename, WCHAR *wszHash); void OpenWebEditor(WCHAR *wszURL, WCHAR *wszFilename); void OpenAlternateEditor(WCHAR *wszFilename); void PrintUsage() { wprintf(L"Usage: webchange \n\n"); wprintf(L"If the temp file is a \"pending\" or \"new\" Source Depot changelist:\n"); wprintf(L"\tWebChange opens the specified web page and fills in the hash of the\n"); wprintf(L"\tfile to complete the query string. The web page should host the\n"); wprintf(L"\tWebChangelistEditor ActiveX control, which can be initialized with the\n"); wprintf(L"\tgiven hash to edit the changelist.\n"); wprintf(L"If the temp file is anything else:\n"); wprintf(L"\tWebChange calls %%SDALTFORMEDITOR%% with the name of the temp file.\n"); wprintf(L"\nExample: webchange http://ntserver/submit.asp?key= d:\\temp\\t3104t1.tmp\n"); } int __cdecl wmain(DWORD argc, LPWSTR argv[]) { WCHAR *wszURL; WCHAR *wszFilename; // Parse arguments if (argc != 3) { PrintUsage(); return 1; // Fail } if ((argv[1][0] == L'/') || (argv[1][0] == L'-') || (argv[2][0] == L'/') || (argv[2][0] == L'-')) { PrintUsage(); return 1; // Fail } if ((wcslen(argv[1]) >= MAX_PATH) || (wcslen(argv[2]) >= MAX_PATH)) { PrintUsage(); return 1; // Fail } wszURL = argv[1]; wszFilename = argv[2]; if (IsNewOrPendingChangelist(wszFilename)) { OpenWebEditor(wszURL, wszFilename); } else { OpenAlternateEditor(wszFilename); } } void OpenWebEditor(WCHAR *wszURL, WCHAR *wszFilename) { WCHAR *wszCommand = NULL; WCHAR wszName[MAX_PATH]; WCHAR wszHash[41]; DWORD dwRet; PROCESS_INFORMATION ProcessInfo; STARTUPINFOW StartupInfo; HKEY hKey = NULL; HANDLE hWait[2]; HANDLE hEvent = NULL; ProcessInfo.hProcess = NULL; if (!CalculateHash(wszFilename, wszHash)) { // Error message is printed by CalculateHash function return; } // Build the program name starting with the program files directory name if (!SHGetSpecialFolderPathW(NULL, // hWnd wszName, // Path Out CSIDL_PROGRAM_FILES, // Folder ID FALSE) || (wcslen(wszName) > (MAX_PATH - 100))) { wprintf(L"WebChange Error: Unable to find Program Files directory\n"); return; } // Finish building program name wcscat(wszName, L"\\Internet Explorer\\IEXPLORE.exe"); // Allocate the command string wszCommand = (WCHAR*) malloc((1 + wcslen(wszName) + 2 + wcslen(wszURL) + wcslen(wszHash) + 1) * sizeof(WCHAR)); if (wszCommand == NULL) { wprintf(L"WebChange Error: Out of Memory\n"); goto Done; } // Build the command string wcscpy(wszCommand, L"\""); wcscat(wszCommand, wszName); wcscat(wszCommand, L"\" "); wcscat(wszCommand, wszURL); wcscat(wszCommand, wszHash); // Create our event for registry notification hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (hEvent == NULL) { wprintf(L"WebChange Error: Unable to create event\n"); goto Done; } // Create the Registry Value that the ActiveX control needs // Open our Key: if (RegCreateKeyExW(HKEY_CURRENT_USER, g_wszRegKey, 0, NULL, 0, KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_NOTIFY, NULL, &hKey, NULL) != ERROR_SUCCESS) { wprintf(L"WebChange Error: Failed to open the registry key\n"); hKey = NULL; // Just to make sure it does not get closed in cleanup goto Done; } // Set the specified value: if (FAILED(RegSetValueExW(hKey, wszHash, 0, REG_SZ, (LPBYTE)wszFilename, (wcslen(wszFilename) + 1) * sizeof(WCHAR)))) { wprintf(L"WebChange Error: Failed to create registry value\n"); goto Done; } // Watch our key for changes if (RegNotifyChangeKeyValue(hKey, FALSE, REG_NOTIFY_CHANGE_LAST_SET, hEvent, TRUE) != ERROR_SUCCESS) { wprintf(L"WebChange Error: Failed to set registry notification\n"); goto Done; } // Initialize StartupInfo structure memset(&StartupInfo, 0, sizeof(STARTUPINFOW)); StartupInfo.cb = sizeof(STARTUPINFOW); // Call IE and open to the specified page if (CreateProcessW(wszName, wszCommand, // command line string NULL, // SD NULL, // SD FALSE, // handle inheritance option CREATE_NEW_PROCESS_GROUP, // creation flags NULL, // new environment block NULL, // current directory name &StartupInfo, // startup information &ProcessInfo)) // process information { // Close the Thread handle. I don't use it. CloseHandle(ProcessInfo.hThread); hWait[0] = hEvent; hWait[1] = ProcessInfo.hProcess; // Wait until IE exits or our Key is changed. WaitAgain: dwRet = WaitForMultipleObjects(2, hWait, FALSE, INFINITE); if (dwRet == WAIT_OBJECT_0) { // Then the Registry was modified. // First reset the event. if (!ResetEvent(hEvent)) { goto Done; } // Then restart the Notify on the registry key if (RegNotifyChangeKeyValue(hKey, FALSE, REG_NOTIFY_CHANGE_LAST_SET, hEvent, TRUE) != ERROR_SUCCESS) { goto Done; } // Then check to see if our value is still there // We do this last to avoid race issues if (RegQueryValueExW(hKey, wszHash, 0, NULL, NULL, NULL) == ERROR_SUCCESS) { // The key is still there, so wait again goto WaitAgain; } else { // The key was deleted, so exit goto Done; } } if (dwRet == (WAIT_OBJECT_0 + 1)) { // Then IE was closed. // Attempt to delete our value whether it's there or not RegDeleteValueW(hKey, wszHash); } } else { ProcessInfo.hProcess = NULL; // Just to be sure. wprintf(L"WebChange Error %08X while attempting to execute:\n%s\n", GetLastError(), wszCommand); } Done: if (hEvent) { CloseHandle(hEvent); } if (ProcessInfo.hProcess) { CloseHandle(ProcessInfo.hProcess); } if (hKey) { RegCloseKey(hKey); } if (wszCommand) { free(wszCommand); } } void OpenAlternateEditor(WCHAR *wszFilename) { WCHAR *wszCommand; wszCommand = (WCHAR*) malloc((18 + wcslen(wszFilename) + 1) * sizeof(WCHAR)); if (wszCommand == NULL) { wprintf(L"WebChange Error: Out of Memory\n"); } // Build the command string wcscpy(wszCommand, L"%SDALTFORMEDITOR% "); wcscat(wszCommand, wszFilename); // Execute the alternate editor and wait for it to return. if (_wsystem(wszCommand) == -1) { wprintf(L"WebChange Error: Could not execute: %s\n", wszCommand); } free(wszCommand); } BOOL CalculateHash(WCHAR *wszFilename, WCHAR *wszHash) { CRYPT_HASH_BLOB SHA1; HANDLE hFile; // Initialize the Hash structure SHA1.pbData = (BYTE*)malloc(20); if (SHA1.pbData) { SHA1.cbData = 20; } else { wprintf(L"WebChange Error: Out of Memory\n"); return FALSE; } // Open the file hFile = CreateFileW(wszFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (!hFile) { wprintf(L"WebChange Error: Unable to open %s\n", wszFilename); return FALSE; } // Create the hash of the file using the catalog function if (!CryptCATAdminCalcHashFromFileHandle(hFile, &SHA1.cbData, SHA1.pbData, NULL)) { CloseHandle(hFile); wprintf(L"WebChange Error: Failed to create file hash\n"); return FALSE; } CloseHandle(hFile); for (DWORD j = 0; j