/*++ Copyright (c) 1996 Microsoft Corporation Module Name: links.c Abstract: This source file implements the Win95 side of LNK and PIF processing Author: Calin Negreanu (calinn) 09-Feb-1998 Revision History: calinn 23-Sep-1998 Redesigned several pieces --*/ #include "pch.h" #include "migdbp.h" #include "migappp.h" POOLHANDLE g_LinksPool = NULL; INT g_LinkStubSequencer = 0; typedef struct _LINK_STRUCT { PCTSTR ReportEntry; PCTSTR Category; PCTSTR Context; PCTSTR Object; PCTSTR LinkName; PCTSTR LinkNameNoPath; PMIGDB_CONTEXT MigDbContext; } LINK_STRUCT, *PLINK_STRUCT; BOOL InitLinkAnnounce ( VOID ) { // // Create PoolMem for keeping all structures during this phase // g_LinksPool = PoolMemInitNamedPool ("Links Pool"); return TRUE; } BOOL DoneLinkAnnounce ( VOID ) { // Write LinkStub max sequencer data MemDbSetValue (MEMDB_CATEGORY_LINKSTUB_MAXSEQUENCE, g_LinkStubSequencer); // // Free Links Pool. // if (g_LinksPool != NULL) { PoolMemDestroyPool (g_LinksPool); g_LinksPool = NULL; } return TRUE; } BOOL SaveLinkFiles ( IN PFILE_HELPER_PARAMS Params ) { PCTSTR Ext; if (Params->Handled) { return TRUE; } Ext = GetFileExtensionFromPath (Params->FullFileSpec); // Save LNK and PIF filenames to memdb to enumerate later if (Ext && (StringIMatch (Ext, TEXT("LNK")) || StringIMatch (Ext, TEXT("PIF")))) { MemDbSetValueEx ( MEMDB_CATEGORY_SHORTCUTS, Params->FullFileSpec, NULL, NULL, 0, NULL ); } return TRUE; } VOID RemoveLinkFromSystem ( IN LPCTSTR LinkPath ) { // // Remove any move or copy operation specified for the link, then // mark it for deletion. // RemoveOperationsFromPath (LinkPath, ALL_DEST_CHANGE_OPERATIONS); MarkFileForDelete (LinkPath); } // // Function to send instruction to MemDb to edit a shell link or pif file. // It checks to see whether the link involved has been touched yet by any // MemDb operation. It modifies the target path, if in a relocating directory, // to one of the relocated copies. // VOID pAddLinkEditToMemDb ( IN PCTSTR LinkPath, IN PCTSTR NewTarget, IN PCTSTR NewArgs, IN PCTSTR NewWorkDir, IN PCTSTR NewIconPath, IN INT NewIconNr, IN PLNK_EXTRA_DATA ExtraData, OPTIONAL IN BOOL ForceToShowNormal ) { UINT sequencer; TCHAR tmpStr [20]; sequencer = AddOperationToPath (LinkPath, OPERATION_LINK_EDIT); if (sequencer == INVALID_OFFSET) { DEBUGMSG ((DBG_ERROR, "Cannot set OPERATION_LINK_EDIT on %s", LinkPath)); return; } if (NewTarget) { AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, NewTarget, MEMDB_CATEGORY_LINKEDIT_TARGET); } if (NewArgs) { AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, NewArgs, MEMDB_CATEGORY_LINKEDIT_ARGS); } if (NewWorkDir) { AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, NewWorkDir, MEMDB_CATEGORY_LINKEDIT_WORKDIR); } if (NewIconPath) { AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, NewIconPath, MEMDB_CATEGORY_LINKEDIT_ICONPATH); } if (NewIconPath) { _itoa (NewIconNr, tmpStr, 16); AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, tmpStr, MEMDB_CATEGORY_LINKEDIT_ICONNUMBER); } if (ForceToShowNormal) { AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, TEXT("1"), MEMDB_CATEGORY_LINKEDIT_SHOWNORMAL); } if (ExtraData) { _itoa (ExtraData->FullScreen, tmpStr, 10); AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, tmpStr, MEMDB_CATEGORY_LINKEDIT_FULLSCREEN); _itoa (ExtraData->xSize, tmpStr, 10); AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, tmpStr, MEMDB_CATEGORY_LINKEDIT_XSIZE); _itoa (ExtraData->ySize, tmpStr, 10); AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, tmpStr, MEMDB_CATEGORY_LINKEDIT_YSIZE); _itoa (ExtraData->QuickEdit, tmpStr, 10); AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, tmpStr, MEMDB_CATEGORY_LINKEDIT_QUICKEDIT); AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, ExtraData->FontName, MEMDB_CATEGORY_LINKEDIT_FONTNAME); _itoa (ExtraData->xFontSize, tmpStr, 10); AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, tmpStr, MEMDB_CATEGORY_LINKEDIT_XFONTSIZE); _itoa (ExtraData->yFontSize, tmpStr, 10); AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, tmpStr, MEMDB_CATEGORY_LINKEDIT_YFONTSIZE); _itoa (ExtraData->FontWeight, tmpStr, 10); AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, tmpStr, MEMDB_CATEGORY_LINKEDIT_FONTWEIGHT); _itoa (ExtraData->FontFamily, tmpStr, 10); AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, tmpStr, MEMDB_CATEGORY_LINKEDIT_FONTFAMILY); _itoa (ExtraData->CurrentCodePage, tmpStr, 10); AddPropertyToPathEx (sequencer, OPERATION_LINK_EDIT, tmpStr, MEMDB_CATEGORY_LINKEDIT_CODEPAGE); } MYASSERT (IsFileMarkedForOperation (LinkPath, OPERATION_LINK_EDIT)); } // // Function to send instruction to MemDb to save some data about a link that's going to be edited. // We do that to be able to restore this link later using lnkstub.exe // UINT pAddLinkStubToMemDb ( IN PCTSTR LinkPath, IN PCTSTR OldTarget, IN PCTSTR OldArgs, IN PCTSTR OldWorkDir, IN PCTSTR OldIconPath, IN INT OldIconNr, IN DWORD OldShowMode, IN DWORD Announcement, IN DWORD Availability ) { UINT sequencer; TCHAR tmpStr [20]; MEMDB_ENUM e, e1; TCHAR key [MEMDB_MAX]; MYASSERT (OldTarget || OldWorkDir || OldIconPath || OldIconNr); sequencer = AddOperationToPath (LinkPath, OPERATION_LINK_STUB); if (sequencer == INVALID_OFFSET) { DEBUGMSG ((DBG_ERROR, "Cannot set OPERATION_LINK_STUB on %s", LinkPath)); return 0; } g_LinkStubSequencer++; if (OldTarget) { AddPropertyToPathEx (sequencer, OPERATION_LINK_STUB, OldTarget, MEMDB_CATEGORY_LINKSTUB_TARGET); } if (OldArgs) { AddPropertyToPathEx (sequencer, OPERATION_LINK_STUB, OldArgs, MEMDB_CATEGORY_LINKSTUB_ARGS); } if (OldWorkDir) { AddPropertyToPathEx (sequencer, OPERATION_LINK_STUB, OldWorkDir, MEMDB_CATEGORY_LINKSTUB_WORKDIR); } if (OldIconPath) { AddPropertyToPathEx (sequencer, OPERATION_LINK_STUB, OldIconPath, MEMDB_CATEGORY_LINKSTUB_ICONPATH); } if (OldIconPath) { _itoa (OldIconNr, tmpStr, 16); AddPropertyToPathEx (sequencer, OPERATION_LINK_STUB, tmpStr, MEMDB_CATEGORY_LINKSTUB_ICONNUMBER); } _itoa (OldShowMode, tmpStr, 16); AddPropertyToPathEx (sequencer, OPERATION_LINK_STUB, tmpStr, MEMDB_CATEGORY_LINKSTUB_SHOWMODE); _itoa (g_LinkStubSequencer, tmpStr, 16); AddPropertyToPathEx (sequencer, OPERATION_LINK_STUB, tmpStr, MEMDB_CATEGORY_LINKSTUB_SEQUENCER); _itoa (Announcement, tmpStr, 16); AddPropertyToPathEx (sequencer, OPERATION_LINK_STUB, tmpStr, MEMDB_CATEGORY_LINKSTUB_ANNOUNCEMENT); _itoa (Availability, tmpStr, 16); AddPropertyToPathEx (sequencer, OPERATION_LINK_STUB, tmpStr, MEMDB_CATEGORY_LINKSTUB_REPORTAVAIL); MemDbBuildKey (key, MEMDB_CATEGORY_REQFILES_MAIN, OldTarget, TEXT("*"), NULL); if (MemDbEnumFirstValue (&e, key, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { do { MemDbBuildKey (key, MEMDB_CATEGORY_REQFILES_ADDNL, e.szName, TEXT("*"), NULL); if (MemDbEnumFirstValue (&e1, key, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { AddPropertyToPathEx (sequencer, OPERATION_LINK_STUB, e1.szName, MEMDB_CATEGORY_LINKSTUB_REQFILE); } } while (MemDbEnumNextValue (&e)); } MYASSERT (IsFileMarkedForOperation (LinkPath, OPERATION_LINK_STUB)); return g_LinkStubSequencer; } BOOL pReportEntry ( IN PCTSTR ReportEntry, IN PCTSTR Category, IN PCTSTR Message, IN PCTSTR Context, IN PCTSTR Object ) { PCTSTR component; component = JoinPaths (ReportEntry, Category); MsgMgr_ContextMsg_Add (Context, component, Message); MsgMgr_LinkObjectWithContext (Context, Object); FreePathString (component); return TRUE; } PTSTR GetLastDirFromPath ( IN PCTSTR FileName ) { PTSTR result = NULL; PTSTR temp = NULL; PTSTR ptr; temp = DuplicatePathString (FileName, 0); __try { ptr = (PTSTR)GetFileNameFromPath (temp); if (ptr == temp) { __leave; } ptr = _tcsdec (temp, ptr); if (!ptr) { __leave; } *ptr = 0; ptr = (PTSTR)GetFileNameFromPath (temp); if (ptr == temp) { __leave; } result = DuplicatePathString (ptr, 0); } __finally { FreePathString (temp); } return result; } PTSTR GetDriveFromPath ( IN PCTSTR FileName ) { PTSTR result; PTSTR ptr; result = DuplicatePathString (FileName, 0); ptr = _tcschr (result, TEXT(':')); if (!ptr) { FreePathString (result); result = NULL; } else { *ptr = 0; } return result; } #define MAX_PRIORITY 0xFFFF BOOL HandleDeferredAnnounce ( IN PCTSTR LinkName, IN PCTSTR ModuleName, IN BOOL DosApp ) { TCHAR key [MEMDB_MAX]; PMIGDB_CONTEXT migDbContext; DWORD actType; PLINK_STRUCT linkStruct; PCTSTR reportEntry = NULL; DWORD priority; PCTSTR newLinkName = NULL; PCTSTR linkName = NULL; PCTSTR extPtr; MEMDB_ENUM eNicePaths; DWORD messageId = 0; PTSTR pattern = NULL; PTSTR category = NULL; PTSTR tempParse = NULL; PTSTR lastDir; PTSTR drive; DWORD oldValue; DWORD oldPrior; PTSTR argArray[3]; PCTSTR p; PTSTR q; BOOL reportEntryIsResource = TRUE; PCTSTR temp1, temp2; MYASSERT(ModuleName); MemDbBuildKey (key, MEMDB_CATEGORY_DEFERREDANNOUNCE, ModuleName, NULL, NULL); if (!MemDbGetValueAndFlags (key, (PDWORD)(&migDbContext), &actType)) { actType = ACT_UNKNOWN; migDbContext = NULL; } // // we need to set the following variables: // - ReportEntry - is going to be either "Software Incompatible with NT", // "Software with minor problems" or // "Software that require reinstallation" // - Category - is one of the following: - Localized section name // - link name (with friendly addition) // - Unlocalized section name // - Message - this is in migdb context // // - Object - this is module name // linkStruct = (PLINK_STRUCT) PoolMemGetMemory (g_LinksPool, sizeof (LINK_STRUCT)); ZeroMemory (linkStruct, sizeof (LINK_STRUCT)); linkStruct->MigDbContext = migDbContext; linkStruct->Object = PoolMemDuplicateString (g_LinksPool, ModuleName); switch (actType) { case ACT_REINSTALL: #if 0 if ((linkStruct->MigDbContext) && (linkStruct->MigDbContext->Message) ) { reportEntry = GetStringResource (MSG_MINOR_PROBLEM_ROOT); } else { reportEntry = GetStringResource (MSG_REINSTALL_ROOT); } #endif temp1 = GetStringResource (MSG_REINSTALL_ROOT); if (!temp1) { break; } temp2 = GetStringResource ( linkStruct->MigDbContext && linkStruct->MigDbContext->Message ? MSG_REINSTALL_DETAIL_SUBGROUP : MSG_REINSTALL_LIST_SUBGROUP ); if (!temp2) { break; } reportEntry = JoinPaths (temp1, temp2); reportEntryIsResource = FALSE; FreeStringResource (temp1); FreeStringResource (temp2); break; case ACT_REINSTALL_BLOCK: temp1 = GetStringResource (MSG_BLOCKING_ITEMS_ROOT); if (!temp1) { break; } temp2 = GetStringResource (MSG_REINSTALL_BLOCK_ROOT); if (!temp2) { break; } reportEntry = JoinPaths (temp1, temp2); reportEntryIsResource = FALSE; FreeStringResource (temp1); FreeStringResource (temp2); break; case ACT_MINORPROBLEMS: reportEntry = GetStringResource (MSG_MINOR_PROBLEM_ROOT); break; case ACT_INCOMPATIBLE: case ACT_INC_NOBADAPPS: case ACT_INC_IHVUTIL: case ACT_INC_PREINSTUTIL: case ACT_INC_SIMILAROSFUNC: if (DosApp && (*g_Boot16 != BOOT16_NO)) { reportEntry = GetStringResource (MSG_DOS_DESIGNED_ROOT); } else { temp1 = GetStringResource (MSG_INCOMPATIBLE_ROOT); switch (actType) { case ACT_INC_SIMILAROSFUNC: temp2 = GetStringResource (MSG_INCOMPATIBLE_UTIL_SIMILAR_FEATURE_SUBGROUP); break; case ACT_INC_PREINSTUTIL: temp2 = GetStringResource (MSG_INCOMPATIBLE_PREINSTALLED_UTIL_SUBGROUP); break; case ACT_INC_IHVUTIL: temp2 = GetStringResource (MSG_INCOMPATIBLE_HW_UTIL_SUBGROUP); break; default: temp2 = GetStringResource ( linkStruct->MigDbContext && linkStruct->MigDbContext->Message ? MSG_INCOMPATIBLE_DETAIL_SUBGROUP: MSG_TOTALLY_INCOMPATIBLE_SUBGROUP ); break; } MYASSERT (temp1 && temp2); reportEntry = JoinPaths (temp1, temp2); reportEntryIsResource = FALSE; FreeStringResource (temp1); FreeStringResource (temp2); } break; case ACT_INC_SAFETY: MYASSERT (LinkName); temp1 = GetStringResource (MSG_INCOMPATIBLE_ROOT); temp2 = GetStringResource (MSG_REMOVED_FOR_SAFETY_SUBGROUP); MYASSERT (temp1 && temp2); reportEntry = JoinPaths (temp1, temp2); reportEntryIsResource = FALSE; FreeStringResource (temp1); FreeStringResource (temp2); newLinkName = JoinPaths (S_RUNKEYFOLDER, GetFileNameFromPath (LinkName)); break; case ACT_UNKNOWN: reportEntry = GetStringResource (MSG_UNKNOWN_ROOT); break; default: LOG((LOG_ERROR, "Unknown action for deferred announcement.")); return FALSE; } if (!newLinkName) { newLinkName = LinkName; } if (reportEntry != NULL) { linkStruct->ReportEntry = PoolMemDuplicateString (g_LinksPool, reportEntry); if (reportEntryIsResource) { FreeStringResource (reportEntry); } else { FreePathString (reportEntry); } } linkStruct->LinkName = newLinkName?PoolMemDuplicateString (g_LinksPool, newLinkName):NULL; // // all we need to set now is the category // // if we have a migdb context with a Localized name section // if ((migDbContext != NULL) && (migDbContext->SectLocalizedName != NULL) ) { linkStruct->Context = PoolMemDuplicateString (g_LinksPool, migDbContext->SectLocalizedName); linkStruct->Category = PoolMemDuplicateString (g_LinksPool, migDbContext->SectLocalizedName); priority = 0; } else { linkStruct->Context = PoolMemDuplicateString (g_LinksPool, newLinkName?newLinkName:ModuleName); if (newLinkName == NULL) { MYASSERT (migDbContext); if (migDbContext->SectName) { linkStruct->Category = PoolMemDuplicateString (g_LinksPool, migDbContext->SectName); } else { linkStruct->Category = NULL; } priority = 0; } else { linkName = GetFileNameFromPath (newLinkName); extPtr = GetFileExtensionFromPath (linkName); if (extPtr != NULL) { extPtr = _tcsdec (linkName, extPtr); } if (extPtr == NULL) { extPtr = GetEndOfString (linkName); } messageId = 0; priority = MAX_PRIORITY; if (MemDbEnumFirstValue (&eNicePaths, MEMDB_CATEGORY_NICE_PATHS"\\*", MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { do { pattern = JoinPaths (eNicePaths.szName, "\\*"); if (IsPatternMatch (pattern, newLinkName)) { if (priority > eNicePaths.UserFlags) { messageId = eNicePaths.dwValue; priority = eNicePaths.UserFlags; } } FreePathString (pattern); } while (MemDbEnumNextValue (&eNicePaths)); } category = AllocText ((PBYTE) extPtr - (PBYTE) linkName + sizeof (TCHAR)); p = linkName; q = category; while (p < extPtr) { if (_tcsnextc (p) == TEXT(' ')) { do { p++; } while (_tcsnextc (p) == TEXT(' ')); if (q > category && *p) { *q++ = TEXT(' '); } } else if (IsLeadByte (*p)) { *q++ = *p++; *q++ = *p++; } else { *q++ = *p++; } } *q = 0; if (messageId == 0) { lastDir = GetLastDirFromPath (newLinkName); drive = GetDriveFromPath (newLinkName); if (drive != NULL) { drive[0] = (TCHAR)toupper (drive[0]); if (lastDir != NULL) { argArray [0] = category; argArray [1] = lastDir; argArray [2] = drive; tempParse = (PTSTR)ParseMessageID (MSG_NICE_PATH_DRIVE_AND_FOLDER, argArray); } else { argArray [0] = category; argArray [1] = drive; tempParse = (PTSTR)ParseMessageID (MSG_NICE_PATH_DRIVE, argArray); } } else { if (lastDir != NULL) { argArray [0] = category; argArray [1] = lastDir; tempParse = (PTSTR)ParseMessageID (MSG_NICE_PATH_FOLDER, argArray); } else { argArray [0] = category; tempParse = (PTSTR)ParseMessageID (MSG_NICE_PATH_LINK, argArray); } } linkStruct->Category = PoolMemDuplicateString (g_LinksPool, tempParse); FreeStringResourcePtrA (&tempParse); priority = MAX_PRIORITY; } else { tempParse = (PTSTR)ParseMessageID (messageId, &category); StringCopy (category, tempParse); linkStruct->Category = PoolMemDuplicateString (g_LinksPool, tempParse); FreeStringResourcePtrA (&tempParse); } FreeText (category); } } linkStruct->LinkNameNoPath = linkName?PoolMemDuplicateString (g_LinksPool, linkName):linkStruct->Context; MemDbBuildKey ( key, MEMDB_CATEGORY_REPORT_LINKS, linkStruct->ReportEntry, linkName?linkName:linkStruct->Context, ModuleName); if ((!MemDbGetValueAndFlags (key, &oldValue, &oldPrior)) || (oldPrior > priority) ) { MemDbSetValueAndFlags (key, (DWORD)linkStruct, priority, 0); } if (newLinkName != LinkName) { FreePathString (newLinkName); } return TRUE; } BOOL pIsGUIDLauncherApproved ( IN PCTSTR FileName ) { INFCONTEXT context; MYASSERT (g_Win95UpgInf != INVALID_HANDLE_VALUE); return (SetupFindFirstLine (g_Win95UpgInf, S_APPROVED_GUID_LAUNCHER, FileName, &context)); } #define GUID_LEN (sizeof ("{00000000-0000-0000-0000-000000000000}") - 1) #define GUID_DASH_1 (sizeof ("{00000000") - 1) #define GUID_DASH_2 (sizeof ("{00000000-0000") - 1) #define GUID_DASH_3 (sizeof ("{00000000-0000-0000") - 1) #define GUID_DASH_4 (sizeof ("{00000000-0000-0000-0000") - 1) BOOL pSendCmdLineGuidsToMemdb ( IN PCTSTR File, IN PCTSTR Target, IN PCTSTR Arguments ) /*++ Routine Description: pSendCmdLineGuidsToMemdb saves any GUIDs contained in a command line to memdb, along with the file name. Later, OLEREG resolves the GUIDs and deletes the file if a GUID is incompatible. Arguments: File - Specifies the file to delete if the command line arguments contain an invalid GUID. Target - Specifies the Target (needs to be one of the approved targets for the LNK file to go away in an incompatible case). Arguments - Specifies a command line that may contain one or more GUIDs in the {a-b-c-d-e} format. Return value: TRUE - the operation was successful FALSE - the operation failed --*/ { LPCTSTR p, q; DWORD Offset; BOOL b; static DWORD Seq = 0; TCHAR TextSeq[16]; TCHAR Guid[GUID_LEN + 1]; PCTSTR namePtr; namePtr = GetFileNameFromPath (Target); if (namePtr && pIsGUIDLauncherApproved (namePtr)) { p = _tcschr (Arguments, TEXT('{')); while (p) { q = _tcschr (p, TEXT('}')); if (q && ((q - p) == (GUID_LEN - 1))) { if (p[GUID_DASH_1] == TEXT('-') && p[GUID_DASH_2] == TEXT('-') && p[GUID_DASH_3] == TEXT('-') && p[GUID_DASH_4] == TEXT('-') ) { // // Extract the GUID // q = _tcsinc (q); StringCopyAB (Guid, p, q); // // Add the file name // b = MemDbSetValueEx ( MEMDB_CATEGORY_LINK_STRINGS, File, NULL, NULL, 0, &Offset ); if (b) { // // Now add an entry for the GUID // Seq++; wsprintf (TextSeq, TEXT("%u"), Seq); b = MemDbSetValueEx ( MEMDB_CATEGORY_LINK_GUIDS, Guid, TextSeq, NULL, Offset, NULL ); } if (!b) { LOG ((LOG_ERROR, "Failed to store command line guids.")); } } } p = _tcschr (p + 1, TEXT('{')); } } return TRUE; } BOOL pIsFileInStartup ( IN PCTSTR FileName ) { TCHAR key [MEMDB_MAX]; MemDbBuildKey (key, MEMDB_CATEGORY_SF_STARTUP, FileName, NULL, NULL); return (MemDbGetPatternValue (key, NULL)); } BOOL pProcessShortcut ( IN PCTSTR FileName, IN IShellLink *ShellLink, IN IPersistFile *PersistFile ) { TCHAR shortcutTarget [MEMDB_MAX]; TCHAR shortcutArgs [MEMDB_MAX]; TCHAR shortcutWorkDir [MEMDB_MAX]; TCHAR shortcutIconPath [MEMDB_MAX]; PTSTR shortcutNewTarget = NULL; PTSTR shortcutNewArgs = NULL; PTSTR shortcutNewIconPath = NULL; PTSTR shortcutNewWorkDir = NULL; PTSTR commandPath = NULL; PTSTR fullPath = NULL; PCTSTR extPtr; INT shortcutIcon; INT newShortcutIcon; DWORD shortcutShowMode; WORD shortcutHotKey; DWORD fileStatus; BOOL msDosMode; BOOL dosApp; DWORD attrib; LNK_EXTRA_DATA ExtraData; INT lnkIdx; TCHAR lnkIdxStr [10]; BOOL toBeModified = FALSE; BOOL ConvertedLnk = FALSE; DWORD announcement; DWORD availability; __try { fileStatus = GetFileStatusOnNt (FileName); if (((fileStatus & FILESTATUS_DELETED ) == FILESTATUS_DELETED ) || ((fileStatus & FILESTATUS_REPLACED) == FILESTATUS_REPLACED) ) { __leave; } if (!ExtractShortcutInfo ( shortcutTarget, shortcutArgs, shortcutWorkDir, shortcutIconPath, &shortcutIcon, &shortcutHotKey, &dosApp, &msDosMode, &shortcutShowMode, &ExtraData, FileName, ShellLink, PersistFile )) { __leave; } if (msDosMode) { // // we want to modify this PIF file so it doesn't have MSDOS mode set // we will only add it to the modify list. The NT side will know what // to do when a PIF is marked for beeing modify // toBeModified = TRUE; } if (IsFileMarkedForAnnounce (shortcutTarget)) { announcement = GetFileAnnouncement (shortcutTarget); if (g_ConfigOptions.ShowAllReport || ((announcement != ACT_INC_IHVUTIL) && (announcement != ACT_INC_PREINSTUTIL) && (announcement != ACT_INC_SIMILAROSFUNC) ) ) { HandleDeferredAnnounce (FileName, shortcutTarget, dosApp); } } fileStatus = GetFileStatusOnNt (shortcutTarget); if ((fileStatus & FILESTATUS_DELETED) == FILESTATUS_DELETED) { if (IsFileMarkedForAnnounce (shortcutTarget)) { if (!pIsFileInStartup (FileName)) { if (!g_ConfigOptions.KeepBadLinks) { RemoveLinkFromSystem (FileName); } else { // we only care about LNK files if (StringIMatch (GetFileExtensionFromPath (FileName), TEXT("LNK"))) { // let's see what kind of announcement we have here. // We want to leave the LNK as is if the app was announced // using MigDb. However, if the app was announced using // dynamic checking (module checking) then we want to point // this shortcut to our stub EXE announcement = GetFileAnnouncement (shortcutTarget); if ((announcement == ACT_INC_NOBADAPPS) || (announcement == ACT_REINSTALL) || (announcement == ACT_REINSTALL_BLOCK) || (announcement == ACT_INC_IHVUTIL) || (announcement == ACT_INC_PREINSTUTIL) || (announcement == ACT_INC_SIMILAROSFUNC) ) { // // This is the case when we want to redirect this LNK to point // to our lnk stub. Extract will fail if the icon is known-good. // In that case, keep using the target icon. // if (ExtractIconIntoDatFile ( (*shortcutIconPath)?shortcutIconPath:shortcutTarget, shortcutIcon, &g_IconContext, &newShortcutIcon )) { shortcutNewIconPath = JoinPaths (g_System32Dir, TEXT("migicons.exe")); shortcutIcon = newShortcutIcon; } else { shortcutNewIconPath = GetPathStringOnNt ( (*shortcutIconPath) ? shortcutIconPath : shortcutTarget ); } availability = g_ConfigOptions.ShowAllReport || ((announcement != ACT_INC_IHVUTIL) && (announcement != ACT_INC_PREINSTUTIL) && (announcement != ACT_INC_SIMILAROSFUNC) ); lnkIdx = pAddLinkStubToMemDb ( FileName, shortcutTarget, shortcutArgs, shortcutWorkDir, shortcutNewIconPath, shortcutIcon + 1, // Add 1 because lnkstub.exe is one-based, but we are zero based shortcutShowMode, announcement, availability ); wsprintf (lnkIdxStr, TEXT("%d"), lnkIdx); shortcutNewTarget = JoinPaths (g_System32Dir, S_LNKSTUB_EXE); shortcutNewArgs = DuplicatePathString (lnkIdxStr, 0); pAddLinkEditToMemDb ( FileName, shortcutNewTarget, shortcutNewArgs, shortcutNewWorkDir, shortcutNewIconPath, shortcutIcon, // don't add one -- shortcuts are zero based NULL, TRUE ); } } else { RemoveLinkFromSystem (FileName); } } } else { // // This is a startup item // RemoveLinkFromSystem (FileName); } } else { RemoveLinkFromSystem (FileName); } __leave; } if ((fileStatus & FILESTATUS_REPLACED) != FILESTATUS_REPLACED) { // // this target is not replaced by a migration DLL or by NT. We need // to know if this is a "known good" target. If not, we will announce // this link as beeing "unknown" // if (!IsFileMarkedAsKnownGood (shortcutTarget)) { fullPath = JoinPaths (shortcutWorkDir, shortcutTarget); if (!IsFileMarkedAsKnownGood (fullPath)) { extPtr = GetFileExtensionFromPath (shortcutTarget); if (extPtr) { if (StringIMatch (extPtr, TEXT("EXE"))) { // // This one statement controls our // "unknown" category. We have the // ability to list the things we don't // recognize. // // It is currently "off". // //HandleDeferredAnnounce (FileName, shortcutTarget, dosApp); } } } FreePathString (fullPath); } } // // If this LNK points to a target that will change, back up the // original LNK, because we might change it. // if (fileStatus & ALL_CHANGE_OPERATIONS) { MarkFileForBackup (FileName); } // // If target points to an OLE object, remove any links to incompatible OLE objects // pSendCmdLineGuidsToMemdb (FileName, shortcutTarget, shortcutArgs); //all we try to do now is to see if this lnk or pif file is going to be edited //on NT side. That is if target or icon should change. shortcutNewTarget = GetPathStringOnNt (shortcutTarget); if (!StringIMatch (shortcutNewTarget, shortcutTarget)) { toBeModified = TRUE; // // special case for COMMAND.COM // if (shortcutArgs [0] == 0) { commandPath = JoinPaths (g_System32Dir, S_COMMAND_COM); if (StringIMatch (commandPath, shortcutNewTarget)) { if (msDosMode) { // // remove MS-DOS mode PIF files that point to command.com // RemoveLinkFromSystem (FileName); // // If msdosmode was on, we need to determine how we are going to handle // boot16. We will turn on boot16 mode if: // (a) The .pif points to something besides command.com // (b) The .pif is in a shell folder. // // Note that the check for b simply entails seeing if the PIF file has // OPERATION_FILE_MOVE_SHELL_FOLDER associated with it. // // if (msDosMode && *g_Boot16 == BOOT16_AUTOMATIC) { if (!StringIMatch(GetFileNameFromPath (shortcutNewTarget?shortcutNewTarget:shortcutTarget), S_COMMAND_COM) || IsFileMarkedForOperation (FileName, OPERATION_FILE_MOVE_SHELL_FOLDER)) { *g_Boot16 = BOOT16_YES; } } __leave; } else { ConvertedLnk = TRUE; FreePathString (shortcutNewTarget); shortcutNewTarget = JoinPaths (g_System32Dir, S_CMD_EXE); } } FreePathString (commandPath); shortcutNewArgs = NULL; } else { shortcutNewArgs = DuplicatePathString (shortcutArgs, 0); } } else { FreePathString (shortcutNewTarget); shortcutNewTarget = NULL; } // // If msdosmode was on, we need to determine how we are going to handle // boot16. We will turn on boot16 mode if: // (a) The .pif points to something besides command.com // (b) The .pif is in a shell folder. // // Note that the check for b simply entails seeing if the PIF file has // OPERATION_FILE_MOVE_SHELL_FOLDER associated with it. // // if (msDosMode && *g_Boot16 == BOOT16_AUTOMATIC) { if (!StringIMatch(GetFileNameFromPath (shortcutNewTarget?shortcutNewTarget:shortcutTarget), S_COMMAND_COM) || IsFileMarkedForOperation (FileName, OPERATION_FILE_MOVE_SHELL_FOLDER)) { *g_Boot16 = BOOT16_YES; } } // // If the link points to a directory, see that the directory survives on NT. // Potentially this directory can be cleaned up if it's in a shell folder and // becomes empty after our ObsoleteLinks check // attrib = QuietGetFileAttributes (shortcutTarget); if ((attrib != INVALID_ATTRIBUTES) && (attrib & FILE_ATTRIBUTE_DIRECTORY) ){ MarkDirectoryAsPreserved (shortcutNewTarget?shortcutNewTarget:shortcutTarget); } //OK, so much with target, let's see what's with the work dir shortcutNewWorkDir = GetPathStringOnNt (shortcutWorkDir); if (!StringIMatch (shortcutNewWorkDir, shortcutWorkDir)) { toBeModified = TRUE; } else { FreePathString (shortcutNewWorkDir); shortcutNewWorkDir = NULL; } // // If the working dir for this link is a directory, see that the directory survives on NT. // Potentially this directory can be cleaned up if it's in a shell folder and // becomes empty after our ObsoleteLinks check // attrib = QuietGetFileAttributes (shortcutWorkDir); if ((attrib != INVALID_ATTRIBUTES) && (attrib & FILE_ATTRIBUTE_DIRECTORY) ){ MarkDirectoryAsPreserved (shortcutNewWorkDir?shortcutNewWorkDir:shortcutWorkDir); } //OK, so much with workdir, let's see what's with icon fileStatus = GetFileStatusOnNt (shortcutIconPath); if ((fileStatus & FILESTATUS_DELETED) || ((fileStatus & FILESTATUS_REPLACED) && (fileStatus & FILESTATUS_NTINSTALLED)) || (IsFileMarkedForOperation (shortcutIconPath, OPERATION_FILE_MOVE_SHELL_FOLDER)) ) { // // Our icon will go away, because our file is getting deleted or // replaced. Let's try to preserve it. Extract will fail only if // the icon is known-good. // if (ExtractIconIntoDatFile ( shortcutIconPath, shortcutIcon, &g_IconContext, &newShortcutIcon )) { shortcutNewIconPath = JoinPaths (g_System32Dir, TEXT("migicons.exe")); shortcutIcon = newShortcutIcon; toBeModified = TRUE; } } if (!shortcutNewIconPath) { shortcutNewIconPath = GetPathStringOnNt (shortcutIconPath); if (!StringIMatch (shortcutNewIconPath, shortcutIconPath)) { toBeModified = TRUE; } else { FreePathString (shortcutNewIconPath); shortcutNewIconPath = NULL; } } if (toBeModified) { if (ConvertedLnk) { // // Set this for modifying PIF to LNK // pAddLinkEditToMemDb ( FileName, shortcutNewTarget?shortcutNewTarget:shortcutTarget, shortcutNewArgs?shortcutNewArgs:shortcutArgs, shortcutNewWorkDir?shortcutNewWorkDir:shortcutWorkDir, shortcutNewIconPath?shortcutNewIconPath:shortcutIconPath, shortcutIcon, &ExtraData, FALSE ); } else { pAddLinkEditToMemDb ( FileName, shortcutNewTarget, shortcutNewArgs, shortcutNewWorkDir, shortcutNewIconPath, shortcutIcon, NULL, FALSE ); } } } __finally { if (shortcutNewWorkDir != NULL) { FreePathString (shortcutNewWorkDir); } if (shortcutNewIconPath != NULL) { FreePathString (shortcutNewIconPath); } if (shortcutNewArgs != NULL) { FreePathString (shortcutNewArgs); } if (shortcutNewTarget != NULL) { FreePathString (shortcutNewTarget); } } return TRUE; } PCTSTR pBuildNewCategory ( IN PCTSTR LinkName, IN PCTSTR Category, IN UINT Levels ) { PCTSTR *levPtrs = NULL; PCTSTR wackPtr = NULL; PCTSTR result = NULL; PCTSTR resultTmp = NULL; UINT index = 0; UINT indexLnk = 0; MYASSERT (Levels); levPtrs = (PCTSTR *) PoolMemGetMemory (g_LinksPool, (Levels + 1) * sizeof (PCTSTR)); wackPtr = LinkName; while (wackPtr) { levPtrs[index] = wackPtr; wackPtr = _tcschr (wackPtr, TEXT('\\')); if (wackPtr) { wackPtr = _tcsinc (wackPtr); index ++; if (index > Levels) { index = 0; } } } indexLnk = index; if (index == Levels) { index = 0; } else { index ++; } resultTmp = StringSearchAndReplace (levPtrs [index], levPtrs [indexLnk], Category); if (resultTmp) { result = StringSearchAndReplace (resultTmp, TEXT("\\"), TEXT("->")); } else { result = NULL; } FreePathString (resultTmp); PoolMemReleaseMemory (g_LinksPool, (PVOID) levPtrs); return result; } VOID pGatherInfoFromDefaultPif ( VOID ) { PCTSTR defaultPifPath = NULL; TCHAR tmpStr [20]; TCHAR pifTarget [MEMDB_MAX]; TCHAR pifArgs [MEMDB_MAX]; TCHAR pifWorkDir [MEMDB_MAX]; TCHAR pifIconPath [MEMDB_MAX]; INT pifIcon; BOOL pifMsDosMode; LNK_EXTRA_DATA pifExtraData; defaultPifPath = JoinPaths (g_WinDir, S_COMMAND_PIF); if (ExtractPifInfo ( pifTarget, pifArgs, pifWorkDir, pifIconPath, &pifIcon, &pifMsDosMode, &pifExtraData, defaultPifPath )) { _itoa (pifExtraData.FullScreen, tmpStr, 10); MemDbSetValueEx (MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_FULLSCREEN, tmpStr, NULL, 0, NULL); _itoa (pifExtraData.xSize, tmpStr, 10); MemDbSetValueEx (MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_XSIZE, tmpStr, NULL, 0, NULL); _itoa (pifExtraData.ySize, tmpStr, 10); MemDbSetValueEx (MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_YSIZE, tmpStr, NULL, 0, NULL); _itoa (pifExtraData.QuickEdit, tmpStr, 10); MemDbSetValueEx (MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_QUICKEDIT, tmpStr, NULL, 0, NULL); MemDbSetValueEx (MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_FONTNAME, pifExtraData.FontName, NULL, 0, NULL); _itoa (pifExtraData.xFontSize, tmpStr, 10); MemDbSetValueEx (MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_XFONTSIZE, tmpStr, NULL, 0, NULL); _itoa (pifExtraData.yFontSize, tmpStr, 10); MemDbSetValueEx (MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_YFONTSIZE, tmpStr, NULL, 0, NULL); _itoa (pifExtraData.FontWeight, tmpStr, 10); MemDbSetValueEx (MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_FONTWEIGHT, tmpStr, NULL, 0, NULL); _itoa (pifExtraData.FontFamily, tmpStr, 10); MemDbSetValueEx (MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_FONTFAMILY, tmpStr, NULL, 0, NULL); _itoa (pifExtraData.CurrentCodePage, tmpStr, 10); MemDbSetValueEx (MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_CODEPAGE, tmpStr, NULL, 0, NULL); } FreePathString (defaultPifPath); } BOOL pProcessLinks ( VOID ) { MEMDB_ENUM enumItems; MEMDB_ENUM enumDups; TCHAR pattern[MEMDB_MAX]; IShellLink *shellLink; IPersistFile *persistFile; PLINK_STRUCT linkStruct, linkDup; BOOL resolved; PCTSTR newCategory = NULL; PCTSTR dupCategory = NULL; UINT levels = 0; DWORD count = 0; MYASSERT (g_LinksPool); if (InitCOMLink (&shellLink, &persistFile)) { wsprintf (pattern, TEXT("%s\\*"), MEMDB_CATEGORY_SHORTCUTS); if (MemDbEnumFirstValue ( &enumItems, pattern, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY )) { do { if (!SafeModeActionCrashed (SAFEMODEID_LNK9X, enumItems.szName)) { SafeModeRegisterAction(SAFEMODEID_LNK9X, enumItems.szName); if (!pProcessShortcut (enumItems.szName, shellLink, persistFile)) { LOG((LOG_ERROR, "Error processing shortcut %s", enumItems.szName)); } count++; if (!(count % 4)) { TickProgressBar (); } SafeModeUnregisterAction(); } } while (MemDbEnumNextValue (&enumItems)); } FreeCOMLink (&shellLink, &persistFile); } if (MemDbEnumFirstValue (&enumItems, MEMDB_CATEGORY_REPORT_LINKS"\\*", MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { do { newCategory = NULL; levels = 0; linkStruct = (PLINK_STRUCT)enumItems.dwValue; if (linkStruct->LinkName) { resolved = !(StringIMatch (linkStruct->LinkNameNoPath, GetFileNameFromPath (linkStruct->LinkName))); } else { resolved = TRUE; } while (!resolved) { resolved = TRUE; MemDbBuildKey ( pattern, MEMDB_CATEGORY_REPORT_LINKS, TEXT("*"), linkStruct->LinkNameNoPath, TEXT("*") ); if (MemDbEnumFirstValue (&enumDups, pattern, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { do { linkDup = (PLINK_STRUCT)enumDups.dwValue; if ((enumItems.Offset != enumDups.Offset) && (enumItems.UserFlags == enumDups.UserFlags) && (StringIMatch (linkStruct->Category, linkDup->Category)) ) { if (newCategory) { dupCategory = pBuildNewCategory (linkDup->LinkName, linkDup->Category, levels); if (!dupCategory) { MYASSERT (FALSE); continue; } if (!StringIMatch (dupCategory, newCategory)) { FreePathString (dupCategory); continue; } FreePathString (newCategory); } levels++; newCategory = pBuildNewCategory (linkStruct->LinkName, linkStruct->Category, levels); resolved = FALSE; break; } } while (MemDbEnumNextValue (&enumDups)); } } pReportEntry ( linkStruct->ReportEntry, newCategory?newCategory:linkStruct->Category, linkStruct->MigDbContext?linkStruct->MigDbContext->Message:NULL, linkStruct->Context, linkStruct->Object ); if (newCategory) { newCategory = NULL; } } while (MemDbEnumNextValue (&enumItems)); } TickProgressBar (); // gather default command prompt attributes pGatherInfoFromDefaultPif (); DoneLinkAnnounce (); // // Delete MemDb tree used for this phase // MemDbDeleteTree (MEMDB_CATEGORY_REPORT_LINKS); return TRUE; } DWORD ProcessLinks ( IN DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_PROCESS_LINKS; case REQUEST_RUN: if (!pProcessLinks ()) { return GetLastError (); } else { return ERROR_SUCCESS; } default: DEBUGMSG ((DBG_ERROR, "Bad parameter in ProcessLinks")); } return 0; } BOOL pProcessCPLs ( VOID ) { CHAR pattern[MEMDB_MAX]; MEMDB_ENUM enumItems; DWORD announcement; PMIGDB_CONTEXT context; MemDbBuildKey (pattern, MEMDB_CATEGORY_CPLS, TEXT("*"), NULL, NULL); if (MemDbEnumFirstValue (&enumItems, pattern, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { do { if ((IsFileMarkedForAnnounce (enumItems.szName)) && (IsDisplayableCPL (enumItems.szName)) ) { announcement = GetFileAnnouncement (enumItems.szName); context = (PMIGDB_CONTEXT) GetFileAnnouncementContext (enumItems.szName); ReportControlPanelApplet ( enumItems.szName, context, announcement ); } } while (MemDbEnumNextValue (&enumItems)); } return TRUE; } DWORD ProcessCPLs ( IN DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_PROCESS_CPLS; case REQUEST_RUN: if (!pProcessCPLs ()) { return GetLastError (); } else { return ERROR_SUCCESS; } default: DEBUGMSG ((DBG_ERROR, "Bad parameter in ProcessCPLs")); } return 0; }