#include #include // for _snprintf #include "md5.h" #define HASHLENGTH 32 #define HASHSTRINGLENGTH HASHLENGTH+1 #ifdef _UNICODE #error this program should not be compiled with UNICODE #endif // _UNICODE #ifdef _MBCS #error this program should not be compiled with MBCS #endif // _MBCS // Note: this is not really a WCHAR/Unicode/UTF implementation! // ???? UTF-8? // BUGBUG: Files are never saved as UTF-8 encoding and format const char szHeader[] = { "\r\n\r\n\r\n\r\n" }; const char szEndheader2[] = { "\r\n\r\n" }; // app manifest only const char szShellstateprefix[] = { "\r\n\t\r\n\r\n\r\n" }; const char szFileprefix[] = { "\t\r\n" }; // subscription manifest only const char szDepend[] = { "\r\n\t\r\n\t\t\r\n\r\n\t\t\r\n\t\t\r\n\r\n\t\r\n\r\n" }; const char szEndfile[] = { "" }; const char szDefaultdescription[] = { "This is a description of the app" }; //assume not unicode file, use ReadFile() then cast it as char[] ///////////////////////////////////////////////////////////////////////////////////////////////// void getHash(const char* szFilename, char* szHash) { HANDLE hFile = INVALID_HANDLE_VALUE; DWORD dwLength; unsigned char buffer[16384], signature[HASHLENGTH/2]; struct MD5Context md5c; int i; char* p; // minimal error checking here... if (szHash == NULL) goto exit; szHash[0] = '\0'; MD5Init(&md5c); hFile = CreateFile(szFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { // hr = GetLastWin32Error(); printf("Open file error during hashing\n"); goto exit; } ZeroMemory(buffer, sizeof(buffer)); while ( ReadFile (hFile, buffer, sizeof(buffer), &dwLength, NULL) && dwLength ) { MD5Update(&md5c, buffer, (unsigned) dwLength); } if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; // note: the next few lines should follow the previous ReadFile() if (dwLength != 0) { printf("Read error during hashing\n"); goto exit; } MD5Final(signature, &md5c); // convert hash from byte array to hex p = szHash; for (int i = 0; i < sizeof(signature); i++) { // BUGBUG?: format string 0 does not work w/ X according to MSDN? why is this working? sprintf(p, "%02X", signature[i]); p += 2; } exit: if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; return; } ///////////////////////////////////////////////////////////////////////////////////////////////// HRESULT write(HANDLE hFile, const char* string) { HRESULT hr = S_OK; DWORD dwLength = strlen(string); //???? DWORD dwWritten = 0; if ( !WriteFile(hFile, string, dwLength, &dwWritten, NULL) || dwWritten != dwLength) { printf("Manifest file write error\n"); hr = E_FAIL; } return hr; } ///////////////////////////////////////////////////////////////////////////////////////////////// // szPath is the path and name of the directory w/o the last '\' // szPath, szPathFromAppRoot will be used/modified during runs HRESULT doFiles(HANDLE hFile, char* szPath, char* szPathFromAppRoot) { // find all files, in all sub dir, get hash, output HRESULT hr = S_OK; char szSearchPath[MAX_PATH]; HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATA fdFile; DWORD dwLastError = 0; BOOL fNeedSetCurrentDir = TRUE; // this has trailing "\*" if (_snprintf(szSearchPath, MAX_PATH, "%s\\*", szPath) < 0) { hr = CO_E_PATHTOOLONG; printf("Error: Search path too long\n"); goto exit; } hFind = FindFirstFile(szSearchPath, &fdFile); if (hFind == INVALID_HANDLE_VALUE) { hr = E_FAIL; //GetLastWin32Error(); printf("Find file error\n"); goto exit; } while (dwLastError != ERROR_NO_MORE_FILES) { // ignore "." and ".." if (strcmp(fdFile.cFileName, ".") != 0 && strcmp(fdFile.cFileName, "..") != 0) { if ((fdFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { // recurse into dirs char* p = NULL; if (!PathAppend(szPath, fdFile.cFileName)) { hr = E_FAIL; printf("Path append error\n"); goto exit; } if (szPathFromAppRoot[0] == '\0') strncpy(szPathFromAppRoot, fdFile.cFileName, MAX_PATH-1); else if (!PathAppend(szPathFromAppRoot, fdFile.cFileName)) { hr = E_FAIL; printf("Path append error\n"); goto exit; } if (FAILED(hr=doFiles(hFile, szPath, szPathFromAppRoot))) goto exit; p = PathFindFileName(szPath); if (p <= szPath) { // this should not happen! hr = E_FAIL; printf("Path error\n"); goto exit; } *(p-1) = '\0'; p = PathFindFileName(szPathFromAppRoot); if (p <= szPathFromAppRoot) { szPathFromAppRoot[0] = '\0'; } else *(p-1) = '\0'; fNeedSetCurrentDir = TRUE; } else { char szHash[HASHSTRINGLENGTH]; if (fNeedSetCurrentDir) { if (!SetCurrentDirectory(szPath)) { hr = E_FAIL; //GetLastWin32Error(); printf("Set current directory error\n"); goto exit; } fNeedSetCurrentDir = FALSE; } // get hash from that file in the current dir getHash(fdFile.cFileName, szHash); // write into the manifest file if (FAILED(write(hFile, szFileprefix))) goto exit; // path to the file from app root if (szPathFromAppRoot[0] != '\0') { if (FAILED(write(hFile, szPathFromAppRoot))) goto exit; if (FAILED(write(hFile, "\\"))) goto exit; } if (FAILED(write(hFile, fdFile.cFileName))) goto exit; if (FAILED(write(hFile, szHashprefix))) goto exit; if (FAILED(write(hFile, szHash))) goto exit; if (FAILED(write(hFile, szHashpostfix))) goto exit; } } if (!FindNextFile(hFind, &fdFile)) { dwLastError = GetLastError(); continue; } } exit: if (hFind != INVALID_HANDLE_VALUE) { if (!FindClose(hFind)) { hr = E_FAIL; //GetLastWin32Error(); printf("Find handle close error\n"); } } return hr; } ///////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////// const UINT s_ucMaxNameLen = 45; const UINT s_ucMaxVerLen = 25; const UINT s_ucMaxProArchLen = 10; const UINT s_ucMaxPktLen = 20; const UINT s_ucMaxLangLen = 10; int __cdecl main( int argc, char *argv[ ], char *envp[ ] ) { HANDLE hFile = INVALID_HANDLE_VALUE; DWORD dwRead, dwWritten, dwAttrib; char buffer[1024]; char* pszExt = NULL; char szManifestName[MAX_PATH]; char szAppDir[MAX_PATH]; char szPathFromAppDir[MAX_PATH]; // relative from app dir - temp use only char szName[s_ucMaxNameLen]; char szVer[s_ucMaxVerLen]; char szProArch[s_ucMaxProArchLen]; char szPkt[s_ucMaxPktLen]; char szLang[s_ucMaxLangLen]; // ???? need to check path are current and sub dir of current ??? or not? - see known issues szPathFromAppDir[0] = '\0'; if (argc < 3) { printf("Fusion manifest generator v1.0.0.0\n\n usage-\n\t mangen manifestName appDir [description]\n\n\n"); goto exit; } if (PathIsRelative(argv[2])) { // findfirstfile findnextfile will not work if path is relative...? printf("Error: App dir (%s) should not be relative\n", argv[2]); goto exit; } if (!PathCanonicalize(szAppDir, argv[2])) { printf("App dir (%s) canonicalize error\n", argv[2]); goto exit; } dwAttrib = GetFileAttributes(szAppDir); if (dwAttrib == (DWORD)-1 || (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0) { printf("Error: Invalid app dir (%s)\n", szAppDir); goto exit; } pszExt = PathFindExtension(argv[1]); if (strcmp(pszExt, ".manifest") != 0) { if (_snprintf(szManifestName, MAX_PATH, "%s.manifest", argv[1]) < 0) { printf("Invalid manifest name/extension, error adding extension\n"); goto exit; } } else strcpy(szManifestName, argv[1]); if (PathFileExists(szManifestName)) { int ch = 0; printf("Application manifest file exists! Overwrite(y/N)?"); ch = getchar(); if (ch != 'Y' && ch != 'y') goto exit; // clear newline char ch = getchar(); printf("\n"); } // always overwrite... hFile = CreateFile(szManifestName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // Failed to open the disk file if(hFile == INVALID_HANDLE_VALUE) { printf("Application manifest file open error\n"); goto exit; } if (FAILED(write(hFile, szHeader))) goto exit; if (FAILED(write(hFile, szAsmidtype))) goto exit; if (FAILED(write(hFile, szAsmidtypeapp))) goto exit; // BUGBUG: no error checking and buffer overflow check // BUGBUG: use _getws if (FAILED(write(hFile, szAsmidname))) goto exit; printf("enter asm id name (eg. microsoft.webApps.fusionTest): "); gets(szName); if (FAILED(write(hFile, szName))) goto exit; if (FAILED(write(hFile, szAsmidver))) goto exit; printf("enter asm id version (eg. 1.0.0.0): "); gets(szVer); if (FAILED(write(hFile, szVer))) goto exit; if (FAILED(write(hFile, szAsmidprocessor))) goto exit; //optional?? check valid? x86 printf("enter asm id processor architecture (eg. x86): "); gets(szProArch); if (FAILED(write(hFile, szProArch))) goto exit; if (FAILED(write(hFile, szAsmidpkt))) goto exit; //?? 16 char or 8 byte or 64 bits? printf("enter asm id public key token (eg. 6595b64144ccf1df): "); gets(szPkt); if (FAILED(write(hFile, szPkt))) goto exit; if (FAILED(write(hFile, szAsmidlang))) goto exit; //?? check valid printf("enter asm id language (eg. en): "); gets(szLang); if (FAILED(write(hFile, szLang))) goto exit; if (FAILED(write(hFile, szEndheader1))) goto exit; if (argc >= 5) { if (FAILED(write(hFile, argv[3]))) goto exit; } else { if (FAILED(write(hFile, szDefaultdescription))) goto exit; } if (FAILED(write(hFile, szEndheader2))) goto exit; // BUGBUG: no error checking and buffer overflow check // BUGBUG: use _getws if (FAILED(write(hFile, szShellstateprefix))) goto exit; printf("enter app friendly name (eg. Fusion ClickOnce Test App): "); gets(buffer); if (FAILED(write(hFile, buffer))) goto exit; if (FAILED(write(hFile, szShellentry))) goto exit; printf("enter app entry point (eg. foo.exe): "); gets(buffer); if (FAILED(write(hFile, buffer))) goto exit; if (FAILED(write(hFile, szShellimagetype))) goto exit; printf("enter app entry point's image type {win32Executable, .NetAssembly}: "); gets(buffer); if (FAILED(write(hFile, buffer))) goto exit; printf("enter app shortcut hotkey value (optional) (eg. 0): "); gets(buffer); if (strlen(buffer) != 0) { if (FAILED(write(hFile, szShellhotkey))) goto exit; if (FAILED(write(hFile, buffer))) goto exit; } printf("enter app shortcut icon file (optional) (eg. bar.dll): "); gets(buffer); if (strlen(buffer) != 0) { if (FAILED(write(hFile, szShelliconfile))) goto exit; if (FAILED(write(hFile, buffer))) goto exit; } printf("enter app shortcut icon index in the icon file (optional) (eg. 0): "); gets(buffer); if (strlen(buffer) != 0) { if (FAILED(write(hFile, szShelliconindex))) goto exit; if (FAILED(write(hFile, buffer))) goto exit; } printf("enter app shortcut show command (optional) {normal, maximized, minimized}: "); gets(buffer); if (strlen(buffer) != 0) { if (FAILED(write(hFile, szShellshowcommand))) goto exit; if (FAILED(write(hFile, buffer))) goto exit; } if (FAILED(write(hFile, szShellstatepostfix))) goto exit; printf("\nprocessing app dir...\n"); { char szCurDir[MAX_PATH]; DWORD ret = 0; // note: big hack. doFiles will change current directory // so save it now - 'cos might need it for creating // a subscription manifest // ignore error... // BUGBUG: > MAX_PATH? ret = GetCurrentDirectory(MAX_PATH, szCurDir); if (FAILED(doFiles(hFile, szAppDir, szPathFromAppDir))) goto exit; // set back current dir if (ret != 0) { SetCurrentDirectory(szCurDir); } } if (FAILED(write(hFile, szEndfile))) goto exit; printf("* Application manifest generated successfully *\n"); { int ch = 0; printf("Generate a subscription manifest (y/N)?"); ch = getchar(); if (ch != 'Y' && ch != 'y') goto exit; // clear newline char ch = getchar(); printf("\n"); } pszExt = PathFindExtension(szManifestName); *pszExt = '\0'; { int len = lstrlen(szManifestName); if (lstrcpyn(szManifestName+len, "subscription.manifest", MAX_PATH-len) == NULL) { printf("Invalid manifest name/extension, error adding subscription extension\n"); goto exit; } } if (PathFileExists(szManifestName)) { int ch = 0; printf("Subscription manifest file exists! Overwrite(y/N)?"); ch = getchar(); if (ch != 'Y' && ch != 'y') goto exit; // clear newline char ch = getchar(); printf("\n"); } CloseHandle(hFile); // always overwrite... hFile = CreateFile(szManifestName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // Failed to open the disk file if(hFile == INVALID_HANDLE_VALUE) { printf("Subscription manifest file open error\n"); goto exit; } if (FAILED(write(hFile, szHeader))) goto exit; if (FAILED(write(hFile, szAsmidtype))) goto exit; if (FAILED(write(hFile, szAsmidtypesub))) goto exit; if (FAILED(write(hFile, szAsmidname))) goto exit; if (FAILED(write(hFile, szName))) goto exit; if (FAILED(write(hFile, ".subscription"))) //.subscription goto exit; if (FAILED(write(hFile, szAsmidver))) goto exit; if (FAILED(write(hFile, szVer))) // have another (auto-generated?) version for subscription? goto exit; if (FAILED(write(hFile, szAsmidprocessor))) goto exit; if (FAILED(write(hFile, szProArch))) goto exit; if (FAILED(write(hFile, szAsmidpkt))) goto exit; if (FAILED(write(hFile, szPkt))) goto exit; if (FAILED(write(hFile, szAsmidlang))) goto exit; if (FAILED(write(hFile, szLang))) goto exit; if (FAILED(write(hFile, szEndheader1))) goto exit; if (argc >= 5) { if (FAILED(write(hFile, argv[3]))) goto exit; } else { if (FAILED(write(hFile, szDefaultdescription))) goto exit; } if (FAILED(write(hFile, szEndheader2))) goto exit; if (FAILED(write(hFile, szDepend))) goto exit; if (FAILED(write(hFile, szAsmidtype2))) goto exit; if (FAILED(write(hFile, szAsmidtypeapp))) goto exit; if (FAILED(write(hFile, szAsmidname2))) goto exit; if (FAILED(write(hFile, szName))) goto exit; if (FAILED(write(hFile, szAsmidver2))) goto exit; if (FAILED(write(hFile, szVer))) goto exit; if (FAILED(write(hFile, szAsmidprocessor2))) goto exit; if (FAILED(write(hFile, szProArch))) goto exit; if (FAILED(write(hFile, szAsmidpkt2))) goto exit; if (FAILED(write(hFile, szPkt))) goto exit; if (FAILED(write(hFile, szAsmidlang2))) goto exit; if (FAILED(write(hFile, szLang))) goto exit; if (FAILED(write(hFile, szDependcodebase))) goto exit; printf("enter codebase for app manifest (eg. http://www.microsoft.com/fooApp/microsoft.webApps.fusionTest.manifest): "); gets(buffer); if (FAILED(write(hFile, buffer))) goto exit; if (FAILED(write(hFile, szDependpolling))) goto exit; printf("enter update polling interval (in milliseconds) (eg. 21600000): "); gets(buffer); if (FAILED(write(hFile, buffer))) goto exit; if (FAILED(write(hFile, szEndDepend))) goto exit; if (FAILED(write(hFile, szEndfile))) goto exit; printf("* Subscription manifest generated successfully *\n"); exit: if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; return 0; } // known issues: // 1. the target manifest file and the import file if present in the app dir // this program will try to open them up for hashing but will fail because they are already opened // -> all files in/under the app dir will be listed and hashed as is... // 2. Note. Be careful with %USERPROFILE% etc when they have space in them. Use quotes. // 3. Note. Error messages are not the most descriptive. But - you can probably figure out from the // resulted manifest file. That should be relative easy. // note: should rename application manifest to app asm id name. application manifest's filename // has to be the same as asm id name.