/* 'File Association Manager'-Plugin for Miranda IM Copyright (C) 2005-2007 H. Herkenrath This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program (AssocMgr-License.txt); if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "stdafx.h" extern HINSTANCE hInst; #ifdef _DEBUG /* Debug: Ensure all registry calls do succeed and have valid parameters. * Shows a details message box otherwise. */ static __inline LONG regchk(LONG res, const char *pszFunc, const void *pszInfo, BOOL fInfoUnicode, const char *pszFile, unsigned int nLine) { if (res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND && res != ERROR_NO_MORE_ITEMS) { wchar_t szMsg[1024], *pszInfo2; char *pszErr; pszErr = GetWinErrorDescription(res); pszInfo2 = s2t(pszInfo, fInfoUnicode, FALSE); /* does NULL check */ mir_snwprintf(szMsg, TranslateT("Access failed:\n%.64hs(%.128s)\n%.250hs(%u)\n%.256hs (%u)"), pszFunc, pszInfo2, pszFile, nLine, pszErr, res); MessageBox(NULL, szMsg, TranslateT("Registry warning"), MB_OK | MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TOPMOST | MB_TASKMODAL); if (pszErr != NULL) LocalFree(pszErr); mir_free(pszInfo2); /* does NULL check */ } return res; } #undef RegCloseKey #define RegCloseKey(hKey) \ regchk(RegCloseKey(hKey),"RegCloseKey",NULL,FALSE,__FILE__,__LINE__) #undef RegOpenKeyExA #define RegOpenKeyExA(hKey,szSubkey,opt,rights,phKey) \ regchk(RegOpenKeyExA(hKey,szSubkey,opt,rights,phKey),"RegOpenKeyExA",szSubkey,FALSE,__FILE__,__LINE__) #undef RegCreateKeyExA #define RegCreateKeyExA(hKey,szSubkey,x,y,opt,rights,sec,phKey,pDisp) \ regchk(RegCreateKeyExA(hKey,szSubkey,x,y,opt,rights,sec,phKey,pDisp),"RegCreateKeyExA",szSubkey,FALSE,__FILE__,__LINE__) #undef RegDeleteKeyA #define RegDeleteKeyA(hKey,szName) \ regchk(RegDeleteKeyA(hKey,szName),"RegDeleteKeyA",szName,FALSE,__FILE__,__LINE__) #undef RegSetValueExA #define RegSetValueExA(hSubKey,szName,x,type,pVal,size) \ regchk(RegSetValueExA(hSubKey,szName,x,type,pVal,size),"RegSetValueExA",szName,FALSE,__FILE__,__LINE__) #undef RegQueryValueExA #define RegQueryValueExA(hKey,szName,x,pType,pVal,pSize) \ regchk(RegQueryValueExA(hKey,szName,x,pType,pVal,pSize),"RegQueryValueExA",szName,FALSE,__FILE__,__LINE__) #undef RegQueryInfoKeyA #define RegQueryInfoKeyA(hKey,x,y,z,pnKeys,pnKeyLen,a,pnVals,pnNames,pnValLen,sec,pTime) \ regchk(RegQueryInfoKeyA(hKey,x,y,z,pnKeys,pnKeyLen,a,pnVals,pnNames,pnValLen,sec,pTime),"RegQueryInfoKeyA",NULL,FALSE,__FILE__,__LINE__) #undef RegEnumKeyExA #define RegEnumKeyExA(hKey,idx,pName,pnName,x,y,z,pTime) \ regchk(RegEnumKeyExA(hKey,idx,pName,pnName,x,y,z,pTime),"RegEnumKeyExA",NULL,FALSE,__FILE__,__LINE__) #undef RegDeleteValueA #define RegDeleteValueA(hKey,szName) \ regchk(RegDeleteValueA(hKey,szName),"RegDeleteValueA",szName,FALSE,__FILE__,__LINE__) #undef RegOpenKeyExW #define RegOpenKeyExW(hKey,szSubkey,x,sam,phKey) \ regchk(RegOpenKeyExW(hKey,szSubkey,x,sam,phKey),"RegOpenKeyExW",szSubkey,TRUE,__FILE__,__LINE__) #undef RegCreateKeyExW #define RegCreateKeyExW(hKey,szSubkey,x,y,z,rights,p,phKey,q) \ regchk(RegCreateKeyExW(hKey,szSubkey,x,y,z,rights,p,phKey,q),"RegCreateKeyExW",szSubkey,TRUE,__FILE__,__LINE__) #undef RegDeleteKeyW #define RegDeleteKeyW(hKey,szName) \ regchk(RegDeleteKeyW(hKey,szName),"RegDeleteKeyW",szName,TRUE,__FILE__,__LINE__) #undef RegSetValueExW #define RegSetValueExW(hSubKey,szName,x,type,pVal,size) \ regchk(RegSetValueExW(hSubKey,szName,x,type,pVal,size),"RegSetValueExW",szName,TRUE,__FILE__,__LINE__) #undef RegQueryValueExW #define RegQueryValueExW(hKey,szName,x,pType,pVal,pSize) \ regchk(RegQueryValueExW(hKey,szName,x,pType,pVal,pSize),"RegQueryValueExW",szName,TRUE,__FILE__,__LINE__) #undef RegQueryInfoKeyW #define RegQueryInfoKeyW(hKey,x,y,z,pnKeys,pnKeyLen,a,pnVals,pnNames,pnValLen,sec,pTime) \ regchk(RegQueryInfoKeyW(hKey,x,y,z,pnKeys,pnKeyLen,a,pnVals,pnNames,pnValLen,sec,pTime),"RegQueryInfoKeyW",NULL,TRUE,__FILE__,__LINE__) #undef RegEnumKeyExW #define RegEnumKeyExW(hKey,idx,pName,pnName,x,y,z,pTime) \ regchk(RegEnumKeyExW(hKey,idx,pName,pnName,x,y,z,pTime),"RegEnumKeyExW",NULL,TRUE,__FILE__,__LINE__) #undef RegDeleteValueW #define RegDeleteValueW(hKey,szName) \ regchk(RegDeleteValueW(hKey,szName),"RegDeleteValueW",szName,TRUE,__FILE__,__LINE__) #endif // _DEBUG /************************* Strings ********************************/ // mir_free() the return value char *MakeFileClassName(const char *pszFileExt) { size_t cbLen = mir_strlen(pszFileExt)+12; char *pszClass = (char*)mir_alloc(cbLen); if (pszClass != NULL) /* using correctly formated PROGID */ mir_snprintf(pszClass, cbLen, "miranda%sfile", pszFileExt); /* includes dot, buffer safe */ return pszClass; } // mir_free() the return value char *MakeUrlClassName(const char *pszUrl) { char *pszClass = mir_strdup(pszUrl); if (pszClass != NULL) /* remove trailing : */ pszClass[mir_strlen(pszClass)-1]=0; return pszClass; } static BOOL IsFileClassName(char *pszClassName, char **ppszFileExt) { *ppszFileExt = strchr(pszClassName,'.'); return *ppszFileExt!=NULL; } // mir_free() the return value wchar_t *MakeRunCommand(BOOL fMirExe,BOOL fFixedDbProfile) { wchar_t szDbFile[MAX_PATH], szExe[MAX_PATH], *pszFmt; if (fFixedDbProfile) { if ( CallService(MS_DB_GETPROFILENAMET, _countof(szDbFile), (LPARAM)szDbFile)) return NULL; wchar_t *p = wcsrchr(szDbFile, '.'); if (p) *p = 0; } else mir_wstrcpy(szDbFile, L"%1"); /* buffer safe */ if ( !GetModuleFileName(fMirExe ? NULL : hInst, szExe, _countof(szExe))) return NULL; if (fMirExe) /* run command for miranda32.exe */ pszFmt = L"\"%s\" \"/profile:%s\""; else { /* run command for rundll32.exe calling WaitForDDE */ pszFmt = L"rundll32.exe %s,WaitForDDE \"/profile:%s\""; /* ensure the command line is not too long */ GetShortPathName(szExe, szExe, _countof(szExe)); /* surround by quotes if failed */ size_t len = mir_wstrlen(szExe); if ( wcschr(szExe,' ') != NULL && (len+2) < _countof(szExe)) { memmove(szExe, szExe+1, (len+1)*sizeof(wchar_t)); szExe[len+2] = szExe[0] = '\"'; szExe[len+3] = 0; } } wchar_t tszBuffer[1024]; mir_snwprintf(tszBuffer, pszFmt, szExe, szDbFile); return mir_wstrdup(tszBuffer); } static BOOL IsValidRunCommand(const wchar_t *pszRunCmd) { wchar_t *buf,*pexe,*pargs; wchar_t szFullExe[MAX_PATH],*pszFilePart; buf=mir_wstrcpy((wchar_t*)_alloca((mir_wstrlen(pszRunCmd)+1)*sizeof(wchar_t)),pszRunCmd); /* split into executable path and arguments */ if (buf[0]=='\"') { pargs=wcschr(&buf[1],'\"'); if (pargs!=NULL) *(pargs++)=0; pexe=&buf[1]; if (*pargs==' ') ++pargs; } else { pargs=wcschr(buf,' '); if (pargs!=NULL) *pargs=0; pexe=buf; } if (SearchPath(NULL,pexe,L".exe",_countof(szFullExe),szFullExe,&pszFilePart)) { if (pszFilePart!=NULL) if (!mir_wstrcmpi(pszFilePart,L"rundll32.exe") || !mir_wstrcmpi(pszFilePart,L"rundll.exe")) { /* split into dll path and arguments */ if (pargs[0]=='\"') { ++pargs; pexe=wcschr(&pargs[1],'\"'); if (pexe!=NULL) *pexe=0; } else { pexe=wcschr(pargs,','); if (pexe!=NULL) *pexe=0; } return SearchPath(NULL,pargs,L".dll",0,NULL,NULL)!=0; } return TRUE; } return FALSE; } // mir_free() the return value wchar_t *MakeIconLocation(HMODULE hModule,WORD nIconResID) { wchar_t szModule[MAX_PATH],*pszIconLoc=NULL; int cch; if ((cch=GetModuleFileName(hModule,szModule,_countof(szModule))) != 0) { pszIconLoc=(wchar_t*)mir_alloc((cch+=8)*sizeof(wchar_t)); if (pszIconLoc!=NULL) mir_snwprintf(pszIconLoc, cch, L"%s,%i", szModule, -(int)nIconResID); /* id may be 0, buffer safe */ } return pszIconLoc; } // mir_free() the return value wchar_t *MakeAppFileName(BOOL fMirExe) { wchar_t szExe[MAX_PATH],*psz; if (GetModuleFileName(fMirExe?NULL:hInst,szExe,_countof(szExe))) { psz=wcsrchr(szExe,'\\'); if (psz!=NULL) ++psz; else psz=szExe; return mir_wstrdup(psz); } return NULL; } /************************* Helpers ********************************/ static LONG DeleteRegSubTree(HKEY hKey,const wchar_t *pszSubKey) { LONG res; DWORD nMaxSubKeyLen,cchSubKey; wchar_t *pszSubKeyBuf; HKEY hSubKey; if ((res=RegOpenKeyEx(hKey,pszSubKey,0,KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS|DELETE,&hSubKey))==ERROR_SUCCESS) { if ((res=RegQueryInfoKey(hSubKey,NULL,NULL,NULL,NULL,&nMaxSubKeyLen,NULL,NULL,NULL,NULL,NULL,NULL))==ERROR_SUCCESS) { pszSubKeyBuf=(wchar_t*)mir_alloc((nMaxSubKeyLen+1)*sizeof(wchar_t)); if (pszSubKeyBuf==NULL) res=ERROR_NOT_ENOUGH_MEMORY; while(!res) { cchSubKey=nMaxSubKeyLen+1; if ((res=RegEnumKeyEx(hSubKey,0,pszSubKeyBuf,&cchSubKey,NULL,NULL,NULL,NULL))==ERROR_SUCCESS) res=DeleteRegSubTree(hSubKey,pszSubKeyBuf); /* recursion */ } mir_free(pszSubKeyBuf); /* does NULL check */ if (res==ERROR_NO_MORE_ITEMS) res=ERROR_SUCCESS; } RegCloseKey(hSubKey); } if (!res) res=RegDeleteKey(hKey,pszSubKey); return res; } // hMainKey must have been opened with KEY_CREATE_SUB_KEY access right static LONG SetRegSubKeyStrDefValue(HKEY hMainKey,const wchar_t *pszSubKey,const wchar_t *pszVal) { HKEY hSubKey; LONG res=RegCreateKeyEx(hMainKey,pszSubKey,0,NULL,0,KEY_SET_VALUE|KEY_QUERY_VALUE,NULL,&hSubKey,NULL); if (!res) { res=RegSetValueEx(hSubKey,NULL,0,REG_SZ,(BYTE*)pszVal,(int)(mir_wstrlen(pszVal)+1)*sizeof(wchar_t)); RegCloseKey(hSubKey); } return res; } // hKey must have been opened with KEY_SET_VALUE access right static void SetRegStrPrefixValue(HKEY hKey,const wchar_t *pszValPrefix,const wchar_t *pszVal) { size_t dwSize = (mir_wstrlen(pszVal)+mir_wstrlen(pszValPrefix)+1)*sizeof(wchar_t); wchar_t *pszStr = (wchar_t*)_alloca(dwSize); mir_wstrcat(mir_wstrcpy(pszStr, pszValPrefix), pszVal); /* buffer safe */ RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE*)pszStr, (int)dwSize); } // hKey must have been opened with KEY_QUERY_VALUE access right // mir_free() the return value static wchar_t *GetRegStrValue(HKEY hKey,const wchar_t *pszValName) { wchar_t *pszVal,*pszVal2; DWORD dwSize,dwType; /* get size */ if (!RegQueryValueEx(hKey,pszValName,NULL,NULL,NULL,&dwSize) && dwSize>sizeof(wchar_t)) { pszVal=(wchar_t*)mir_alloc(dwSize+sizeof(wchar_t)); if (pszVal!=NULL) { /* get value */ if (!RegQueryValueEx(hKey,pszValName,NULL,&dwType,(BYTE*)pszVal,&dwSize)) { pszVal[dwSize/sizeof(wchar_t)]=0; if (dwType==REG_EXPAND_SZ) { dwSize=MAX_PATH; pszVal2=(wchar_t*)mir_alloc(dwSize*sizeof(wchar_t)); if (ExpandEnvironmentStrings(pszVal,pszVal2,dwSize)) { mir_free(pszVal); return pszVal2; } mir_free(pszVal2); } else if (dwType==REG_SZ) return pszVal; } mir_free(pszVal); } } return NULL; } // hKey must have been opened with KEY_QUERY_VALUE access right static BOOL IsRegStrValue(HKEY hKey,const wchar_t *pszValName,const wchar_t *pszCmpVal) { BOOL fSame=FALSE; wchar_t *pszVal=GetRegStrValue(hKey,pszValName); if (pszVal!=NULL) { fSame=!mir_wstrcmp(pszVal,pszCmpVal); mir_free(pszVal); } return fSame; } // hKey must have been opened with KEY_QUERY_VALUE access right static BOOL IsRegStrValueA(HKEY hKey,const wchar_t *pszValName,const char *pszCmpVal) { BOOL fSame=FALSE; char *pszValA; wchar_t *pszVal=GetRegStrValue(hKey,pszValName); if (pszVal!=NULL) { pszValA=t2a(pszVal); if (pszValA!=NULL) fSame=!mir_strcmp(pszValA,pszCmpVal); mir_free(pszValA); /* does NULL check */ mir_free(pszVal); } return fSame; } /************************* Backup to DB ***************************/ #define REGF_ANSI 0x80000000 /* this bit is set in dwType for ANSI registry data */ // pData must always be Unicode data, registry supports Unicode even on Win95 static void WriteDbBackupData(const char *pszSetting,DWORD dwType,BYTE *pData,DWORD cbData) { size_t cbLen = cbData + sizeof(DWORD); PBYTE buf = (PBYTE)mir_alloc(cbLen); if (buf) { *(DWORD*)buf = dwType; memcpy(buf+sizeof(DWORD), pData, cbData); db_set_blob(NULL, "AssocMgr", pszSetting, buf, (unsigned)cbLen); mir_free(buf); } } // mir_free() the value returned in ppData static BOOL ReadDbBackupData(const char *pszSetting,DWORD *pdwType,BYTE **ppData,DWORD *pcbData) { DBVARIANT dbv; if (!db_get(0, "AssocMgr", pszSetting, &dbv)) { if (dbv.type==DBVT_BLOB && dbv.cpbVal>=sizeof(DWORD)) { *pdwType=*(DWORD*)dbv.pbVal; *ppData=dbv.pbVal; *pcbData=dbv.cpbVal-sizeof(DWORD); memmove(*ppData,*ppData+sizeof(DWORD),*pcbData); return TRUE; } db_free(&dbv); } return FALSE; } struct BackupRegTreeParam { char **ppszDbPrefix; DWORD *pdwDbPrefixSize; int level; }; static void BackupRegTree_Worker(HKEY hKey,const char *pszSubKey,struct BackupRegTreeParam *param) { LONG res; DWORD nMaxSubKeyLen,nMaxValNameLen,nMaxValSize; DWORD index,cchName,dwType,cbData; BYTE *pData; char *pszName; register wchar_t *ptszName; DWORD nDbPrefixLen; if ((res=RegOpenKeyExA(hKey,pszSubKey,0,KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS,&hKey))==ERROR_SUCCESS) { if ((res=RegQueryInfoKey(hKey,NULL,NULL,NULL,NULL,&nMaxSubKeyLen,NULL,NULL,&nMaxValNameLen,&nMaxValSize,NULL,NULL))==ERROR_SUCCESS) { if (nMaxSubKeyLen>nMaxValNameLen) nMaxValNameLen=nMaxSubKeyLen; /* prepare buffer */ nDbPrefixLen = (DWORD)(mir_strlen(*param->ppszDbPrefix) + mir_strlen(pszSubKey) + 1); cchName = nDbPrefixLen + nMaxValNameLen + 3; if (cchName>*param->pdwDbPrefixSize) { pszName=(char*)mir_realloc(*param->ppszDbPrefix,cchName); if (pszName==NULL) return; *param->ppszDbPrefix=pszName; *param->pdwDbPrefixSize=cchName; } mir_strcat(mir_strcat(*param->ppszDbPrefix,pszSubKey),"\\"); /* buffer safe */ /* enum values */ pszName=(char*)mir_alloc(nMaxValNameLen+1); if (nMaxValSize==0) nMaxValSize=1; pData=(BYTE*)mir_alloc(nMaxValSize); if (pszName!=NULL && pData!=NULL) { index=0; while(!res) { cchName=nMaxValNameLen+1; cbData=nMaxValSize; if ((res=RegEnumValueA(hKey,index++,pszName,&cchName,NULL,NULL,NULL,NULL))==ERROR_SUCCESS) { (*param->ppszDbPrefix)[nDbPrefixLen]=0; mir_strcat(*param->ppszDbPrefix,pszName); /* buffer safe */ ptszName=a2t(pszName); if (ptszName!=NULL) { if (!RegQueryValueEx(hKey,ptszName,NULL,&dwType,pData,&cbData)) { WriteDbBackupData(*param->ppszDbPrefix,dwType,pData,cbData); } mir_free(ptszName); } } } if (res==ERROR_NO_MORE_ITEMS) res=ERROR_SUCCESS; } mir_free(pData); /* does NULL check */ /* enum subkeys */ if (param->level<32 && pszName!=NULL) { ++param->level; /* can be max 32 levels deep (after prefix), restriction of RegCreateKeyEx() */ index=0; while(!res) { cchName=nMaxSubKeyLen+1; if ((res=RegEnumKeyExA(hKey,index++,pszName,&cchName,NULL,NULL,NULL,NULL))==ERROR_SUCCESS) { (*param->ppszDbPrefix)[nDbPrefixLen]=0; BackupRegTree_Worker(hKey,pszName,param); /* recursion */ } } } if (res==ERROR_NO_MORE_ITEMS) res=ERROR_SUCCESS; mir_free(pszName); /* does NULL check */ } RegCloseKey(hKey); } } static void BackupRegTree(HKEY hKey, const char *pszSubKey, const char *pszDbPrefix) { char *prefix = mir_strdup(pszDbPrefix); struct BackupRegTreeParam param; DWORD dwDbPrefixSize; param.level = 0; param.pdwDbPrefixSize = &dwDbPrefixSize; param.ppszDbPrefix = (char**)&prefix; dwDbPrefixSize = (int)mir_strlen(prefix) + 1; BackupRegTree_Worker(hKey, pszSubKey, ¶m); mir_free(prefix); } static LONG RestoreRegTree(HKEY hKey,const char *pszSubKey,const char *pszDbPrefix) { char **ppszSettings,*pszSuffix; int nSettingsCount,i; char *pslash=NULL,*pnext,*pkeys; char *pszValName; WCHAR *pwszValName; HKEY hSubKey; DWORD dwType,cbData; BYTE *pData; int nDbPrefixLen = (int)mir_strlen(pszDbPrefix); int nPrefixWithSubKeyLen = nDbPrefixLen + (int)mir_strlen(pszSubKey) + 1; char *pszPrefixWithSubKey = (char*)mir_alloc(nPrefixWithSubKeyLen+1); if (pszPrefixWithSubKey == NULL) return ERROR_OUTOFMEMORY; mir_strcat(mir_strcat(mir_strcpy(pszPrefixWithSubKey,pszDbPrefix),pszSubKey),"\\"); /* buffer safe */ LONG res=ERROR_NO_MORE_ITEMS; if (pszPrefixWithSubKey!=NULL) { if (EnumDbPrefixSettings("AssocMgr",pszPrefixWithSubKey,&ppszSettings,&nSettingsCount)) { for(i=0;i