Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

443 lines
13 KiB

  1. /*********************************************************************
  2. *********************************************************************/
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <windows.h>
  6. #include <shlwapi.h>
  7. #include <shlobj.h>
  8. #include <unicode.h>
  9. #include <mscat.h>
  10. const WCHAR* g_wszRegKey = L"Software\\Microsoft\\WebChangelistEditor";
  11. BOOL IsNewOrPendingChangelist(WCHAR *wszFilename);
  12. BOOL CalculateHash(WCHAR *wszFilename, WCHAR *wszHash);
  13. void OpenWebEditor(WCHAR *wszURL, WCHAR *wszFilename);
  14. void OpenAlternateEditor(WCHAR *wszFilename);
  15. void PrintUsage()
  16. {
  17. wprintf(L"Usage: webchange <URL with query string> <temp filename>\n\n");
  18. wprintf(L"If the temp file is a \"pending\" or \"new\" Source Depot changelist:\n");
  19. wprintf(L"\tWebChange opens the specified web page and fills in the hash of the\n");
  20. wprintf(L"\tfile to complete the query string. The web page should host the\n");
  21. wprintf(L"\tWebChangelistEditor ActiveX control, which can be initialized with the\n");
  22. wprintf(L"\tgiven hash to edit the changelist.\n");
  23. wprintf(L"If the temp file is anything else:\n");
  24. wprintf(L"\tWebChange calls %%SDALTFORMEDITOR%% with the name of the temp file.\n");
  25. wprintf(L"\nExample: webchange http://ntserver/submit.asp?key= d:\\temp\\t3104t1.tmp\n");
  26. }
  27. int __cdecl wmain(DWORD argc, LPWSTR argv[])
  28. {
  29. WCHAR *wszURL;
  30. WCHAR *wszFilename;
  31. // Parse arguments
  32. if (argc != 3)
  33. {
  34. PrintUsage();
  35. return 1; // Fail
  36. }
  37. if ((argv[1][0] == L'/') ||
  38. (argv[1][0] == L'-') ||
  39. (argv[2][0] == L'/') ||
  40. (argv[2][0] == L'-'))
  41. {
  42. PrintUsage();
  43. return 1; // Fail
  44. }
  45. if ((wcslen(argv[1]) >= MAX_PATH) ||
  46. (wcslen(argv[2]) >= MAX_PATH))
  47. {
  48. PrintUsage();
  49. return 1; // Fail
  50. }
  51. wszURL = argv[1];
  52. wszFilename = argv[2];
  53. if (IsNewOrPendingChangelist(wszFilename))
  54. {
  55. OpenWebEditor(wszURL, wszFilename);
  56. }
  57. else
  58. {
  59. OpenAlternateEditor(wszFilename);
  60. }
  61. }
  62. void OpenWebEditor(WCHAR *wszURL, WCHAR *wszFilename)
  63. {
  64. WCHAR *wszCommand = NULL;
  65. WCHAR wszName[MAX_PATH];
  66. WCHAR wszHash[41];
  67. DWORD dwRet;
  68. PROCESS_INFORMATION ProcessInfo;
  69. STARTUPINFOW StartupInfo;
  70. HKEY hKey = NULL;
  71. HANDLE hWait[2];
  72. HANDLE hEvent = NULL;
  73. ProcessInfo.hProcess = NULL;
  74. if (!CalculateHash(wszFilename, wszHash))
  75. {
  76. // Error message is printed by CalculateHash function
  77. return;
  78. }
  79. // Build the program name starting with the program files directory name
  80. if (!SHGetSpecialFolderPathW(NULL, // hWnd
  81. wszName, // Path Out
  82. CSIDL_PROGRAM_FILES, // Folder ID
  83. FALSE) ||
  84. (wcslen(wszName) > (MAX_PATH - 100)))
  85. {
  86. wprintf(L"WebChange Error: Unable to find Program Files directory\n");
  87. return;
  88. }
  89. // Finish building program name
  90. wcscat(wszName, L"\\Internet Explorer\\IEXPLORE.exe");
  91. // Allocate the command string
  92. wszCommand = (WCHAR*) malloc((1 + wcslen(wszName) + 2 + wcslen(wszURL) +
  93. wcslen(wszHash) + 1) * sizeof(WCHAR));
  94. if (wszCommand == NULL)
  95. {
  96. wprintf(L"WebChange Error: Out of Memory\n");
  97. goto Done;
  98. }
  99. // Build the command string
  100. wcscpy(wszCommand, L"\"");
  101. wcscat(wszCommand, wszName);
  102. wcscat(wszCommand, L"\" ");
  103. wcscat(wszCommand, wszURL);
  104. wcscat(wszCommand, wszHash);
  105. // Create our event for registry notification
  106. hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  107. if (hEvent == NULL)
  108. {
  109. wprintf(L"WebChange Error: Unable to create event\n");
  110. goto Done;
  111. }
  112. // Create the Registry Value that the ActiveX control needs
  113. // Open our Key:
  114. if (RegCreateKeyExW(HKEY_CURRENT_USER,
  115. g_wszRegKey,
  116. 0,
  117. NULL,
  118. 0,
  119. KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_NOTIFY,
  120. NULL,
  121. &hKey,
  122. NULL) != ERROR_SUCCESS)
  123. {
  124. wprintf(L"WebChange Error: Failed to open the registry key\n");
  125. hKey = NULL; // Just to make sure it does not get closed in cleanup
  126. goto Done;
  127. }
  128. // Set the specified value:
  129. if (FAILED(RegSetValueExW(hKey,
  130. wszHash,
  131. 0,
  132. REG_SZ,
  133. (LPBYTE)wszFilename,
  134. (wcslen(wszFilename) + 1) * sizeof(WCHAR))))
  135. {
  136. wprintf(L"WebChange Error: Failed to create registry value\n");
  137. goto Done;
  138. }
  139. // Watch our key for changes
  140. if (RegNotifyChangeKeyValue(hKey,
  141. FALSE,
  142. REG_NOTIFY_CHANGE_LAST_SET,
  143. hEvent,
  144. TRUE) != ERROR_SUCCESS)
  145. {
  146. wprintf(L"WebChange Error: Failed to set registry notification\n");
  147. goto Done;
  148. }
  149. // Initialize StartupInfo structure
  150. memset(&StartupInfo, 0, sizeof(STARTUPINFOW));
  151. StartupInfo.cb = sizeof(STARTUPINFOW);
  152. // Call IE and open to the specified page
  153. if (CreateProcessW(wszName,
  154. wszCommand, // command line string
  155. NULL, // SD
  156. NULL, // SD
  157. FALSE, // handle inheritance option
  158. CREATE_NEW_PROCESS_GROUP, // creation flags
  159. NULL, // new environment block
  160. NULL, // current directory name
  161. &StartupInfo, // startup information
  162. &ProcessInfo)) // process information
  163. {
  164. // Close the Thread handle. I don't use it.
  165. CloseHandle(ProcessInfo.hThread);
  166. hWait[0] = hEvent;
  167. hWait[1] = ProcessInfo.hProcess;
  168. // Wait until IE exits or our Key is changed.
  169. WaitAgain:
  170. dwRet = WaitForMultipleObjects(2, hWait, FALSE, INFINITE);
  171. if (dwRet == WAIT_OBJECT_0)
  172. {
  173. // Then the Registry was modified.
  174. // First reset the event.
  175. if (!ResetEvent(hEvent))
  176. {
  177. goto Done;
  178. }
  179. // Then restart the Notify on the registry key
  180. if (RegNotifyChangeKeyValue(hKey,
  181. FALSE,
  182. REG_NOTIFY_CHANGE_LAST_SET,
  183. hEvent,
  184. TRUE) != ERROR_SUCCESS)
  185. {
  186. goto Done;
  187. }
  188. // Then check to see if our value is still there
  189. // We do this last to avoid race issues
  190. if (RegQueryValueExW(hKey,
  191. wszHash,
  192. 0,
  193. NULL,
  194. NULL,
  195. NULL) == ERROR_SUCCESS)
  196. {
  197. // The key is still there, so wait again
  198. goto WaitAgain;
  199. }
  200. else
  201. {
  202. // The key was deleted, so exit
  203. goto Done;
  204. }
  205. }
  206. if (dwRet == (WAIT_OBJECT_0 + 1))
  207. {
  208. // Then IE was closed.
  209. // Attempt to delete our value whether it's there or not
  210. RegDeleteValueW(hKey, wszHash);
  211. }
  212. }
  213. else
  214. {
  215. ProcessInfo.hProcess = NULL; // Just to be sure.
  216. wprintf(L"WebChange Error %08X while attempting to execute:\n%s\n",
  217. GetLastError(), wszCommand);
  218. }
  219. Done:
  220. if (hEvent)
  221. {
  222. CloseHandle(hEvent);
  223. }
  224. if (ProcessInfo.hProcess)
  225. {
  226. CloseHandle(ProcessInfo.hProcess);
  227. }
  228. if (hKey)
  229. {
  230. RegCloseKey(hKey);
  231. }
  232. if (wszCommand)
  233. {
  234. free(wszCommand);
  235. }
  236. }
  237. void OpenAlternateEditor(WCHAR *wszFilename)
  238. {
  239. WCHAR *wszCommand;
  240. wszCommand = (WCHAR*) malloc((18 + wcslen(wszFilename) + 1) * sizeof(WCHAR));
  241. if (wszCommand == NULL)
  242. {
  243. wprintf(L"WebChange Error: Out of Memory\n");
  244. }
  245. // Build the command string
  246. wcscpy(wszCommand, L"%SDALTFORMEDITOR% ");
  247. wcscat(wszCommand, wszFilename);
  248. // Execute the alternate editor and wait for it to return.
  249. if (_wsystem(wszCommand) == -1)
  250. {
  251. wprintf(L"WebChange Error: Could not execute: %s\n", wszCommand);
  252. }
  253. free(wszCommand);
  254. }
  255. BOOL CalculateHash(WCHAR *wszFilename, WCHAR *wszHash)
  256. {
  257. CRYPT_HASH_BLOB SHA1;
  258. HANDLE hFile;
  259. // Initialize the Hash structure
  260. SHA1.pbData = (BYTE*)malloc(20);
  261. if (SHA1.pbData)
  262. {
  263. SHA1.cbData = 20;
  264. }
  265. else
  266. {
  267. wprintf(L"WebChange Error: Out of Memory\n");
  268. return FALSE;
  269. }
  270. // Open the file
  271. hFile = CreateFileW(wszFilename,
  272. GENERIC_READ,
  273. FILE_SHARE_READ,
  274. NULL,
  275. OPEN_EXISTING,
  276. FILE_ATTRIBUTE_NORMAL,
  277. NULL);
  278. if (!hFile)
  279. {
  280. wprintf(L"WebChange Error: Unable to open %s\n", wszFilename);
  281. return FALSE;
  282. }
  283. // Create the hash of the file using the catalog function
  284. if (!CryptCATAdminCalcHashFromFileHandle(hFile,
  285. &SHA1.cbData,
  286. SHA1.pbData,
  287. NULL))
  288. {
  289. CloseHandle(hFile);
  290. wprintf(L"WebChange Error: Failed to create file hash\n");
  291. return FALSE;
  292. }
  293. CloseHandle(hFile);
  294. for (DWORD j = 0; j<SHA1.cbData; j++)
  295. { // Print the hash to a string:
  296. swprintf(&(wszHash[j*2]), L"%02X", SHA1.pbData[j]);
  297. }
  298. // Finished calculating the hash.
  299. return TRUE;
  300. }
  301. BOOL IsNewOrPendingChangelist(WCHAR *wszFilename)
  302. {
  303. BOOL fRetVal = FALSE;
  304. FILE *pFile = NULL;
  305. WCHAR wszBuffer[500];
  306. DWORD dwState = 0; // State of parsing engine
  307. // Open the file read-only
  308. pFile = _wfopen(wszFilename, L"rt");
  309. if (pFile == NULL)
  310. goto Done;
  311. while (fwscanf(pFile, L"%499[^\n]%*[\n]", &wszBuffer) == 1)
  312. {
  313. // Expect a specific comment
  314. switch (dwState)
  315. {
  316. case 0: // Comment block at top of file
  317. if (wcscmp(wszBuffer, L"# A Source Depot Change Specification.") == 0)
  318. {
  319. // move on:
  320. dwState++;
  321. break;
  322. }
  323. else
  324. {
  325. // Invalid file.
  326. goto Done;
  327. }
  328. case 1:
  329. if (wszBuffer[0] == L'#')
  330. {
  331. // Ignore the comment line
  332. break;
  333. }
  334. else
  335. {
  336. // Stop expecting a comment block
  337. dwState++;
  338. // Fall through to status=2 below
  339. }
  340. case 2: // Change field
  341. if (wcsncmp(wszBuffer, L"Change:\t", 8) == 0)
  342. {
  343. // move on:
  344. dwState++;
  345. break;
  346. }
  347. else
  348. {
  349. // Invalid file.
  350. goto Done;
  351. }
  352. case 3: // Status field
  353. if (wcsncmp(wszBuffer, L"Status:\t", 8) == 0)
  354. {
  355. // Check the Status string:
  356. if ((_wcsicmp(&wszBuffer[8], L"pending") != 0) &&
  357. (_wcsicmp(&wszBuffer[8], L"new") != 0))
  358. {
  359. // Invalid file.
  360. goto Done;
  361. }
  362. // move on:
  363. dwState++;
  364. break;
  365. }
  366. else
  367. {
  368. // maybe we haven't gotten to the status field
  369. break;
  370. }
  371. } // end of case statement
  372. } // end of while loop
  373. if (dwState == 4)
  374. {
  375. // We got past the Status section, so we completed parsing successfully.
  376. fRetVal = TRUE;
  377. }
  378. Done:
  379. if (pFile)
  380. fclose(pFile);
  381. return fRetVal;
  382. }