VOID WINAPI HashSaveStart( HWND hWndOwner, HSIMPLELIST hListRaw ) { // Explorer will be blocking as long as this function is running, so we // want to return as quickly as possible and leave the work up to the // thread that we are going to spawn PHASHSAVECONTEXT phsctx = SLSetContextSize(hListRaw, sizeof(HASHSAVECONTEXT)); if (phsctx) { HANDLE hThread; phsctx->hWnd = hWndOwner; phsctx->hListRaw = hListRaw; InterlockedIncrement(&g_cRefThisDll); SLAddRef(hListRaw); if (hThread = CreateThreadCRT(HashSaveThread, phsctx)) { CloseHandle(hThread); return; } // If the thread creation was successful, the thread will be // responsible for decrementing the ref count SLRelease(hListRaw); InterlockedDecrement(&g_cRefThisDll); } }
VOID WINAPI HashVerifyParseData( PHASHVERIFYCONTEXT phvctx ) { PTSTR pszData = phvctx->pszFileData; // Points to the next line to process UINT cchChecksum; // Expected length of the checksum in TCHARs BOOL bReverseFormat = FALSE; // TRUE if using SFV's format of putting the checksum last BOOL bLinesRemaining = TRUE; // TRUE if we have not reached the end of the data // Try to determine the file type from the extension { PTSTR pszExt = StrRChr(phvctx->pszPath, NULL, TEXT('.')); if (pszExt) { if (StrCmpI(pszExt, TEXT(".sfv")) == 0) { phvctx->whctx.flags = WHEX_CHECKCRC32; cchChecksum = 8; bReverseFormat = TRUE; } else if (StrCmpI(pszExt, TEXT(".md4")) == 0) { phvctx->whctx.flags = WHEX_CHECKMD4; cchChecksum = 32; } else if (StrCmpI(pszExt, TEXT(".md5")) == 0) { phvctx->whctx.flags = WHEX_CHECKMD5; cchChecksum = 32; } else if (StrCmpI(pszExt - 1, TEXT(".sha1")) == 0) { phvctx->whctx.flags = WHEX_CHECKSHA1; cchChecksum = 40; } } } while (bLinesRemaining) { PTSTR pszStartOfLine; // First non-whitespace character of the line PTSTR pszEndOfLine; // Last non-whitespace character of the line PTSTR pszChecksum = NULL, pszFileName = NULL; INT16 cchPath; // This INCLUDES the NULL terminator! // Step 1: Isolate the current line as a NULL-terminated string { pszStartOfLine = pszData; // Find the end of the line while (*pszData && *pszData != TEXT('\n')) ++pszData; // Terminate it if necessary, otherwise flag the end of the data if (*pszData) *pszData = 0; else bLinesRemaining = FALSE; pszEndOfLine = pszData; // Strip spaces from the end of the line... while (--pszEndOfLine >= pszStartOfLine && *pszEndOfLine == TEXT(' ')) *pszEndOfLine = 0; // ...and from the start of the line while (*pszStartOfLine == TEXT(' ')) ++pszStartOfLine; // Skip past this line's terminator; point at the remaining data ++pszData; } // Step 2a: Parse the line as SFV if (bReverseFormat) { pszEndOfLine -= 7; if (pszEndOfLine > pszStartOfLine && ValidateHexSequence(pszEndOfLine, 8)) { pszChecksum = pszEndOfLine; // Trim spaces between the checksum and the file name while (--pszEndOfLine >= pszStartOfLine && *pszEndOfLine == TEXT(' ')) *pszEndOfLine = 0; // Lines that begin with ';' are comments in SFV if (*pszStartOfLine && *pszStartOfLine != TEXT(';')) pszFileName = pszStartOfLine; } } // Step 2b: All other file formats else { // If we do not know the type yet, make a stab at detecting it if (phvctx->whctx.flags == 0) { if (ValidateHexSequence(pszStartOfLine, 8)) { cchChecksum = 8; phvctx->whctx.flags = WHEX_ALL32; // WHEX_CHECKCRC32 } else if (ValidateHexSequence(pszStartOfLine, 32)) { cchChecksum = 32; phvctx->whctx.flags = WHEX_ALL128; // WHEX_CHECKMD4 | WHEX_CHECKMD5 // Disambiguate from the filename, if possible if (StrStrI(phvctx->pszPath, TEXT("MD5"))) phvctx->whctx.flags = WHEX_CHECKMD5; else if (StrStrI(phvctx->pszPath, TEXT("MD4"))) phvctx->whctx.flags = WHEX_CHECKMD4; } else if (ValidateHexSequence(pszStartOfLine, 40)) { cchChecksum = 40; phvctx->whctx.flags = WHEX_ALL160; // WHEX_CHECKSHA1 } } // Parse the line if ( phvctx->whctx.flags && pszEndOfLine > pszStartOfLine + cchChecksum && ValidateHexSequence(pszStartOfLine, cchChecksum) ) { pszChecksum = pszStartOfLine; pszStartOfLine += cchChecksum + 1; // Skip over spaces between the checksum and filename while (*pszStartOfLine == TEXT(' ')) ++pszStartOfLine; if (*pszStartOfLine) pszFileName = pszStartOfLine; } } // Step 3: Do something useful with the results if (pszFileName && (cchPath = (INT16)(pszEndOfLine + 2 - pszFileName)) > 1) { // Since pszEndOfLine points to the character BEFORE the terminator, // cchLine == 1 + pszEnd - pszStart, and then +1 for the NULL // terminator means that we need to add 2 TCHARs to the length // By treating cchPath as INT16 and checking the sign, we ensure // that the path does not exceed 32K. // Create the new data block PHASHVERIFYITEM pItem = SLAddItem(phvctx->hList, NULL, sizeof(HASHVERIFYITEM)); // Abort if we are out of memory if (!pItem) break; pItem->filesize.ui64 = -1; pItem->filesize.sz[0] = 0; pItem->pszDisplayName = pszFileName; pItem->pszExpected = pszChecksum; pItem->cchDisplayName = cchPath; pItem->uStatusID = HV_STATUS_NULL; pItem->szActual[0] = 0; ++phvctx->cTotal; } // If the current line was found to be valid } // Loop until there are no lines left // Build the index if ( phvctx->cTotal && (phvctx->index = SLSetContextSize(phvctx->hList, phvctx->cTotal * sizeof(PHVITEM))) ) { SLBuildIndex(phvctx->hList, phvctx->index); } else { phvctx->cTotal = 0; } }