/* '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 "common.h" #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) { TCHAR szMsg[1024],*pszInfo2; char *pszErr; pszErr=GetWinErrorDescription(res); pszInfo2=s2t(pszInfo,fInfoUnicode,FALSE); /* does NULL check */ mir_sntprintf(szMsg,SIZEOF(szMsg),_T("Access failed:\n%.64hs(%.128s)\n%.250hs(%u)\n%.256hs (%u)"),pszFunc,pszInfo2,pszFile,nLine,pszErr,res); MessageBox(NULL,szMsg,_T("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) { char *pszClass; pszClass=(char*)mir_alloc((lstrlenA(pszFileExt)+12)*sizeof(TCHAR)); if(pszClass!=NULL) /* using correctly formated PROGID */ wsprintfA(pszClass,"miranda%sfile",pszFileExt); /* includes dot, buffer safe */ return pszClass; } // mir_free() the return value char *MakeUrlClassName(const char *pszUrl) { char *pszClass; pszClass=mir_strdup(pszUrl); if(pszClass!=NULL) /* remove trailing : */ pszClass[lstrlenA(pszClass)-1]=0; return pszClass; } static BOOL IsFileClassName(const char *pszClassName,const char **ppszFileExt) { *ppszFileExt=strchr(pszClassName,'.'); return *ppszFileExt!=NULL; } // mir_free() the return value TCHAR *MakeRunCommand(BOOL fMirExe,BOOL fFixedDbProfile) { extern HINSTANCE hInst; TCHAR szExe[MAX_PATH],*pszFmt,*pszRunCmd=NULL; char szDbFile[MAX_PATH]; int cch; if (!fFixedDbProfile || !CallService(MS_DB_GETPROFILENAME,SIZEOF(szDbFile),(LPARAM)szDbFile)) if(GetModuleFileNameWorkaround(fMirExe?NULL:hInst,szExe,SIZEOF(szExe))) { /* db file */ if (!fFixedDbProfile) lstrcpyA(szDbFile,"%1"); /* buffer safe */ /* size */ cch=lstrlen(szExe)+lstrlenA(szDbFile); if(fMirExe) { /* run command for miranda32.exe */ cch+=7; pszFmt=_T("\"%s\" \"%hs\""); } else { DWORD len; /* run command for rundll32.exe calling WaitForDDE */ cch+=28; pszFmt=_T("rundll32.exe %s,WaitForDDE \"%hs\""); /* ensure the command line is not too long */ GetShortPathName(szExe,szExe,SIZEOF(szExe)); /* surround by quotes if failed */ len=lstrlen(szExe); if(_tcschr(szExe,_T(' '))!=NULL && (len+2)sizeof(TCHAR)) { pszVal=(TCHAR*)mir_alloc(dwSize+sizeof(TCHAR)); if(pszVal!=NULL) { /* get value */ if (!RegQueryValueEx(hKey,pszValName,NULL,&dwType,(BYTE*)pszVal,&dwSize)) { pszVal[dwSize/sizeof(TCHAR)]=0; if(dwType==REG_EXPAND_SZ) { dwSize=MAX_PATH; pszVal2=(TCHAR*)mir_alloc(dwSize*sizeof(TCHAR)); 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 TCHAR *pszValName,const TCHAR *pszCmpVal) { BOOL fSame=FALSE; TCHAR *pszVal; pszVal=GetRegStrValue(hKey,pszValName); if(pszVal!=NULL) { fSame=!lstrcmp(pszVal,pszCmpVal); mir_free(pszVal); } return fSame; } // hKey must have been opened with KEY_QUERY_VALUE access right static BOOL IsRegStrValueA(HKEY hKey,const TCHAR *pszValName,const char *pszCmpVal) { BOOL fSame=FALSE; TCHAR *pszVal; char *pszValA; pszVal=GetRegStrValue(hKey,pszValName); if(pszVal!=NULL) { pszValA=t2a(pszVal); if(pszValA!=NULL) fSame=!lstrcmpA(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) { DBCONTACTWRITESETTING dbcws; dbcws.szModule="AssocMgr"; dbcws.szSetting=pszSetting; dbcws.value.type=DBVT_BLOB; dbcws.value.cpbVal=(WORD)(cbData+sizeof(DWORD)); dbcws.value.pbVal=(BYTE*)mir_alloc(cbData+sizeof(DWORD)); if(dbcws.value.pbVal==NULL) return; *(DWORD*)dbcws.value.pbVal=dwType; CopyMemory(dbcws.value.pbVal+sizeof(DWORD),pData,cbData); CallService(MS_DB_CONTACT_WRITESETTING,0,(LPARAM)&dbcws); mir_free(dbcws.value.pbVal); } // mir_free() the value returned in ppData static BOOL ReadDbBackupData(const char *pszSetting,DWORD *pdwType,BYTE **ppData,DWORD *pcbData) { DBCONTACTGETSETTING dbcgs; DBVARIANT dbv; dbcgs.szModule="AssocMgr"; dbcgs.szSetting=pszSetting; dbcgs.pValue=&dbv; if (!CallService(MS_DB_CONTACT_GETSETTING,0,(LPARAM)&dbcgs)) { if(dbv.type==DBVT_BLOB && dbv.cpbVal>=sizeof(DWORD)) { *pdwType=*(DWORD*)dbv.pbVal; *ppData=dbv.pbVal; *pcbData=dbv.cpbVal-sizeof(DWORD); MoveMemory(*ppData,*ppData+sizeof(DWORD),*pcbData); return TRUE; } CallService(MS_DB_CONTACT_FREEVARIANT,0,(LPARAM)&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 TCHAR *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)lstrlenA(*param->ppszDbPrefix)+lstrlenA(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; } lstrcatA(lstrcatA(*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; lstrcatA(*param->ppszDbPrefix,pszName); /* buffer safe */ ptszName=a2t(pszName); if(ptszName!=NULL) { if (!RegQueryValueEx(hKey,ptszName,NULL,&dwType,pData,&cbData)) { #ifdef _UNICODE WriteDbBackupData(*param->ppszDbPrefix,dwType,pData,cbData); #else if (!(dwType®F_ANSI)) /* sanity check, never happens */ WriteDbBackupData(*param->ppszDbPrefix,dwType®F_ANSI,pData,cbData); #endif } 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) { struct BackupRegTreeParam param; DWORD dwDbPrefixSize; param.level=0; param.pdwDbPrefixSize=&dwDbPrefixSize; param.ppszDbPrefix=(char**)&pszDbPrefix; pszDbPrefix=mir_strdup(pszDbPrefix); if(pszDbPrefix!=NULL) { dwDbPrefixSize=lstrlenA(pszDbPrefix)+1; BackupRegTree_Worker(hKey,pszSubKey,¶m); mir_free((char*)pszDbPrefix); } } static LONG RestoreRegTree(HKEY hKey,const char *pszSubKey,const char *pszDbPrefix) { char **ppszSettings,*pszSuffix,*pszPrefixWithSubKey; int nSettingsCount,i,nDbPrefixLen,nPrefixWithSubKeyLen; char *pslash=NULL,*pnext,*pkeys; char *pszValName; WCHAR *pwszValName; HKEY hSubKey; DWORD dwType,cbData; BYTE *pData; LONG res; nDbPrefixLen=lstrlenA(pszDbPrefix); nPrefixWithSubKeyLen=nDbPrefixLen+lstrlenA(pszSubKey)+1; pszPrefixWithSubKey=(char*)mir_alloc(nPrefixWithSubKeyLen+1); if(pszPrefixWithSubKey==NULL) return ERROR_OUTOFMEMORY; lstrcatA(lstrcatA(lstrcpyA(pszPrefixWithSubKey,pszDbPrefix),pszSubKey),"\\"); /* buffer safe */ res=ERROR_NO_MORE_ITEMS; if(pszPrefixWithSubKey!=NULL) { if(EnumDbPrefixSettings("AssocMgr",pszPrefixWithSubKey,&ppszSettings,&nSettingsCount)) { for(i=0;i