summaryrefslogtreecommitdiff
path: root/plugins/AssocMgr/src
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-07-04 08:25:18 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-07-04 08:25:18 +0000
commit5500b21532dfe16253820b109df71f8f5d3006f7 (patch)
tree79ce32a372e080691d7e4d8b85e3230e46a7ddf1 /plugins/AssocMgr/src
parent0624d6b4e9b82ccfaabf7a20d00c474a09354e8d (diff)
AssocMgr: changed folder structure
git-svn-id: http://svn.miranda-ng.org/main/trunk@751 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/AssocMgr/src')
-rw-r--r--plugins/AssocMgr/src/assoclist.cpp1102
-rw-r--r--plugins/AssocMgr/src/assoclist.h33
-rw-r--r--plugins/AssocMgr/src/common.h57
-rw-r--r--plugins/AssocMgr/src/dde.cpp295
-rw-r--r--plugins/AssocMgr/src/dde.h30
-rw-r--r--plugins/AssocMgr/src/main.cpp121
-rw-r--r--plugins/AssocMgr/src/reg.cpp1295
-rw-r--r--plugins/AssocMgr/src/reg.h51
-rw-r--r--plugins/AssocMgr/src/resource.h24
-rw-r--r--plugins/AssocMgr/src/test.cpp869
-rw-r--r--plugins/AssocMgr/src/test.h24
-rw-r--r--plugins/AssocMgr/src/utils.cpp194
-rw-r--r--plugins/AssocMgr/src/utils.h39
-rw-r--r--plugins/AssocMgr/src/version.h41
14 files changed, 4175 insertions, 0 deletions
diff --git a/plugins/AssocMgr/src/assoclist.cpp b/plugins/AssocMgr/src/assoclist.cpp
new file mode 100644
index 0000000000..4ccb5645fe
--- /dev/null
+++ b/plugins/AssocMgr/src/assoclist.cpp
@@ -0,0 +1,1102 @@
+/*
+
+'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"
+
+/* Options */
+extern HINSTANCE hInst;
+static HANDLE hHookOptInit;
+/* Services */
+static HANDLE hServiceAddFile, hServiceRemoveFile, hServiceAddUrl, hServiceRemoveUrl;
+
+/************************* Assoc List *****************************/
+
+typedef struct {
+ char *pszClassName; /* class name as used in registry and db */
+ TCHAR *pszDescription;
+ HINSTANCE hInstance; /* allowed to be NULL for miranda32.exe */
+ WORD nIconResID;
+ char *pszService;
+ WORD flags; /* set of FTDF_* and UTDF_* flags */
+ char *pszFileExt; /* file type: NULL for url type*/
+ char *pszMimeType; /* file type: allowed to be NULL */
+ TCHAR *pszVerbDesc; /* file type: allowed to be NULL */
+} ASSOCDATA;
+
+static ASSOCDATA *pAssocList; /* protected by csAssocList */
+static int nAssocListCount; /* protected by csAssocList */
+static CRITICAL_SECTION csAssocList;
+
+/************************* Assoc Enabled **************************/
+
+static BOOL IsAssocEnabled(const ASSOCDATA *assoc)
+{
+ char szSetting[MAXMODULELABELLENGTH];
+ mir_snprintf(szSetting, sizeof(szSetting), "enabled_%s", assoc->pszClassName);
+ return DBGetContactSettingByte(NULL, "AssocMgr", szSetting, (BYTE)!(assoc->flags&FTDF_DEFAULTDISABLED))!= 0;
+}
+
+static void SetAssocEnabled(const ASSOCDATA *assoc, BOOL fEnabled)
+{
+ char szSetting[MAXMODULELABELLENGTH];
+ TCHAR szDLL[MAX_PATH], szBuf[MAX_PATH];
+ mir_snprintf(szSetting, sizeof(szSetting), "enabled_%s", assoc->pszClassName);
+ DBWriteContactSettingByte(NULL, "AssocMgr", szSetting, (BYTE)fEnabled);
+ /* dll name for uninstall */
+ if(assoc->hInstance!= NULL && assoc->hInstance!= hInst && assoc->hInstance!= GetModuleHandle(NULL))
+ if(GetModuleFileName(assoc->hInstance, szBuf, SIZEOF(szBuf)))
+ if(CallService(MS_UTILS_PATHTORELATIVET, (WPARAM)szBuf, (LPARAM)szDLL)) {
+ mir_snprintf(szSetting, sizeof(szSetting), "module_%s", assoc->pszClassName);
+ DBWriteContactSettingTString(NULL, "AssocMgr", szSetting, szDLL);
+ }
+}
+
+static void DeleteAssocEnabledSetting(const ASSOCDATA *assoc)
+{
+ char szSetting[MAXMODULELABELLENGTH];
+ mir_snprintf(szSetting, sizeof(szSetting), "enabled_%s", assoc->pszClassName);
+ DBDeleteContactSetting(NULL, "AssocMgr", szSetting);
+ /* dll name for uninstall */
+ mir_snprintf(szSetting, sizeof(szSetting), "module_%s", assoc->pszClassName);
+ DBDeleteContactSetting(NULL, "AssocMgr", szSetting);
+}
+
+void CleanupAssocEnabledSettings(void)
+{
+ int nSettingsCount;
+ char **ppszSettings, *pszSuffix;
+ DBVARIANT dbv;
+ int i;
+ HANDLE hFile;
+ TCHAR szDLL[MAX_PATH];
+ char szSetting[MAXMODULELABELLENGTH];
+
+ /* delete old enabled_* settings if associated plugin no longer present */
+ if(EnumDbPrefixSettings("AssocMgr", "enabled_", &ppszSettings, &nSettingsCount)) {
+ EnterCriticalSection(&csAssocList);
+ for(i = 0;i<nSettingsCount;++i) {
+ pszSuffix = &ppszSettings[i][8];
+ mir_snprintf(szSetting, sizeof(szSetting), "module_%s", pszSuffix);
+ if (!DBGetContactSettingTString(NULL, "AssocMgr", szSetting, &dbv)) {
+ if(CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)dbv.ptszVal, (LPARAM)szDLL)) {
+ /* file still exists? */
+ hFile = CreateFile(szDLL, 0, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
+ if(hFile == INVALID_HANDLE_VALUE) {
+ DBDeleteContactSetting(NULL, "AssocMgr", ppszSettings[i]);
+ DBDeleteContactSetting(NULL, "AssocMgr", szSetting);
+ } else CloseHandle(hFile);
+ }
+ mir_free(dbv.ptszVal);
+ }
+ mir_free(ppszSettings[i]);
+ }
+ LeaveCriticalSection(&csAssocList);
+ mir_free(ppszSettings); /* does NULL check */
+ }
+}
+
+/************************* Mime Reg *******************************/
+
+static __inline void RememberMimeTypeAdded(const char *pszMimeType, const char *pszFileExt, BYTE fAdded)
+{
+ char szSetting[MAXMODULELABELLENGTH];
+ mir_snprintf(szSetting, sizeof(szSetting), "mime_%s", pszMimeType);
+ if(fAdded) DBWriteContactSettingString(NULL, "AssocMgr", szSetting, pszFileExt);
+ else DBDeleteContactSetting(NULL, "AssocMgr", szSetting);
+}
+
+static __inline BOOL WasMimeTypeAdded(const char *pszMimeType)
+{
+ char szSetting[MAXMODULELABELLENGTH];
+ DBVARIANT dbv;
+ BOOL fAdded = FALSE;
+ mir_snprintf(szSetting, sizeof(szSetting), "mime_%s", pszMimeType);
+ if (!DBGetContactSetting(NULL, "AssocMgr", szSetting, &dbv)) fAdded = TRUE;
+ else DBFreeVariant(&dbv);
+ return fAdded;
+}
+
+void CleanupMimeTypeAddedSettings(void)
+{
+ int nSettingsCount;
+ char **ppszSettings, *pszSuffix;
+ DBVARIANT dbv;
+ int i, j;
+
+ /* delete old mime_* settings and unregister the associated mime type */
+ if(EnumDbPrefixSettings("AssocMgr", "mime_", &ppszSettings, &nSettingsCount)) {
+ EnterCriticalSection(&csAssocList);
+ for(i = 0;i<nSettingsCount;++i) {
+ pszSuffix = &ppszSettings[i][5];
+ for(j = 0;j<nAssocListCount;++j)
+ if (!lstrcmpA(pszSuffix, pAssocList[j].pszMimeType))
+ break; /* mime type in current list */
+ if(j == nAssocListCount) { /* mime type not in current list */
+ if (!DBGetContactSetting(NULL, "AssocMgr", ppszSettings[i], &dbv)) {
+ if(dbv.type == DBVT_ASCIIZ)
+ RemoveRegMimeType(pszSuffix, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ DBDeleteContactSetting(NULL, "AssocMgr", ppszSettings[i]);
+ }
+ mir_free(ppszSettings[i]);
+ }
+ LeaveCriticalSection(&csAssocList);
+ mir_free(ppszSettings);
+ }
+}
+
+/************************* Shell Notify ***************************/
+
+#define SHELLNOTIFY_DELAY 3000 /* time for which assoc changes are buffered */
+
+static UINT nNotifyTimerID; /* protected by csNotifyTimer */
+static CRITICAL_SECTION csNotifyTimer;
+
+static void CALLBACK NotifyTimerProc(HWND hwnd, UINT msg, UINT_PTR nTimerID, DWORD dwTime)
+{
+ UNREFERENCED_PARAMETER(msg);
+ UNREFERENCED_PARAMETER(dwTime);
+ EnterCriticalSection(&csNotifyTimer);
+ KillTimer(hwnd, nTimerID);
+ if(nNotifyTimerID == nTimerID) /* might be stopped previously */
+ SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST|SHCNF_FLUSHNOWAIT, NULL, NULL);
+ nNotifyTimerID = 0;
+ LeaveCriticalSection(&csNotifyTimer);
+}
+
+static void NotifyAssocChange(BOOL fNow)
+{
+ EnterCriticalSection(&csNotifyTimer);
+ if(fNow) {
+ nNotifyTimerID = 0; /* stop previous timer */
+ SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST|SHCNF_FLUSH, NULL, NULL);
+ }
+ else nNotifyTimerID = SetTimer(NULL, nNotifyTimerID, SHELLNOTIFY_DELAY, NotifyTimerProc);
+ LeaveCriticalSection(&csNotifyTimer);
+}
+
+/************************* Assoc List Utils ***********************/
+
+// this function assumes it has got the csAssocList mutex
+static int FindAssocItem(const char *pszClassName)
+{
+ int i;
+ for(i = 0;i<nAssocListCount;++i)
+ if (!lstrcmpA(pszClassName, pAssocList[i].pszClassName))
+ return i;
+ return -1;
+}
+
+BOOL IsRegisteredAssocItem(const char *pszClassName)
+{
+ int index;
+ EnterCriticalSection(&csAssocList);
+ index = FindAssocItem(pszClassName);
+ LeaveCriticalSection(&csAssocList);
+ return index!= -1;
+}
+
+
+// this function assumes it has got the csAssocList mutex
+static ASSOCDATA* CopyAssocItem(const ASSOCDATA *assoc)
+{
+ ASSOCDATA *assoc2;
+ assoc2 = (ASSOCDATA*)mir_alloc(sizeof(ASSOCDATA));
+ if(assoc2 == NULL) return NULL;
+ assoc2->pszClassName = mir_strdup(assoc->pszClassName);
+ assoc2->pszDescription = mir_tstrdup(assoc->pszDescription);
+ assoc2->hInstance = assoc->hInstance;
+ assoc2->nIconResID = assoc->nIconResID;
+ assoc2->pszService = mir_strdup(assoc->pszService);
+ assoc2->flags = assoc->flags;
+ assoc2->pszFileExt = mir_strdup(assoc->pszFileExt);
+ assoc2->pszMimeType = mir_strdup(assoc->pszMimeType);
+ assoc2->pszVerbDesc = mir_tstrdup(assoc->pszVerbDesc);
+ if(assoc2->pszClassName == NULL || assoc2->pszDescription == NULL ||
+ (assoc2->pszFileExt == NULL && assoc->pszFileExt!= NULL)) {
+ mir_free(assoc2->pszClassName); /* does NULL check */
+ mir_free(assoc2->pszDescription); /* does NULL check */
+ mir_free(assoc2->pszService); /* does NULL check */
+ mir_free(assoc2->pszFileExt); /* does NULL check */
+ mir_free(assoc2->pszMimeType); /* does NULL check */
+ mir_free(assoc2->pszVerbDesc); /* does NULL check */
+ mir_free(assoc2);
+ return NULL;
+ }
+ return assoc2;
+}
+
+// this function assumes it has got the csAssocList mutex
+// this function assumes CoInitialize() has been called before
+static int ReplaceImageListAssocIcon(HIMAGELIST himl, const ASSOCDATA *assoc, int iPrevIndex)
+{
+ HICON hIcon = NULL;
+ int index;
+ if(himl == NULL) return -1;
+
+ /* load icon */
+ hIcon = LoadRegClassSmallIcon(assoc->pszClassName);
+ if(hIcon == NULL) {
+ SHFILEINFOA sfi;
+ if(SHGetFileInfoA((assoc->pszFileExt!= NULL)?assoc->pszFileExt:"", FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_SMALLICON|SHGFI_USEFILEATTRIBUTES))
+ hIcon = sfi.hIcon; /* WinXP: this icon is not updated until the process exits */
+ }
+ /* add icon */
+ if(hIcon == NULL) return -1;
+ index = ImageList_ReplaceIcon(himl, iPrevIndex, hIcon);
+ DestroyIcon(hIcon);
+ return index;
+}
+
+// the return value does not need to be freed
+// this function assumes it has got the csAssocList mutex
+static TCHAR* GetAssocTypeDesc(const ASSOCDATA *assoc)
+{
+ static TCHAR szDesc[32];
+ if(assoc->pszFileExt == NULL) mir_sntprintf(szDesc, SIZEOF(szDesc), TranslateT("%hs:"), assoc->pszClassName);
+ else mir_sntprintf(szDesc, SIZEOF(szDesc), TranslateT("%hs files"), assoc->pszFileExt);
+ return szDesc;
+}
+
+// this function assumes it has got the csAssocList mutex
+static BOOL IsAssocRegistered(const ASSOCDATA *assoc)
+{
+ BOOL fSuccess = FALSE, fIsUrl, fUseMainCmdLine;
+ TCHAR *pszRunCmd;
+
+ fIsUrl = (assoc->pszFileExt == NULL);
+ fUseMainCmdLine = (assoc->pszService == NULL);
+
+ /* class */
+ pszRunCmd = MakeRunCommand(fUseMainCmdLine, !fUseMainCmdLine);
+ if(pszRunCmd!= NULL)
+ fSuccess = IsRegClass(assoc->pszClassName, pszRunCmd);
+ mir_free(pszRunCmd); /* does NULL check */
+ /* file ext */
+ if (!fIsUrl)
+ fSuccess = IsRegFileExt(assoc->pszFileExt, assoc->pszClassName);
+
+ return fSuccess;
+}
+
+// this function assumes it has got the csAssocList mutex
+// call GetLastError() on error to get more error details
+static BOOL EnsureAssocRegistered(const ASSOCDATA *assoc)
+{
+ BOOL fSuccess = FALSE, fIsUrl, fUseMainCmdLine;
+ TCHAR *pszIconLoc, *pszRunCmd, *pszDdeCmd, *pszAppFileName;
+
+ fIsUrl = (assoc->pszFileExt == NULL);
+ fUseMainCmdLine = (assoc->pszService == NULL);
+
+ pszRunCmd = MakeRunCommand(fUseMainCmdLine, !fUseMainCmdLine);
+ if(pszRunCmd!= NULL) {
+ fSuccess = TRUE; /* tentatively */
+ /* do not overwrite user customized settings */
+ if (!IsRegClass(assoc->pszClassName, pszRunCmd)) {
+ /* class icon */
+ if (!assoc->nIconResID && fIsUrl) pszIconLoc = MakeIconLocation(NULL, 0); /* miranda logo */
+ else if (!assoc->nIconResID) pszIconLoc = MakeIconLocation(hInst, IDI_MIRANDAFILE); /* generic file */
+ else pszIconLoc = MakeIconLocation(assoc->hInstance, assoc->nIconResID);
+ /* register class */
+ if(fUseMainCmdLine) pszDdeCmd = NULL;
+ else pszDdeCmd = fIsUrl?DDEURLCMD:DDEFILECMD;
+ fSuccess = AddRegClass(assoc->pszClassName, assoc->pszDescription, pszIconLoc, _T(MIRANDANAME), pszRunCmd, pszDdeCmd, DDEAPP, DDETOPIC, assoc->pszVerbDesc, assoc->flags&FTDF_BROWSERAUTOOPEN, fIsUrl, assoc->flags&FTDF_ISSHORTCUT);
+ mir_free(pszIconLoc); /* does NULL check */
+ /* file type */
+ if(fSuccess && !fIsUrl) {
+ /* register mime type */
+ if(assoc->pszMimeType!= NULL)
+ if(AddRegMimeType(assoc->pszMimeType, assoc->pszFileExt, assoc->pszDescription))
+ RememberMimeTypeAdded(assoc->pszMimeType, assoc->pszFileExt, TRUE);
+ /* register file ext */
+ fSuccess = AddRegFileExt(assoc->pszFileExt, assoc->pszClassName, assoc->pszMimeType, assoc->flags&FTDF_ISTEXT);
+ /* register open-with */
+ pszAppFileName = MakeAppFileName(fUseMainCmdLine);
+ if(pszAppFileName!= NULL)
+ AddRegOpenWithExtEntry(pszAppFileName, assoc->pszFileExt, assoc->pszDescription);
+ mir_free(pszAppFileName); /* does NULL check */
+ }
+ }
+ mir_free(pszRunCmd);
+ }
+ else SetLastError(ERROR_OUTOFMEMORY);
+ return fSuccess;
+}
+
+// this function assumes it has got the csAssocList mutex
+// call GetLastError() on error to get more error details
+static BOOL UnregisterAssoc(const ASSOCDATA *assoc)
+{
+ BOOL fIsUrl, fUseMainCmdLine;
+ TCHAR *pszAppFileName, *pszRunCmd;
+
+ fIsUrl = (assoc->pszFileExt == NULL);
+ fUseMainCmdLine = (assoc->pszService == NULL);
+
+ /* class might have been registered by another instance */
+ pszRunCmd = MakeRunCommand(fUseMainCmdLine, !fUseMainCmdLine);
+ if(pszRunCmd!= NULL && !IsRegClass(assoc->pszClassName, pszRunCmd)) {
+ mir_free(pszRunCmd);
+ return TRUE; /* succeed anyway */
+ }
+ mir_free(pszRunCmd); /* does NULL check */
+
+ /* file type */
+ if (!fIsUrl) {
+ /* file extension */
+ RemoveRegFileExt(assoc->pszFileExt, assoc->pszClassName);
+ /* mime type */
+ if(assoc->pszMimeType!= NULL)
+ if(WasMimeTypeAdded(assoc->pszMimeType)) {
+ RemoveRegMimeType(assoc->pszMimeType, assoc->pszFileExt);
+ RememberMimeTypeAdded(assoc->pszMimeType, assoc->pszFileExt, FALSE);
+ }
+ /* open-with entry */
+ pszAppFileName = MakeAppFileName(fUseMainCmdLine);
+ if(pszAppFileName!= NULL)
+ RemoveRegOpenWithExtEntry(pszAppFileName, assoc->pszFileExt);
+ mir_free(pszAppFileName); /* does NULL check */
+ }
+ return RemoveRegClass(assoc->pszClassName);
+}
+
+/************************* Assoc List Workers *********************/
+
+/* this structure represents the head of both
+ * FILETYPEDESC and URLTYPEDESC structures.
+ * the head is identical for both structures. */
+typedef struct {
+ int cbSize; /* either sizeof(FILETYPEDESC) or sizeof(URLTYPEDESC) */
+ const void *pszDescription;
+ HINSTANCE hInstance;
+ UINT nIconResID;
+ const char *pszService;
+ DWORD flags;
+} TYPEDESCHEAD;
+
+// ownership of pszClassName, pszFileExt, pszVerbDesc and pszMimeType is transfered
+// to the storage list on success
+static BOOL AddNewAssocItem_Worker(char *pszClassName, const TYPEDESCHEAD *tdh, char *pszFileExt, TCHAR *pszVerbDesc, char *pszMimeType)
+{
+ ASSOCDATA *pAssocListBuf, *assoc;
+ int index;
+
+ /* is already in list? */
+ EnterCriticalSection(&csAssocList);
+ index = FindAssocItem(pszClassName);
+ if(index!= -1) return FALSE;
+
+ /* resize storage array */
+ pAssocListBuf = (ASSOCDATA*)mir_realloc(pAssocList, (nAssocListCount+1)*sizeof(ASSOCDATA));
+ if(pAssocListBuf == NULL) {
+ LeaveCriticalSection(&csAssocList);
+ return FALSE;
+ }
+ pAssocList = pAssocListBuf;
+
+ /* init new item */
+ assoc = &pAssocList[nAssocListCount];
+ assoc->pszClassName = pszClassName; /* no dup here */
+ assoc->pszDescription = s2t(tdh->pszDescription, tdh->flags&FTDF_UNICODE, TRUE); /* does NULL check */
+ assoc->hInstance = tdh->hInstance; /* hInstance is allowed to be NULL for miranda32.exe */
+ assoc->nIconResID = (WORD)tdh->nIconResID; /* default icon selected later on */
+ assoc->pszService = mir_strdup(tdh->pszService); /* does NULL check */
+ assoc->flags = (WORD)tdh->flags;
+ assoc->pszFileExt = pszFileExt;
+ assoc->pszMimeType = pszMimeType;
+ assoc->pszVerbDesc = pszVerbDesc;
+
+ /* error check */
+ if(assoc->pszDescription == NULL || (assoc->pszService == NULL && tdh->pszService!= NULL)) {
+ mir_free(assoc->pszService); /* does NULL check */
+ mir_free(assoc->pszDescription); /* does NULL check */
+ LeaveCriticalSection(&csAssocList);
+ return FALSE;
+ }
+
+ /* add registry keys */
+ if(IsAssocEnabled(assoc))
+ EnsureAssocRegistered(assoc);
+
+ ++nAssocListCount;
+ NotifyAssocChange(FALSE);
+ return TRUE;
+}
+
+// ownership of pszClassName is *not* transferd to storage list
+static BOOL RemoveAssocItem_Worker(const char *pszClassName)
+{
+ ASSOCDATA *pAssocListBuf, *assoc;
+ int index;
+
+ /* find index */
+ EnterCriticalSection(&csAssocList);
+ index = FindAssocItem(pszClassName);
+ if(index == -1) {
+ LeaveCriticalSection(&csAssocList);
+ return FALSE;
+ }
+ assoc = &pAssocList[index];
+
+ /* delete registry keys and db setting */
+ UnregisterAssoc(assoc);
+ if(assoc->pszMimeType!= NULL)
+ RememberMimeTypeAdded(assoc->pszMimeType, assoc->pszFileExt, FALSE);
+ DeleteAssocEnabledSetting(assoc);
+
+ /* free memory */
+ mir_free(assoc->pszClassName);
+ mir_free(assoc->pszDescription);
+ mir_free(assoc->pszService);
+ mir_free(assoc->pszFileExt); /* does NULL check */
+ mir_free(assoc->pszVerbDesc); /* does NULL check */
+ mir_free(assoc->pszMimeType); /* does NULL check */
+
+ /* resize storage array */
+ if ((index+1)<nAssocListCount)
+ MoveMemory(assoc, &pAssocList[index+1], ((nAssocListCount-index-1)*sizeof(ASSOCDATA)));
+ pAssocListBuf = (ASSOCDATA*)mir_realloc(pAssocList, (nAssocListCount-1)*sizeof(ASSOCDATA));
+ if(pAssocListBuf!= NULL) pAssocList = pAssocListBuf;
+ --nAssocListCount;
+ LeaveCriticalSection(&csAssocList);
+
+ NotifyAssocChange(FALSE);
+ return TRUE;
+}
+
+/************************* Services *******************************/
+
+static INT_PTR ServiceAddNewFileType(WPARAM wParam, LPARAM lParam)
+{
+ const FILETYPEDESC *ftd = (FILETYPEDESC*)lParam;
+ char *pszClassName, *pszFileExt, *pszMimeType;
+ TCHAR *pszVerbDesc;
+ UNREFERENCED_PARAMETER(wParam);
+
+ if(ftd->cbSize<sizeof(FILETYPEDESC))
+ return 1;
+ if(ftd->pszFileExt == NULL || ftd->pszFileExt[0]!= '.')
+ return 2;
+
+ pszFileExt = mir_strdup(ftd->pszFileExt);
+ pszClassName = MakeFileClassName(ftd->pszFileExt);
+ if(pszFileExt!= NULL && pszClassName!= NULL) {
+ pszVerbDesc = s2t(ftd->ptszVerbDesc, ftd->flags&FTDF_UNICODE, TRUE); /* does NULL check */
+ pszMimeType = mir_strdup(ftd->pszMimeType); /* does NULL check */
+ if(AddNewAssocItem_Worker(pszClassName, (TYPEDESCHEAD*)ftd, pszFileExt, pszVerbDesc, pszMimeType))
+ /* no need to free pszClassName, pszFileExt, pszVerbDesc and pszMimeType,
+ * as their ownership got transfered to storage list */
+ return 0;
+ }
+ mir_free(pszClassName); /* does NULL check */
+ mir_free(pszFileExt); /* does NULL check */
+ return 3;
+}
+
+static INT_PTR ServiceRemoveFileType(WPARAM wParam, LPARAM lParam)
+{
+ char *pszClassName;
+ UNREFERENCED_PARAMETER(wParam);
+
+ if ((char*)lParam == NULL) return 2;
+ pszClassName = MakeFileClassName((char*)lParam);
+ if(pszClassName!= NULL)
+ if(RemoveAssocItem_Worker(pszClassName)) {
+ mir_free(pszClassName);
+ return 0;
+ }
+ mir_free(pszClassName); /* does NULL check */
+ return 3;
+}
+
+static INT_PTR ServiceAddNewUrlType(WPARAM wParam, LPARAM lParam)
+{
+ const URLTYPEDESC *utd = (URLTYPEDESC*)lParam;
+ char *pszClassName;
+ UNREFERENCED_PARAMETER(wParam);
+
+ if(utd->cbSize < sizeof(URLTYPEDESC))
+ return 1;
+ if(utd->pszService == NULL)
+ return 2;
+ if(utd->pszProtoPrefix == NULL || utd->pszProtoPrefix[lstrlenA(utd->pszProtoPrefix)-1]!= ':')
+ return 2;
+
+ pszClassName = MakeUrlClassName(utd->pszProtoPrefix);
+ if(pszClassName!= NULL)
+ if(AddNewAssocItem_Worker(pszClassName, (TYPEDESCHEAD*)utd, NULL, NULL, NULL))
+ /* no need to free pszClassName, as its
+ * ownership got transfered to storage list */
+ return 0;
+ mir_free(pszClassName); /* does NULL check */
+ return 3;
+}
+
+static INT_PTR ServiceRemoveUrlType(WPARAM wParam, LPARAM lParam)
+{
+ char *pszClassName;
+ UNREFERENCED_PARAMETER(wParam);
+
+ if ((char*)lParam == NULL) return 2;
+ pszClassName = MakeUrlClassName((char*)lParam);
+ if(pszClassName!= NULL)
+ if(RemoveAssocItem_Worker(pszClassName)) {
+ mir_free(pszClassName);
+ return 0;
+ }
+ mir_free(pszClassName); /* does NULL check */
+ return 3;
+}
+
+/************************* Open Handler ***************************/
+
+static BOOL InvokeHandler_Worker(const char *pszClassName, const TCHAR *pszParam, INT_PTR *res)
+{
+ void *pvParam;
+ char *pszService;
+ int index;
+ ASSOCDATA *assoc;
+
+ /* find it in list */
+ EnterCriticalSection(&csAssocList);
+ index = FindAssocItem(pszClassName);
+ if(index == -1) {
+ LeaveCriticalSection(&csAssocList);
+ return FALSE;
+ }
+ assoc = &pAssocList[index];
+ /* no service specified? correct registry to use main commandline */
+ if(assoc->pszService == NULL) {
+ EnsureAssocRegistered(assoc);
+ NotifyAssocChange(FALSE);
+ /* try main command line */
+ if ((int)ShellExecute(NULL, NULL, pszParam, NULL, NULL, SW_SHOWNORMAL) >= 32)
+ *res = 0; /* success */
+ return TRUE;
+ }
+ /* get params */
+ pszService = mir_strdup(assoc->pszService);
+ pvParam = t2s(pszParam, assoc->flags&FTDF_UNICODE, FALSE);
+ LeaveCriticalSection(&csAssocList);
+
+ /* call service */
+ if(pszService!= NULL && pvParam!= NULL)
+ *res = CallService(pszService, 0, (LPARAM)pvParam);
+ mir_free(pszService); /* does NULL check */
+ mir_free(pvParam); /* does NULL check */
+ return TRUE;
+}
+
+INT_PTR InvokeFileHandler(const TCHAR *pszFileName)
+{
+ char *pszClassName, *pszFileExt;
+ TCHAR *p;
+ INT_PTR res = CALLSERVICE_NOTFOUND;
+
+ /* find extension */
+ p = (TCHAR*)_tcsrchr(pszFileName, _T('.'));
+ if(p!= NULL) {
+ pszFileExt = t2a(p);
+ if(pszFileExt!= NULL) {
+ /* class name */
+ pszClassName = MakeFileClassName(pszFileExt);
+ if(pszClassName!= NULL)
+ if (!InvokeHandler_Worker(pszClassName, pszFileName, &res)) {
+ /* correct registry on error (no longer in list) */
+ RemoveRegFileExt(pszFileExt, pszClassName);
+ RemoveRegClass(pszClassName);
+ }
+ mir_free(pszClassName); /* does NULL check */
+ mir_free(pszFileExt);
+ }
+ }
+ return res;
+}
+
+INT_PTR InvokeUrlHandler(const TCHAR *pszUrl)
+{
+ char *pszClassName, *pszProtoPrefix, *p;
+ INT_PTR res = CALLSERVICE_NOTFOUND;
+
+ /* find prefix */
+ pszProtoPrefix = t2a(pszUrl);
+ if(pszProtoPrefix!= NULL) {
+ p = strchr(pszProtoPrefix, _T(':'));
+ if(p!= NULL) {
+ *(++p) = 0; /* remove trailing : */
+ /* class name */
+ pszClassName = MakeUrlClassName(pszProtoPrefix);
+ if(pszClassName!= NULL)
+ if (!InvokeHandler_Worker(pszClassName, pszUrl, &res))
+ /* correct registry on error (no longer in list) */
+ RemoveRegClass(pszClassName);
+ mir_free(pszClassName); /* does NULL check */
+ }
+ mir_free(pszProtoPrefix);
+ }
+ return res;
+}
+
+/************************* Options ********************************/
+
+static int CALLBACK ListViewSortDesc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+{
+ int cmp;
+ if (((ASSOCDATA*)lParam1)->pszFileExt!= NULL && ((ASSOCDATA*)lParam2)->pszFileExt!= NULL)
+ cmp = CompareStringA((LCID)lParamSort, 0, ((ASSOCDATA*)lParam1)->pszFileExt, -1, ((ASSOCDATA*)lParam2)->pszFileExt, -1);
+ else if (((ASSOCDATA*)lParam1)->pszFileExt == ((ASSOCDATA*)lParam2)->pszFileExt) /* both NULL */
+ cmp = CompareStringA((LCID)lParamSort, 0, ((ASSOCDATA*)lParam1)->pszClassName, -1, ((ASSOCDATA*)lParam2)->pszClassName, -1);
+ else /* different types, incomparable */
+ cmp = (((ASSOCDATA*)lParam1)->pszFileExt == NULL)?CSTR_LESS_THAN:CSTR_GREATER_THAN;
+ if(cmp == CSTR_EQUAL)
+ cmp = CompareString((LCID)lParamSort, 0, ((ASSOCDATA*)lParam1)->pszDescription, -1, ((ASSOCDATA*)lParam2)->pszDescription, -1);
+ if(cmp!= 0) cmp -= 2; /* maintain CRT conventions */
+ return cmp;
+}
+
+#define M_REFRESH_ICONS (WM_APP+1)
+static INT_PTR CALLBACK AssocListOptDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG:
+ { HWND hwndList;
+ HIMAGELIST himl;
+ LVITEM lvi;
+ ASSOCDATA *assoc;
+ int i;
+ TranslateDialogDefault(hwndDlg);
+ CoInitialize(NULL);
+ hwndList = GetDlgItem(hwndDlg, IDC_ASSOCLIST);
+
+ ListView_SetUnicodeFormat(hwndList, TRUE);
+
+ SendDlgItemMessage(hwndDlg, IDC_HEADERTEXT, WM_SETFONT, SendMessage(GetParent(hwndDlg), PSM_GETBOLDFONT, 0, 0), 0);
+ /* checkboxes won't show up on Win95 without IE3+ or 4.70 (plugin opts uses the same) */
+ ListView_SetExtendedListViewStyle(hwndList, LVS_EX_CHECKBOXES|LVS_EX_FULLROWSELECT|LVS_EX_LABELTIP);
+ /* columns */
+ { LVCOLUMN lvc;
+ lvc.mask = LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM;
+ lvc.pszText = TranslateT("Type");
+ lvc.cx = 170;
+ ListView_InsertColumn(hwndList, lvc.iSubItem = 0, &lvc);
+ lvc.pszText = TranslateT("Description");
+ ListView_InsertColumn(hwndList, lvc.iSubItem = 1, &lvc);
+ }
+ /* create image storage */
+ EnterCriticalSection(&csAssocList);
+ { HDC hdc;
+ hdc = GetDC(hwndList);
+ if(hdc!= NULL) { /* BITSPIXEL is compatible with ILC_COLOR flags */
+ himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), GetDeviceCaps(hdc, BITSPIXEL)|ILC_MASK, nAssocListCount, 0);
+ ReleaseDC(hwndList, hdc);
+ } else himl = NULL;
+ }
+ ListView_SetImageList(hwndList, himl, LVSIL_SMALL); /* autodestroyed */
+ /* enum assoc list */
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_TEXT|LVIF_PARAM|LVIF_IMAGE;
+ for(i = 0;i<nAssocListCount;++i) {
+ assoc = &pAssocList[i];
+ lvi.iItem = 0;
+ lvi.lParam = (LPARAM)CopyAssocItem(assoc);
+ lvi.pszText = GetAssocTypeDesc(assoc);
+ lvi.iImage = ReplaceImageListAssocIcon(himl, assoc, -1);
+ lvi.iItem = ListView_InsertItem(hwndList, &lvi);
+ if(lvi.iItem!= -1) {
+ ListView_SetItemText(hwndList, lvi.iItem, 1, assoc->pszDescription);
+ ListView_SetCheckState(hwndList, lvi.iItem, IsAssocEnabled(assoc) && IsAssocRegistered(assoc));
+ }
+ }
+ /* sort items (before moving to groups) */
+ ListView_SortItems(hwndList, ListViewSortDesc, (LPARAM)CallService(MS_LANGPACK_GETLOCALE, 0, 0));
+ /* groups */
+ if(ListView_EnableGroupView(hwndList, TRUE) == 1) { /* returns 0 on pre WinXP or if commctls6 are disabled */
+ LVGROUP lvg;
+ int iItem;
+ /* dummy item for group */
+ lvi.iItem = ListView_GetItemCount(hwndList)-1;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM|LVIF_IMAGE;
+ lvi.iImage = -1;
+ lvi.lParam = 0;
+ /* insert groups */
+ lvg.cbSize = sizeof(lvg);
+ lvg.mask = LVGF_HEADER|LVGF_GROUPID;
+ lvg.iGroupId = 2;
+ lvg.pszHeader = (WCHAR*)TranslateW(L"URLs on Websites");
+ lvi.iItem = ListView_InsertItem(hwndList, &lvi);
+ if(lvi.iItem!= -1) {
+ ListView_InsertGroup(hwndList, lvi.iItem, &lvg);
+ lvg.iGroupId = 1;
+ lvg.pszHeader = (WCHAR*)TranslateW(L"File Types");
+ iItem = lvi.iItem = ListView_InsertItem(hwndList, &lvi);
+ if(lvi.iItem!= -1)
+ ListView_InsertGroup(hwndList, lvi.iItem, &lvg);
+ else ListView_DeleteItem(hwndList, iItem);
+ }
+ /* move to group */
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM|LVIF_GROUPID;
+ for(lvi.iItem = 0;ListView_GetItem(hwndList, &lvi);++lvi.iItem) {
+ assoc = (ASSOCDATA*)lvi.lParam;
+ if(assoc == NULL) continue; /* groups */
+ lvi.iGroupId = (assoc->pszFileExt == NULL)+1;
+ ListView_SetItem(hwndList, &lvi);
+ }
+ }
+ LeaveCriticalSection(&csAssocList);
+ lvi.iItem = ListView_GetTopIndex(hwndList);
+ ListView_SetItemState(hwndList, lvi.iItem, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
+ ListView_SetColumnWidth(hwndList, 1, LVSCW_AUTOSIZE_USEHEADER); /* size to fit window */
+ /* only while running */
+ CheckDlgButton(hwndDlg, IDC_ONLYWHILERUNNING, (BOOL)DBGetContactSettingByte(NULL, "AssocMgr", "OnlyWhileRunning", SETTING_ONLYWHILERUNNING_DEFAULT));
+ /* autostart */
+ { TCHAR *pszRunCmd;
+ pszRunCmd = MakeRunCommand(TRUE, TRUE);
+ if(pszRunCmd!= NULL) {
+ CheckDlgButton(hwndDlg, IDC_AUTOSTART, IsRegRunEntry(_T("MirandaIM"), pszRunCmd));
+ mir_free(pszRunCmd);
+ }
+ }
+ return TRUE;
+ }
+ case WM_SETTINGCHANGE:
+ case M_REFRESH_ICONS:
+ { LVITEM lvi;
+ HIMAGELIST himl;
+ ASSOCDATA *assoc;
+ HWND hwndList;
+ hwndList = GetDlgItem(hwndDlg, IDC_ASSOCLIST);
+ himl = ListView_GetImageList(hwndList, LVSIL_SMALL);
+ /* enum items */
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM|LVIF_IMAGE;
+ for(lvi.iItem = 0;ListView_GetItem(hwndList, &lvi);++lvi.iItem) {
+ assoc = (ASSOCDATA*)lvi.lParam;
+ if(assoc == NULL) continue; /* groups */
+ lvi.iImage = ReplaceImageListAssocIcon(himl, assoc, lvi.iImage);
+ ListView_SetItem(hwndList, &lvi);
+ }
+ if(lvi.iItem) { /* ListView_Update() blinks */
+ ListView_RedrawItems(hwndList, 0, lvi.iItem-1);
+ UpdateWindow(hwndList);
+ }
+ return TRUE;
+ }
+ case WM_CTLCOLORSTATIC:
+ /* use same text color for header as for group boxes (WinXP+) */
+ if(GetDlgCtrlID((HWND)lParam) == IDC_HEADERTEXT) {
+ HMODULE hUxThemeDLL;
+ HBRUSH hBrush;
+ hUxThemeDLL = LoadLibraryA("UXTHEME"); /* all ascii, already loaded */
+ lParam = (LPARAM)GetDlgItem(hwndDlg, IDC_MISCLABEL);
+ hBrush = (HBRUSH)SendMessage(hwndDlg, msg, wParam, lParam);
+ if(hUxThemeDLL!= NULL) {
+ HTHEME (WINAPI *pfnGetWindowTheme)(HWND);
+ HRESULT (WINAPI *pfnGetThemeColor)(HTHEME, int, int, int, COLORREF*);
+ COLORREF clr;
+ *(PROC*)&pfnGetWindowTheme = GetProcAddress(hUxThemeDLL, "GetWindowTheme");
+ *(PROC*)&pfnGetThemeColor = GetProcAddress(hUxThemeDLL, "GetThemeColor");
+ if(pfnGetWindowTheme!= NULL && pfnGetThemeColor!= NULL) {
+ HTHEME hTheme;
+ hTheme = pfnGetWindowTheme((HWND)lParam);
+ if(hTheme!= NULL)
+ if (!pfnGetThemeColor(hTheme, BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, &clr)) {
+ SetBkMode((HDC)wParam, TRANSPARENT);
+ SetTextColor((HDC)wParam, clr);
+ }
+ }
+ FreeLibrary(hUxThemeDLL);
+ }
+ return (BOOL)hBrush;
+ }
+ break;
+ case WM_NCDESTROY:
+ CoUninitialize();
+ return TRUE;
+ case WM_COMMAND:
+ /* enable apply */
+ PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFYFORMAT:
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, NFR_UNICODE);
+ return TRUE;
+
+ case WM_NOTIFY:
+ { NMHDR *nmhdr = (NMHDR*)lParam;
+ switch(nmhdr->idFrom) {
+ case IDC_ASSOCLIST:
+ switch(nmhdr->code) {
+ case LVN_DELETEITEM: /* also called on WM_DESTROY */
+ { LVITEM lvi;
+ lvi.mask = LVIF_PARAM;
+ lvi.iSubItem = 0;
+ lvi.iItem = ((NMLISTVIEW*)lParam)->iItem;
+ /* free memory */
+ if(ListView_GetItem(nmhdr->hwndFrom, &lvi))
+ mir_free((ASSOCDATA*)lvi.lParam); /* does NULL check */
+ return TRUE;
+ }
+ case LVN_ITEMCHANGED:
+ /* enable apply (not while loading) */
+ if(IsWindowVisible(nmhdr->hwndFrom))
+ PostMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ return TRUE;
+ case LVN_KEYDOWN:
+ /* workaround for WinXP (ListView with groups):
+ * eat keyboard navigation that goes beyond the first item in list
+ * as it would scroll out of scope in this case
+ * bug should not be present using WinVista and higher */
+ switch(((NMLVKEYDOWN*)lParam)->wVKey) {
+ case VK_UP:
+ { LVITEM lvi;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(nmhdr->hwndFrom, -1, LVNI_FOCUSED);
+ lvi.iItem = ListView_GetNextItem(nmhdr->hwndFrom, lvi.iItem, LVNI_ABOVE);
+ if(lvi.iItem!= -1)
+ if(ListView_GetItem(nmhdr->hwndFrom, &lvi))
+ if ((ASSOCDATA*)lvi.lParam == NULL) /* groups */
+ lvi.iItem = -1;
+ if(lvi.iItem == -1) {
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); /* eat it */
+ return TRUE;
+ }
+ break;
+ }
+ case VK_PRIOR:
+ { LVITEM lvi;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ListView_GetNextItem(nmhdr->hwndFrom, -1, LVNI_FOCUSED);
+ lvi.iItem-= ListView_GetCountPerPage(nmhdr->hwndFrom);
+ if(lvi.iItem>= 0)
+ if(ListView_GetItem(nmhdr->hwndFrom, &lvi))
+ if ((ASSOCDATA*)lvi.lParam == NULL) /* groups */
+ lvi.iItem = -1;
+ if(lvi.iItem<0) {
+ ListView_SetItemState(nmhdr->hwndFrom, 0, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); /* eat it */
+ return TRUE;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ case 0:
+ switch(nmhdr->code) {
+ case PSN_APPLY:
+ { HWND hwndList;
+ LVITEM lvi;
+ BOOL fEnabled, fRegFailed = FALSE;
+ ASSOCDATA *assoc;
+
+ /* only while running */
+ DBWriteContactSettingByte(NULL, "AssocMgr", "OnlyWhileRunning", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_ONLYWHILERUNNING)!= 0));
+
+ /* save enabled assoc items */
+ hwndList = GetDlgItem(hwndDlg, IDC_ASSOCLIST);
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_PARAM;
+ EnterCriticalSection(&csAssocList);
+ for(lvi.iItem = 0;ListView_GetItem(hwndList, &lvi);++lvi.iItem) {
+ assoc = (ASSOCDATA*)lvi.lParam;
+ if(assoc == NULL) continue; /* groups */
+ fEnabled = ListView_GetCheckState(hwndList, lvi.iItem);
+ SetAssocEnabled(assoc, fEnabled);
+
+ /* re-register registery keys */
+ if(fEnabled?!EnsureAssocRegistered(assoc):!UnregisterAssoc(assoc)) {
+ char *pszErr;
+ pszErr = GetWinErrorDescription(GetLastError());
+ ShowInfoMessage(NIIF_ERROR, Translate("File Association Error"), Translate("There was an error writing to the registry to modify the file/url associations.\nReason: %s"), (pszErr!= NULL)?pszErr:Translate("Unknown"));
+ mir_free(pszErr); /* does NULL check */
+ fRegFailed = TRUE; /* just show one time */
+ }
+ }
+ LeaveCriticalSection(&csAssocList);
+ NotifyAssocChange(TRUE);
+ PostMessage(hwndDlg, M_REFRESH_ICONS, 0, 0);
+ if(fRegFailed) {
+ }
+ /* autostart */
+ { TCHAR *pszRunCmd;
+ pszRunCmd = MakeRunCommand(TRUE, TRUE);
+ fRegFailed = FALSE;
+ if(pszRunCmd!= NULL) {
+ fEnabled = IsDlgButtonChecked(hwndDlg, IDC_AUTOSTART);
+ if(fEnabled?!AddRegRunEntry(_T("MirandaIM"), pszRunCmd):!RemoveRegRunEntry(_T("MirandaIM"), pszRunCmd)) {
+ char *pszErr;
+ pszErr = GetWinErrorDescription(GetLastError());
+ ShowInfoMessage(NIIF_ERROR, Translate("Autostart Error"), Translate("There was an error writing to the registry to modify the autostart list.\n\nReason: %s"), (pszErr!= NULL)?pszErr:Translate("Unknown"));
+ mir_free(pszErr); /* does NULL check */
+ fRegFailed = TRUE; /* just show one time */
+ }
+ mir_free(pszRunCmd);
+ }
+ }
+ return TRUE;
+ }
+ } /* code */
+ break;
+ } /* idFrom */
+ break;
+ } /* WM_NOTIFY */
+ }
+ return FALSE;
+}
+
+static int AssocListOptInit(WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_ASSOCLIST);
+ odp.position = 900000100; /* network opts = 900000000 */
+ odp.pszGroup = LPGEN("Services"); /* autotranslated */
+ odp.pszTitle = LPGEN("Associations"); /* autotranslated */
+ odp.flags = ODPF_BOLDGROUPS | ODPF_EXPERTONLY;
+ odp.pfnDlgProc = AssocListOptDlgProc;
+ Options_AddPage(wParam, &odp);
+ return 0;
+}
+
+/************************* Misc ***********************************/
+
+void InitAssocList(void)
+{
+ /* Options */
+ INITCOMMONCONTROLSEX icc;
+ icc.dwSize = sizeof(icc);
+ icc.dwICC = ICC_LISTVIEW_CLASSES;
+ InitCommonControlsEx(&icc);
+ hHookOptInit = HookEvent(ME_OPT_INITIALISE, AssocListOptInit);
+
+ /* Assoc List */
+ pAssocList = NULL;
+ nAssocListCount = 0;
+ InitializeCriticalSection(&csAssocList);
+
+ /* Services */
+ hServiceAddFile = CreateServiceFunction(MS_ASSOCMGR_ADDNEWFILETYPE, ServiceAddNewFileType);
+ hServiceRemoveFile = CreateServiceFunction(MS_ASSOCMGR_REMOVEFILETYPE, ServiceRemoveFileType);
+ hServiceAddUrl = CreateServiceFunction(MS_ASSOCMGR_ADDNEWURLTYPE, ServiceAddNewUrlType);
+ hServiceRemoveUrl = CreateServiceFunction(MS_ASSOCMGR_REMOVEURLTYPE, ServiceRemoveUrlType);
+
+ /* Notify Shell */
+ nNotifyTimerID = 0;
+ InitializeCriticalSection(&csNotifyTimer);
+
+ /* register open-with app */
+ { TCHAR *pszAppFileName, *pszIconLoc, *pszRunCmd;
+ pszIconLoc = MakeIconLocation(NULL, 0);
+
+ // miranda32.exe
+ pszAppFileName = MakeAppFileName(TRUE);
+ pszRunCmd = MakeRunCommand(TRUE, FALSE);
+ if(pszAppFileName!= NULL && pszRunCmd!= NULL)
+ AddRegOpenWith(pszAppFileName, FALSE, _T(MIRANDANAME), pszIconLoc, pszRunCmd, NULL, NULL, NULL);
+ mir_free(pszRunCmd); /* does NULL check */
+ mir_free(pszAppFileName); /* does NULL check */
+ // assocmgr.dll
+ pszAppFileName = MakeAppFileName(FALSE);
+ pszRunCmd = MakeRunCommand(FALSE, TRUE);
+ if(pszAppFileName!= NULL && pszRunCmd!= NULL)
+ AddRegOpenWith(pszAppFileName, TRUE, _T(MIRANDANAME), pszIconLoc, pszRunCmd, DDEFILECMD, DDEAPP, DDETOPIC);
+ mir_free(pszRunCmd); /* does NULL check */
+ mir_free(pszAppFileName); /* does NULL check */
+
+ mir_free(pszIconLoc); /* does NULL check */
+ }
+
+ /* default items */
+ { FILETYPEDESC ftd;
+ ftd.cbSize = sizeof(FILETYPEDESC);
+ ftd.pszFileExt = ".dat";
+ ftd.pszMimeType = NULL;
+ ftd.ptszDescription = TranslateT("Miranda IM Database");
+ ftd.hInstance = hInst;
+ ftd.nIconResID = IDI_MIRANDAFILE;
+ ftd.ptszVerbDesc = NULL;
+ ftd.pszService = NULL;
+ ftd.flags = FTDF_DEFAULTDISABLED|FTDF_TCHAR;
+ ServiceAddNewFileType(0, (LPARAM)&ftd);
+ }
+}
+
+void UninitAssocList(void)
+{
+ BYTE fOnlyWhileRunning;
+ ASSOCDATA *assoc;
+ int i;
+
+ /* Options */
+ UnhookEvent(hHookOptInit);
+
+ /* Services */
+ DestroyServiceFunction(hServiceAddFile);
+ DestroyServiceFunction(hServiceRemoveFile);
+ DestroyServiceFunction(hServiceAddUrl);
+ DestroyServiceFunction(hServiceRemoveUrl);
+
+ /* Assoc List */
+ fOnlyWhileRunning = DBGetContactSettingByte(NULL, "AssocMgr", "OnlyWhileRunning", SETTING_ONLYWHILERUNNING_DEFAULT);
+ for(i = 0;i<nAssocListCount;++i) {
+ assoc = &pAssocList[i];
+
+ /* remove registry keys */
+ if(fOnlyWhileRunning)
+ UnregisterAssoc(assoc);
+
+ mir_free(assoc->pszClassName);
+ mir_free(assoc->pszDescription);
+ mir_free(assoc->pszService);
+ mir_free(assoc->pszFileExt); /* does NULL check */
+ mir_free(assoc->pszVerbDesc); /* does NULL check */
+ mir_free(assoc->pszMimeType); /* does NULL check */
+ }
+ mir_free(pAssocList);
+ DeleteCriticalSection(&csAssocList);
+
+ /* Notify Shell */
+ if(fOnlyWhileRunning && nAssocListCount)
+ NotifyAssocChange(TRUE);
+ DeleteCriticalSection(&csNotifyTimer);
+
+ /* unregister open-with app */
+ if(fOnlyWhileRunning) {
+ TCHAR *pszAppFileName;
+ // miranda32.exe
+ pszAppFileName = MakeAppFileName(TRUE);
+ if(pszAppFileName!= NULL)
+ RemoveRegOpenWith(pszAppFileName);
+ pszAppFileName = MakeAppFileName(FALSE);
+ // assocmgr.dll
+ if(pszAppFileName!= NULL)
+ RemoveRegOpenWith(pszAppFileName);
+ mir_free(pszAppFileName); /* does NULL check */
+ }
+} \ No newline at end of file
diff --git a/plugins/AssocMgr/src/assoclist.h b/plugins/AssocMgr/src/assoclist.h
new file mode 100644
index 0000000000..5e8806b828
--- /dev/null
+++ b/plugins/AssocMgr/src/assoclist.h
@@ -0,0 +1,33 @@
+/*
+
+'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.
+*/
+
+/* Assoc Enabled */
+void CleanupAssocEnabledSettings(void);
+/* Mime Reg */
+void CleanupMimeTypeAddedSettings(void);
+/* Assoc List Utils */
+BOOL IsRegisteredAssocItem(const char *pszClassName);
+/* Open Handler */
+INT_PTR InvokeFileHandler(const TCHAR *pszFileName);
+INT_PTR InvokeUrlHandler(const TCHAR *pszUrl);
+/* Misc */
+void InitAssocList(void);
+void UninitAssocList(void);
diff --git a/plugins/AssocMgr/src/common.h b/plugins/AssocMgr/src/common.h
new file mode 100644
index 0000000000..9f6c0b7505
--- /dev/null
+++ b/plugins/AssocMgr/src/common.h
@@ -0,0 +1,57 @@
+/*
+
+'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.
+*/
+
+#define _CRT_SECURE_NO_WARNINGS
+#define _WIN32_WINNT 0x0600
+#define __RPCASYNC_H__ /* VC6 has a mistake in there (warning) */
+#include <windows.h>
+#pragma warning(disable:4201) /* nonstandard extension used : nameless struct/union */
+#pragma warning(disable:4115) /* V6 has a mistake in there (warning) */
+#include <shlobj.h> /* for SHChangeNotify() */
+#pragma warning(default:4115)
+#include <commctrl.h>
+#pragma warning(default:4201)
+#include <tchar.h>
+#include <stdio.h> /* for mir_snprintf() */
+#include <Vsstyle.h>
+#include <Vssym32.h>
+#include <malloc.h>
+
+#define MIRANDA_VER 0x0A00
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_database.h>
+#include <m_utils.h>
+#include <m_langpack.h>
+#include <m_plugins.h>
+#include <m_options.h>
+#include <m_skin.h>
+#include <m_clist.h>
+#include "win2k.h"
+
+#define ASSOCMGR_NOHELPERFUNCTIONS
+#include "m_assocmgr.h"
+#include "utils.h"
+#include "reg.h"
+#include "assoclist.h"
+#include "dde.h"
+#include "test.h"
+#include "resource.h"
diff --git a/plugins/AssocMgr/src/dde.cpp b/plugins/AssocMgr/src/dde.cpp
new file mode 100644
index 0000000000..a8328b4d34
--- /dev/null
+++ b/plugins/AssocMgr/src/dde.cpp
@@ -0,0 +1,295 @@
+/*
+
+'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"
+
+/* Conversation */
+extern HINSTANCE hInst;
+static HWND hwndDdeMsg;
+/* Misc */
+static HANDLE hHookModulesLoaded,hHookPreShutdown;
+
+/************************* Open Handler ***************************/
+
+// pszFilePath needs to be allocated using mir_alloc()
+static void __stdcall FileActionAsync(void *param)
+{
+ TCHAR *pszFilePath = (TCHAR*)param;
+ /* invoke main handler */
+ switch(InvokeFileHandler(pszFilePath)) { /* pszFilePath is always a long path name */
+ case 0: /* success */ break;
+ case CALLSERVICE_NOTFOUND:
+ ShowInfoMessage(NIIF_ERROR,Translate("Miranda IM could not open file"),Translate("Miranda IM was not able to open \""TCHAR_STR_PARAM"\".\n\nThere is no registered handler for this file type."),pszFilePath);
+ break;
+ default:
+ ShowInfoMessage(NIIF_ERROR,Translate("Miranda IM could not open file"),Translate("Miranda IM was not able to open \""TCHAR_STR_PARAM"\".\n\nThe file could not be processed."),pszFilePath);
+ }
+ mir_free(pszFilePath); /* async param */
+}
+
+// pszUrl needs to be allocated using mir_alloc()
+static void __stdcall UrlActionAsync(void *param)
+{
+ TCHAR *pszUrl = (TCHAR*)param;
+ /* invoke main handler */
+ switch(InvokeUrlHandler(pszUrl)) {
+ case 0: /* success */ break;
+ case CALLSERVICE_NOTFOUND:
+ ShowInfoMessage(NIIF_ERROR,Translate("Miranda IM could not open URL"),Translate("Miranda IM was not able to open \""TCHAR_STR_PARAM"\".\n\nThere is no registered handler for this URL type."),pszUrl);
+ break;
+ default:
+ ShowInfoMessage(NIIF_ERROR,Translate("Miranda IM could not open URL"),Translate("Miranda IM was not able to open \""TCHAR_STR_PARAM"\".\n\nThe given URL is invalid and can not be parsed."),pszUrl);
+ }
+ mir_free(pszUrl); /* async param */
+}
+
+/************************* Conversation ***************************/
+
+#define DDEMESSAGETIMEOUT 30000
+#define WNDCLASS_DDEMSGWINDOW _T("MirandaDdeMsgWindow")
+
+// returned pointer points into a substring of ppszString
+// returns an empty string if the string does not have enough arguments
+static TCHAR* GetExecuteParam(TCHAR **ppszString)
+{
+ TCHAR *pszParam,*p;
+ BOOL fQuoted;
+
+ fQuoted=(**ppszString==_T('"'));
+ pszParam=*ppszString;
+ if(fQuoted) pszParam++;
+ p=_tcschr(pszParam,(TCHAR)(fQuoted?_T('"'):_T(',')));
+ if(p!=NULL) {
+ *(p++)=0;
+ if(fQuoted && *p==_T(',')) p++;
+ } else p=&pszParam[lstrlen(pszParam)];
+ *ppszString=p;
+ return pszParam;
+}
+
+static LRESULT CALLBACK DdeMessageWindow(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+ switch(msg) {
+ case WM_DDE_INITIATE:
+ { ATOM hSzApp,hSzTopic;
+ hSzApp=LOWORD(lParam); /* no UnpackDDElParam() here */
+ hSzTopic=HIWORD(lParam);
+ if ((hSzApp==GlobalFindAtom(DDEAPP) && hSzTopic==GlobalFindAtom(DDETOPIC)) || !hSzApp) {
+ hSzApp=GlobalAddAtom(DDEAPP);
+ hSzTopic=GlobalAddAtom(DDETOPIC);
+ if(hSzApp && hSzTopic)
+ /* PackDDElParam() only for posted msgs */
+ SendMessage((HWND)wParam,WM_DDE_ACK,(WPARAM)hwnd,MAKELPARAM(hSzApp,hSzTopic));
+ if(hSzApp) GlobalDeleteAtom(hSzApp);
+ if(hSzTopic) GlobalDeleteAtom(hSzTopic);
+ }
+ return 0;
+ }
+ case WM_DDE_EXECUTE: /* posted message */
+ { HGLOBAL hCommand;
+ TCHAR *pszCommand;
+ DDEACK ack;
+ ZeroMemory(&ack,sizeof(ack));
+ if(UnpackDDElParam(msg,lParam,NULL,(PUINT_PTR)&hCommand)) {
+
+ /* ANSI execute command can't happen for shell */
+ if(IsWindowUnicode((HWND)wParam)) {
+
+ pszCommand = (TCHAR*)GlobalLock(hCommand);
+ if(pszCommand!=NULL) {
+ TCHAR *pszAction,*pszArg;
+ pszAction=GetExecuteParam(&pszCommand);
+ pszArg=mir_tstrdup(GetExecuteParam(&pszCommand));
+ if(pszArg!=NULL) {
+ /* we are inside miranda here, we make it async so the shell does
+ * not timeout regardless what the plugins try to do. */
+ if (!lstrcmpi(pszAction,_T("file")))
+ ack.fAck=(short)(CallFunctionAsync(FileActionAsync,pszArg)!=0);
+ else if (!lstrcmpi(pszAction,_T("url")))
+ ack.fAck=(short)(CallFunctionAsync(UrlActionAsync,pszArg)!=0);
+ if (!ack.fAck) mir_free(pszArg); /* otherwise freed by asyncproc */
+ }
+ GlobalUnlock(hCommand);
+ }
+
+ }
+
+ lParam=ReuseDDElParam(lParam,msg,WM_DDE_ACK,*(PUINT)&ack,(UINT)hCommand);
+ if (!PostMessage((HWND)wParam,WM_DDE_ACK,(WPARAM)hwnd,lParam)) {
+ GlobalFree(hCommand);
+ FreeDDElParam(WM_DDE_ACK,lParam);
+ }
+ }
+ return 0;
+ }
+ case WM_DDE_TERMINATE:
+ PostMessage((HWND)wParam,msg,(WPARAM)hwnd,0); /* ack reply */
+ return 0;
+ case WM_DDE_REQUEST:
+ case WM_DDE_ADVISE:
+ case WM_DDE_UNADVISE:
+ case WM_DDE_POKE:
+ /* fail safely for those to avoid memory leak in lParam */
+ { ATOM hSzItem;
+ DDEACK ack;
+ ZeroMemory(&ack,sizeof(ack));
+ if(UnpackDDElParam(msg,lParam,NULL,(PUINT_PTR)&hSzItem)) {
+ lParam=ReuseDDElParam(lParam,msg,WM_DDE_ACK,*(PUINT)&ack,(UINT)hSzItem);
+ if (!PostMessage((HWND)wParam,WM_DDE_ACK,(WPARAM)hwnd,lParam)) {
+ if(hSzItem) GlobalDeleteAtom(hSzItem);
+ FreeDDElParam(WM_DDE_ACK,lParam);
+ }
+ }
+ return 0;
+ }
+ }
+ return DefWindowProc(hwnd,msg,wParam,lParam);
+}
+
+// CloseHandle() the return value
+static HANDLE StartupMainProcess(TCHAR *pszDatabasePath)
+{
+ TCHAR *p,szPath[MAX_PATH];
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si;
+
+ /* we are inside RunDll32 here */
+ if (!GetModuleFileName(hInst,szPath,SIZEOF(szPath))) return NULL;
+ p=_tcsrchr(szPath,_T('\\'));
+ if(p!=NULL) { *p=0; p=_tcsrchr(szPath,_T('\\')); }
+ if(p==NULL) return NULL;
+ lstrcpy(++p,_T("miranda32.exe"));
+
+ /* inherit startup data from RunDll32 process */
+ GetStartupInfo(&si);
+ if (!CreateProcess(szPath,pszDatabasePath,NULL,NULL,TRUE,GetPriorityClass(GetCurrentProcess()),NULL,NULL,&si,&pi))
+ return NULL;
+ CloseHandle(pi.hThread);
+ return pi.hProcess;
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// entry point for RunDll32, this is also WaitForDDEW
+__declspec(dllexport) void CALLBACK WaitForDDE(HWND hwnd,HINSTANCE hinstExe,TCHAR *pszCmdLine,int nCmdShow)
+{
+ HANDLE pHandles[2];
+ DWORD dwTick;
+ UNREFERENCED_PARAMETER(hinstExe);
+ UNREFERENCED_PARAMETER(nCmdShow); /* obeys nCmdShow using GetStartupInfo() */
+ UNREFERENCED_PARAMETER(hwnd);
+
+ /* wait for dde window to be available (avoiding race condition) */
+ pHandles[0]=CreateEvent(NULL,TRUE,FALSE,WNDCLASS_DDEMSGWINDOW);
+ if(pHandles[0]!=NULL) {
+ pHandles[1]=StartupMainProcess(pszCmdLine); /* obeys nCmdShow using GetStartupInfo() */
+ if(pHandles[1]!=NULL) {
+ dwTick=GetTickCount();
+ /* either process terminated or dde window created */
+ if(WaitForMultipleObjects(SIZEOF(pHandles),pHandles,FALSE,DDEMESSAGETIMEOUT)==WAIT_OBJECT_0) {
+ dwTick=GetTickCount()-dwTick;
+ if(dwTick<DDEMESSAGETIMEOUT)
+ WaitForInputIdle(pHandles[1],DDEMESSAGETIMEOUT-dwTick);
+ }
+ CloseHandle(pHandles[1]);
+ }
+ CloseHandle(pHandles[0]);
+ }
+ /* shell called WaitForInputIdle() on us to detect when dde is ready,
+ * we are ready now: exit helper process */
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/************************* Misc ***********************************/
+
+static int DdePreShutdown(WPARAM wParam,LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ /* dde needs to be stopped before any plugins are unloaded */
+ if(hwndDdeMsg!=NULL) DestroyWindow(hwndDdeMsg);
+ UnregisterClass(WNDCLASS_DDEMSGWINDOW,hInst);
+ return 0;
+}
+
+static int DdeModulesLoaded2(WPARAM wParam,LPARAM lParam)
+{
+ WNDCLASS wcl;
+ HANDLE hEvent;
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+
+ /* create message window */
+ wcl.lpfnWndProc=DdeMessageWindow;
+ wcl.cbClsExtra=0;
+ wcl.cbWndExtra=0;
+ wcl.hInstance=hInst;
+ wcl.hCursor=NULL;
+ wcl.lpszClassName=WNDCLASS_DDEMSGWINDOW;
+ wcl.hbrBackground=NULL;
+ wcl.hIcon=NULL;
+ wcl.lpszMenuName=NULL;
+ wcl.style=0;
+ RegisterClass(&wcl);
+ /* Note: use of HWND_MESSAGE does not fit for DDE as the window must be a top-level one */
+ hwndDdeMsg=CreateWindow(WNDCLASS_DDEMSGWINDOW,NULL,0,0,0,0,0,NULL,NULL,hInst,NULL);
+
+ /* make known dde startup code is passed */
+ hEvent=OpenEvent(EVENT_MODIFY_STATE,FALSE,WNDCLASS_DDEMSGWINDOW);
+ if(hEvent!=NULL) {
+ SetEvent(hEvent);
+ CloseHandle(hEvent);
+ }
+
+ CleanupRegTreeBackupSettings();
+ CleanupMimeTypeAddedSettings();
+ CleanupAssocEnabledSettings();
+ return 0;
+}
+
+static int DdeModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ /* dde needs to be loaded after all the other plugins got loaded,
+ * hook again to get the latest position in chain */
+ UnhookEvent(hHookModulesLoaded);
+ hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,DdeModulesLoaded2);
+ return 0;
+}
+
+void InitDde(void)
+{
+ hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,DdeModulesLoaded);
+ hHookPreShutdown=HookEvent(ME_SYSTEM_PRESHUTDOWN,DdePreShutdown);
+}
+
+void UninitDde(void)
+{
+ UnhookEvent(hHookModulesLoaded);
+ UnhookEvent(hHookPreShutdown);
+}
+
diff --git a/plugins/AssocMgr/src/dde.h b/plugins/AssocMgr/src/dde.h
new file mode 100644
index 0000000000..9ca8a340ae
--- /dev/null
+++ b/plugins/AssocMgr/src/dde.h
@@ -0,0 +1,30 @@
+/*
+
+'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.
+*/
+
+/* Conversation */
+#define DDEAPP _T("Miranda")
+#define DDETOPIC _T("OpenAssoc")
+#define DDEURLCMD _T("url,\"%l\"")
+#define DDEFILECMD _T("file,\"%1\"")
+
+/* Misc */
+void InitDde(void);
+void UninitDde(void);
diff --git a/plugins/AssocMgr/src/main.cpp b/plugins/AssocMgr/src/main.cpp
new file mode 100644
index 0000000000..e6095c25c4
--- /dev/null
+++ b/plugins/AssocMgr/src/main.cpp
@@ -0,0 +1,121 @@
+/*
+
+'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"
+#include "version.h"
+
+HINSTANCE hInst;
+static HANDLE hHookModulesLoaded;
+int hLangpack;
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __AUTHOREMAIL,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {52685CD7-0EC7-44c1-A1A6-381612418202}
+ {0x52685cd7,0xec7,0x44c1,{0xa1,0xa6,0x38,0x16,0x12,0x41,0x82,0x2}}
+};
+static const MUUID interfaces[]={MIID_ASSOCMGR,MIID_AUTORUN,MIID_LAST};
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+static void InstallFile(const TCHAR *pszFileName,const TCHAR *pszDestSubDir)
+{
+ TCHAR szFileFrom[MAX_PATH+1],szFileTo[MAX_PATH+1],*p;
+ HANDLE hFile;
+
+ if (!GetModuleFileName(hInst,szFileFrom,SIZEOF(szFileFrom)-lstrlen(pszFileName)))
+ return;
+ p=_tcsrchr(szFileFrom,_T('\\'));
+ if(p!=NULL) *(++p)=0;
+ lstrcat(szFileFrom,pszFileName); /* buffer safe */
+
+ hFile=CreateFile(szFileFrom,0,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
+ if(hFile==INVALID_HANDLE_VALUE) return;
+ CloseHandle(hFile);
+
+ if (!GetModuleFileNameWorkaround(NULL,szFileTo,SIZEOF(szFileTo)-lstrlen(pszDestSubDir)-lstrlen(pszFileName)))
+ return;
+ p=_tcsrchr(szFileTo,_T('\\'));
+ if(p!=NULL) *(++p)=0;
+ lstrcat(szFileTo,pszDestSubDir); /* buffer safe */
+ CreateDirectory(szFileTo,NULL);
+ lstrcat(szFileTo,pszFileName); /* buffer safe */
+
+ if (!MoveFile(szFileFrom,szFileTo) && GetLastError()==ERROR_ALREADY_EXISTS) {
+ DeleteFile(szFileTo);
+ MoveFile(szFileFrom,szFileTo);
+ }
+}
+
+static int AssocMgrModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ if(ServiceExists("DBEditorpp/RegisterSingleModule"))
+ CallService("DBEditorpp/RegisterSingleModule",(WPARAM)"AssocMgr",0);
+ InitTest();
+ return 0;
+}
+
+extern "C" __declspec(dllexport) const PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+extern "C" __declspec(dllexport) int Load(void)
+{
+ mir_getLP(&pluginInfo);
+
+ InitAssocList();
+ InitDde();
+
+ /* installation */
+ InstallFile(_T("AssocMgr-Readme.txt"),_T("Docs\\"));
+ InstallFile(_T("AssocMgr-License.txt"),_T("Docs\\"));
+ InstallFile(_T("AssocMgr-SDK.zip"),_T("Docs\\"));
+ hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,AssocMgrModulesLoaded);
+ return 0;
+}
+
+extern "C" __declspec(dllexport) int Unload(void)
+{
+ UninitTest();
+ UninitDde();
+ UninitAssocList();
+ UnhookEvent(hHookModulesLoaded);
+ return 0;
+} \ No newline at end of file
diff --git a/plugins/AssocMgr/src/reg.cpp b/plugins/AssocMgr/src/reg.cpp
new file mode 100644
index 0000000000..92451a52d3
--- /dev/null
+++ b/plugins/AssocMgr/src/reg.cpp
@@ -0,0 +1,1295 @@
+/*
+
+'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(char *pszClassName, 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(szExe)) {
+ MoveMemory(szExe,szExe+1,(len+1)*sizeof(TCHAR));
+ szExe[len+2]=szExe[0]=_T('\"');
+ szExe[len+3]=0;
+ }
+ }
+ pszRunCmd=(TCHAR*)mir_alloc(cch*sizeof(TCHAR));
+ if(pszRunCmd!=NULL)
+ mir_sntprintf(pszRunCmd,cch,pszFmt,szExe,szDbFile);
+ }
+ return pszRunCmd;
+}
+
+static BOOL IsValidRunCommand(const TCHAR *pszRunCmd)
+{
+ TCHAR *buf,*pexe,*pargs;
+ TCHAR szFullExe[MAX_PATH],*pszFilePart;
+ buf=lstrcpy((TCHAR*)_alloca((lstrlen(pszRunCmd)+1)*sizeof(TCHAR)),pszRunCmd);
+ /* split into executable path and arguments */
+ if(buf[0]==_T('\"')) {
+ pargs=_tcschr(&buf[1],_T('\"'));
+ if(pargs!=NULL) *(pargs++)=0;
+ pexe=&buf[1];
+ if (*pargs==_T(' ')) ++pargs;
+ } else {
+ pargs=_tcschr(buf,_T(' '));
+ if(pargs!=NULL) *pargs=0;
+ pexe=buf;
+ }
+ if(SearchPath(NULL,pexe,_T(".exe"),SIZEOF(szFullExe),szFullExe,&pszFilePart)) {
+ if(pszFilePart!=NULL)
+ if (!lstrcmpi(pszFilePart,_T("rundll32.exe")) || !lstrcmpi(pszFilePart,_T("rundll.exe"))) {
+ /* split into dll path and arguments */
+ if(pargs[0]==_T('\"')) {
+ ++pargs;
+ pexe=_tcschr(&pargs[1],_T('\"'));
+ if(pexe!=NULL) *pexe=0;
+ } else {
+ pexe=_tcschr(pargs,_T(','));
+ if(pexe!=NULL) *pexe=0;
+ }
+ return SearchPath(NULL,pargs,_T(".dll"),0,NULL,NULL)!=0;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// mir_free() the return value
+TCHAR *MakeIconLocation(HMODULE hModule,WORD nIconResID)
+{
+ TCHAR szModule[MAX_PATH],*pszIconLoc=NULL;
+ int cch;
+ if ((cch=GetModuleFileNameWorkaround(hModule,szModule,SIZEOF(szModule)))!=0) {
+ pszIconLoc=(TCHAR*)mir_alloc((cch+=8)*sizeof(TCHAR));
+ if(pszIconLoc!=NULL)
+ wsprintf(pszIconLoc,_T("%s,%i"),szModule,-(int)nIconResID); /* id may be 0, buffer safe */
+ }
+ return pszIconLoc;
+}
+
+// mir_free() the return value
+TCHAR *MakeAppFileName(BOOL fMirExe)
+{
+ extern HINSTANCE hInst;
+ TCHAR szExe[MAX_PATH],*psz;
+ if(GetModuleFileNameWorkaround(fMirExe?NULL:hInst,szExe,SIZEOF(szExe))) {
+ psz=_tcsrchr(szExe,_T('\\'));
+ if(psz!=NULL) ++psz;
+ else psz=szExe;
+ return mir_tstrdup(psz);
+ }
+ return NULL;
+}
+
+/************************* Helpers ********************************/
+
+static LONG DeleteRegSubTree(HKEY hKey,const TCHAR *pszSubKey)
+{
+ LONG res;
+ DWORD nMaxSubKeyLen,cchSubKey;
+ TCHAR *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=(TCHAR*)mir_alloc((nMaxSubKeyLen+1)*sizeof(TCHAR));
+ 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 TCHAR *pszSubKey,const TCHAR *pszVal)
+{
+ HKEY hSubKey;
+ LONG res;
+ 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,(lstrlen(pszVal)+1)*sizeof(TCHAR));
+ RegCloseKey(hSubKey);
+ }
+ return res;
+}
+
+// hKey must have been opened with KEY_SET_VALUE access right
+static void SetRegStrPrefixValue(HKEY hKey,const TCHAR *pszValPrefix,const TCHAR *pszVal)
+{
+ TCHAR *pszStr;
+ DWORD dwSize;
+ dwSize=(lstrlen(pszVal)+lstrlen(pszValPrefix)+1)*sizeof(TCHAR);
+ pszStr=(TCHAR*)mir_alloc(dwSize);
+ if(pszStr==NULL) return;
+ lstrcat(lstrcpy(pszStr,pszValPrefix),pszVal); /* buffer safe */
+ RegSetValueEx(hKey,NULL,0,REG_SZ,(BYTE*)pszStr,dwSize);
+ mir_free(pszStr);
+}
+
+// hKey must have been opened with KEY_QUERY_VALUE access right
+// mir_free() the return value
+static TCHAR *GetRegStrValue(HKEY hKey,const TCHAR *pszValName)
+{
+ TCHAR *pszVal,*pszVal2;
+ DWORD dwSize,dwType;
+ /* get size */
+ if (!RegQueryValueEx(hKey,pszValName,NULL,NULL,NULL,&dwSize) && dwSize>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)) {
+
+ 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)
+{
+ 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,&param);
+ 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<nSettingsCount;++i) {
+ pszSuffix=&ppszSettings[i][nDbPrefixLen];
+ /* key hierachy */
+ pkeys=lstrcpyA((char*)_alloca(lstrlenA(pszSuffix)+1),pszSuffix);
+ pnext=pkeys;
+ while((pnext=strchr(pnext+1,_T('\\')))!=NULL) pslash=pnext;
+ if(pslash!=NULL) {
+ /* create subkey */
+ *(pslash++)=0;
+ hSubKey=hKey;
+ if(pslash!=pkeys+1)
+ if ((res=RegCreateKeyExA(hKey,pkeys,0,NULL,0,KEY_SET_VALUE,NULL,&hSubKey,NULL))!=ERROR_SUCCESS)
+ break;
+ pszValName=pslash;
+ /* read data */
+ if(ReadDbBackupData(ppszSettings[i],&dwType,&pData,&cbData)) {
+ /* set value */
+ if (!(dwType&REGF_ANSI)) {
+ pwszValName=a2u(pszValName,FALSE);
+ if(pwszValName!=NULL) res=RegSetValueExW(hSubKey,pwszValName,0,dwType,pData,cbData);
+ else res=ERROR_NOT_ENOUGH_MEMORY;
+ mir_free(pwszValName); /* does NULL check */
+ } else res=RegSetValueExA(hSubKey,pszValName,0,dwType&~REGF_ANSI,pData,cbData);
+ mir_free(pData);
+ } else res=ERROR_INVALID_DATA;
+ if(res) break;
+ DBDeleteContactSetting(NULL,"AssocMgr",ppszSettings[i]);
+ if(hSubKey!=hKey) RegCloseKey(hSubKey);
+ }
+ mir_free(ppszSettings[i]);
+ }
+ mir_free(ppszSettings);
+ }
+ mir_free(pszPrefixWithSubKey);
+ }
+ return res;
+}
+
+static void DeleteRegTreeBackup(const char *pszSubKey,const char *pszDbPrefix)
+{
+ char **ppszSettings,*pszPrefixWithSubKey;
+ int nSettingsCount,i;
+
+ pszPrefixWithSubKey=(char*)mir_alloc(lstrlenA(pszDbPrefix)+lstrlenA(pszSubKey)+2);
+ if(pszPrefixWithSubKey==NULL) return;
+ lstrcatA(lstrcatA(lstrcpyA(pszPrefixWithSubKey,pszDbPrefix),pszSubKey),"\\"); /* buffer safe */
+ if(pszPrefixWithSubKey!=NULL) {
+ if(EnumDbPrefixSettings("AssocMgr",pszPrefixWithSubKey,&ppszSettings,&nSettingsCount)) {
+ for(i=0;i<nSettingsCount;++i) {
+ DBDeleteContactSetting(NULL,"AssocMgr",ppszSettings[i]);
+ mir_free(ppszSettings[i]);
+ }
+ mir_free(ppszSettings);
+ }
+ mir_free(pszPrefixWithSubKey);
+ }
+}
+
+void CleanupRegTreeBackupSettings(void)
+{
+ int nSettingsCount;
+ char **ppszSettings;
+ char *pszClassName,*pszBuf,*pszFileExt;
+ int i,j;
+
+ /* delete old bak_* settings and try to restore backups */
+ if(EnumDbPrefixSettings("AssocMgr","bak_",&ppszSettings,&nSettingsCount)) {
+ for(i=0;i<nSettingsCount;++i) {
+ pszClassName=&ppszSettings[i][4];
+ pszBuf=strchr(pszClassName,'\\');
+ if(pszBuf!=NULL) {
+ *pszBuf='\0';
+ /* remove others in list with same class name */
+ for(j=i;j<nSettingsCount;++j) {
+ pszBuf=strchr(&ppszSettings[j][4],'\\');
+ if(pszBuf!=NULL) *pszBuf='\0';
+ if(lstrcmpA(pszClassName,&ppszSettings[j][4])) continue;
+ MoveMemory(&ppszSettings[j],&ppszSettings[j+1],((--nSettingsCount)-j)*sizeof(TCHAR*));
+ --j; /* reiterate current index */
+ }
+ /* no longer registered? */
+ if (!IsRegisteredAssocItem(pszClassName)) {
+ if(IsFileClassName(pszClassName,&pszFileExt))
+ RemoveRegFileExt(pszFileExt,pszClassName);
+ else RemoveRegClass(pszClassName);
+ }
+ }
+ mir_free(ppszSettings[i]);
+ }
+ mir_free(ppszSettings);
+ }
+}
+
+/************************* Opera Support **************************/
+
+/*
+ * These are helpers accessing the Opera settings file.
+ * Should work with Opera 6 up to 9.10 (current)
+ */
+
+static BOOL Opera6_GetIniFilePath(TCHAR *szIniFile)
+{
+ HKEY hExeKey;
+ TCHAR szPath[MAX_PATH],*p;
+ BOOL fSuccess=FALSE;
+ DWORD len;
+
+ /* Info: http://opera-info.de/forum/thread.php?threadid=2905 */
+ /* app path */
+ if (!RegOpenKeyExA(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Netscape.exe",0,KEY_QUERY_VALUE,&hExeKey)) {
+ /* exe name */
+ p=GetRegStrValue(hExeKey,NULL);
+ if(p!=NULL && _tcsstr(p,_T("Opera.exe"))!=NULL) {
+ /* path */
+ mir_free(p);
+ p=GetRegStrValue(hExeKey,_T("Path"));
+ len=lstrlen(p);
+ if(p[len-1]==_T('\\')) p[len-1]=0;
+ fSuccess=(p!=NULL && ExpandEnvironmentStrings(p,szPath,MAX_PATH));
+ }
+ mir_free(p); /* does NULL check */
+ RegCloseKey(hExeKey);
+ }
+ if(fSuccess) {
+ TCHAR szFileBuf[MAX_PATH+34];
+ /* operadef6.ini */
+ lstrcat(lstrcpy(szFileBuf,szPath),_T("\\operadef6.ini")); /* buffer safe */
+ /* If enabled Opera will use Windows profiles to store individual user settings */
+ if(GetPrivateProfileInt(_T("System"),_T("Multi User"),0,szFileBuf)==1) {
+ p=_tcsrchr(szPath,'\\');
+ lstrcpy(szFileBuf,_T("%APPDATA%\\Opera")); /* buffer safe */
+ if(p!=NULL) lstrcat(szFileBuf,p); /* buffer safe */
+ } else lstrcpy(szFileBuf,szPath);
+ /* opera6.ini */
+ lstrcat(szFileBuf,_T("\\profile\\opera6.ini")); /* buffer safe */
+ fSuccess=ExpandEnvironmentStrings(szFileBuf,szIniFile,MAX_PATH)!=0;
+ }
+ /* check file existstance */
+ if(fSuccess) {
+ HANDLE hFile;
+ hFile=CreateFile(szIniFile,0,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
+ if(hFile==INVALID_HANDLE_VALUE) fSuccess=FALSE;
+ else CloseHandle(hFile);
+ }
+ return fSuccess;
+}
+
+// pszProtoPrefix is expected to have no trailing :
+static void Opera6_AddTrustedProto(const char *pszProtoPrefix)
+{
+ TCHAR szIniFile[MAX_PATH],*ptszProtoPrefix;
+ if(Opera6_GetIniFilePath(szIniFile)) {
+ /* trusted protocols */
+ ptszProtoPrefix=a2t(pszProtoPrefix);
+ if(ptszProtoPrefix!=NULL) {
+ WritePrivateProfileString(_T("Trusted Protocols"),ptszProtoPrefix,_T("1,0,"),szIniFile);
+ mir_free(ptszProtoPrefix);
+ }
+ }
+}
+
+static void Opera6_AddKnownMimeType(const char *pszMimeType,const char *pszFileExt,const TCHAR *pszDescription)
+{
+ TCHAR szIniFile[MAX_PATH],szVal[256],*ptszMimeType;
+ if(Opera6_GetIniFilePath(szIniFile)) {
+ /* section version */
+ if(GetPrivateProfileInt(_T("File Types Section Info"),_T("Version"),0,szIniFile)==2) {
+ ptszMimeType=a2t(pszMimeType);
+ if(ptszMimeType!=NULL) {
+ /* file type */
+ mir_sntprintf(szVal,SIZEOF(szVal),_T("4,,,,%.15hs,|%.128s (%.16hs)"),&pszFileExt[1],pszDescription,pszFileExt);
+ WritePrivateProfileString(_T("File Types"),ptszMimeType,szVal,szIniFile);
+ /* file type extension */
+ WritePrivateProfileString(_T("File Types Extension"),ptszMimeType,_T(",0"),szIniFile);
+ mir_free(ptszMimeType);
+ }
+ }
+ }
+}
+
+/************************* Class **********************************/
+
+/*
+ * Add a new file class to the class list.
+ * This either represents a superclass for several file extensions or
+ * the the url object.
+ * Urls just need a class named after their prefix e.g. "http".
+ * File extensions should follow the rule "appname.extension".
+ */
+
+// pszIconLoc, pszVerbDesc and pszDdeCmd are allowed to be NULL
+// call GetLastError() on error to get more error details
+BOOL AddRegClass(const char *pszClassName,const TCHAR *pszTypeDescription,const TCHAR *pszIconLoc,const TCHAR *pszAppName,const TCHAR *pszRunCmd,const TCHAR *pszDdeCmd,const TCHAR *pszDdeApp,const TCHAR *pszDdeTopic,const TCHAR *pszVerbDesc,BOOL fBrowserAutoOpen,BOOL fUrlProto,BOOL fIsShortcut)
+{
+ LONG res;
+ HKEY hRootKey,hClassKey,hShellKey,hVerbKey,hDdeKey;
+
+ /* some error checking for disallowed values (to avoid errors in registry) */
+ if(strchr(pszClassName,'\\')!=NULL || strchr(pszClassName,' ')!=NULL) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ /* try to open interactive user's classes key */
+ if (!IsWinVer2000Plus() || RegOpenKeyEx(HKEY_CURRENT_USER,_T("Software\\Classes"),0,KEY_CREATE_SUB_KEY,&hRootKey))
+ hRootKey=HKEY_CLASSES_ROOT; /* might be write protected by security settings */
+
+ /* class */
+ if ((res=RegCreateKeyExA(hRootKey,pszClassName,0,NULL,0,KEY_SET_VALUE|KEY_CREATE_SUB_KEY|DELETE|KEY_QUERY_VALUE,NULL,&hClassKey,NULL))==ERROR_SUCCESS) {
+ /* backup class if shared */
+ if(fUrlProto) BackupRegTree(hRootKey,pszClassName,"bak_");
+ /* type description */
+ if(fUrlProto) SetRegStrPrefixValue(hClassKey,_T("URL:"),pszTypeDescription);
+ else RegSetValueEx(hClassKey,NULL,0,REG_SZ,(BYTE*)pszTypeDescription,(lstrlen(pszTypeDescription)+1)*sizeof(TCHAR));
+ /* default icon */
+ if(pszIconLoc!=NULL) SetRegSubKeyStrDefValue(hClassKey,_T("DefaultIcon"),pszIconLoc);
+ /* url protocol */
+ if (!fUrlProto) RegDeleteValue(hClassKey,_T("URL Protocol"));
+ else RegSetValueEx(hClassKey,_T("URL Protocol"),0,REG_SZ,NULL,0);
+ /* moniker clsid */
+ RegDeleteKey(hClassKey,_T("CLSID"));
+ /* edit flags */
+ { DWORD dwFlags=0,dwSize=sizeof(dwFlags);
+ RegQueryValueEx(hClassKey,_T("EditFlags"),NULL,NULL,(BYTE*)&dwFlags,&dwSize);
+ if(fBrowserAutoOpen) dwFlags=(dwFlags&~FTA_AlwaysUnsafe)|FTA_OpenIsSafe;
+ if (!fUrlProto) dwFlags|=FTA_HasExtension;
+ else dwFlags=(dwFlags&~FTA_HasExtension)|FTA_Show; /* show classes without extension */
+ RegSetValueEx(hClassKey,_T("EditFlags"),0,REG_DWORD,(BYTE*)&dwFlags,sizeof(dwFlags));
+ }
+ if(fIsShortcut) {
+ RegSetValueExA(hClassKey,"IsShortcut",0,REG_SZ,NULL,0);
+ RegSetValueExA(hClassKey,"NeverShowExt",0,REG_SZ,NULL,0);
+ }
+ /* shell */
+ if ((res=RegCreateKeyEx(hClassKey,_T("shell"),0,NULL,0,KEY_SET_VALUE|KEY_CREATE_SUB_KEY,NULL,&hShellKey,NULL))==ERROR_SUCCESS) {
+ /* default verb (when empty "open" is used) */
+ RegSetValueEx(hShellKey,NULL,0,REG_SZ,(BYTE*)_T("open"),5*sizeof(TCHAR));
+ /* verb */
+ if ((res=RegCreateKeyEx(hShellKey,_T("open"),0,NULL,0,KEY_SET_VALUE|KEY_CREATE_SUB_KEY|DELETE,NULL,&hVerbKey,NULL))==ERROR_SUCCESS) {
+ /* verb description */
+ if(pszVerbDesc==NULL) RegDeleteValue(hVerbKey,NULL);
+ else RegSetValueEx(hVerbKey,NULL,0,REG_SZ,(BYTE*)pszVerbDesc,(lstrlen(pszVerbDesc)+1)*sizeof(TCHAR));
+ /* friendly appname (mui string) */
+ RegSetValueEx(hVerbKey,_T("FriendlyAppName"),0,REG_SZ,(BYTE*)pszAppName,(lstrlen(pszAppName)+1)*sizeof(TCHAR));
+ /* command */
+ SetRegSubKeyStrDefValue(hVerbKey,_T("command"),pszRunCmd);
+ /* ddeexec */
+ if(pszDdeCmd!=NULL) {
+ if (!RegCreateKeyEx(hVerbKey,_T("ddeexec"),0,NULL,0,KEY_SET_VALUE|KEY_CREATE_SUB_KEY|DELETE,NULL,&hDdeKey,NULL)) {
+ /* command */
+ RegSetValueEx(hDdeKey,NULL,0,REG_SZ,(BYTE*)pszDdeCmd,(lstrlen(pszDdeCmd)+1)*sizeof(TCHAR));
+ /* application */
+ SetRegSubKeyStrDefValue(hDdeKey,_T("application"),pszDdeApp);
+ /* topic */
+ SetRegSubKeyStrDefValue(hDdeKey,_T("topic"),pszDdeTopic);
+ /* ifexec */
+ RegDeleteKey(hDdeKey,_T("ifexec"));
+ RegCloseKey(hDdeKey);
+ }
+ } else {
+ if (!RegOpenKeyEx(hVerbKey,_T("ddeexec"),0,DELETE,&hDdeKey)) {
+ /* application */
+ RegDeleteKey(hDdeKey,_T("application"));
+ /* topic */
+ RegDeleteKey(hDdeKey,_T("topic"));
+ /* ifexec */
+ RegDeleteKey(hDdeKey,_T("ifexec"));
+ RegCloseKey(hDdeKey);
+ }
+ RegDeleteKey(hVerbKey,_T("ddeexec"));
+ }
+ /* drop target (WinXP+) */
+ RegDeleteKey(hVerbKey,_T("DropTarget"));
+ RegCloseKey(hVerbKey);
+ }
+ RegCloseKey(hShellKey);
+ /* Opera support */
+ if(fUrlProto) Opera6_AddTrustedProto(pszClassName);
+ }
+ RegCloseKey(hClassKey);
+ }
+
+ if(hRootKey!=HKEY_CLASSES_ROOT)
+ RegCloseKey(hRootKey);
+
+ if(res) SetLastError(res);
+ return !res;
+}
+
+BOOL RemoveRegClass(const char *pszClassName)
+{
+ LONG res;
+ HKEY hRootKey,hClassKey,hShellKey,hVerbKey;
+ TCHAR *ptszClassName,*ptszPrevRunCmd;
+
+ /* try to open interactive user's classes key */
+ if (!IsWinVer2000Plus() || RegOpenKeyEx(HKEY_CURRENT_USER,_T("Software\\Classes"),0,DELETE,&hRootKey))
+ hRootKey=HKEY_CLASSES_ROOT;
+
+ /* class name */
+ ptszClassName=a2t(pszClassName);
+ if(ptszClassName!=NULL)
+ res=DeleteRegSubTree(hRootKey,ptszClassName);
+ else res=ERROR_OUTOFMEMORY;
+ mir_free(ptszClassName); /* does NULL check */
+
+ /* backup only saved/restored for fUrlProto */
+ if (!res) {
+ if ((res=RestoreRegTree(hRootKey,pszClassName,"bak_"))==ERROR_SUCCESS)
+ /* class */
+ if (!RegOpenKeyExA(hRootKey,pszClassName,0,KEY_QUERY_VALUE,&hClassKey)) {
+ /* shell */
+ if (!RegOpenKeyEx(hClassKey,_T("shell"),0,KEY_QUERY_VALUE,&hShellKey)) {
+ /* verb */
+ if (!RegOpenKeyEx(hShellKey,_T("open"),0,KEY_QUERY_VALUE,&hVerbKey)) {
+ /* command */
+ ptszPrevRunCmd=GetRegStrValue(hVerbKey,_T("command"));
+ if(ptszPrevRunCmd!=NULL && !IsValidRunCommand(ptszPrevRunCmd))
+ res=DeleteRegSubTree(hRootKey,ptszClassName); /* backup outdated, remove all */
+ mir_free(ptszPrevRunCmd); /* does NULL check */
+ RegCloseKey(hVerbKey);
+ }
+ RegCloseKey(hShellKey);
+ }
+ RegCloseKey(hClassKey);
+ }
+ } else DeleteRegTreeBackup(pszClassName,"bak_");
+
+ if(hRootKey!=HKEY_CLASSES_ROOT)
+ RegCloseKey(hRootKey);
+
+ if(res==ERROR_SUCCESS || res==ERROR_FILE_NOT_FOUND || res==ERROR_NO_MORE_ITEMS) return TRUE;
+ SetLastError(res);
+ return FALSE;
+}
+
+/*
+ * Test if a given class belongs to the current process
+ * specified via its run command.
+ * This is especially needed for Urls where the same class name "http" can be
+ * registered and thus be overwritten by multiple applications.
+ */
+
+BOOL IsRegClass(const char *pszClassName,const TCHAR *pszRunCmd)
+{
+ BOOL fSuccess=FALSE;
+ HKEY hClassKey,hShellKey,hVerbKey,hCmdKey;
+
+ /* using the merged view classes key for reading */
+ /* class */
+ if (!RegOpenKeyExA(HKEY_CLASSES_ROOT,pszClassName,0,KEY_QUERY_VALUE,&hClassKey)) {
+ /* shell */
+ if (!RegOpenKeyEx(hClassKey,_T("shell"),0,KEY_QUERY_VALUE,&hShellKey)) {
+ /* verb */
+ if (!RegOpenKeyEx(hShellKey,_T("open"),0,KEY_QUERY_VALUE,&hVerbKey)) {
+ /* command */
+ if (!RegOpenKeyEx(hVerbKey,_T("command"),0,KEY_QUERY_VALUE,&hCmdKey)) {
+ /* it is enough to check if the command is right */
+ fSuccess=IsRegStrValue(hCmdKey,NULL,pszRunCmd);
+ RegCloseKey(hCmdKey);
+ }
+ RegCloseKey(hVerbKey);
+ }
+ RegCloseKey(hShellKey);
+ }
+ RegCloseKey(hClassKey);
+ }
+ return fSuccess;
+}
+
+/*
+ * Extract the icon name of the class from the registry and load it.
+ * For uses especially with url classes.
+ */
+
+// DestroyIcon() the return value
+HICON LoadRegClassSmallIcon(const char *pszClassName)
+{
+ HICON hIcon=NULL;
+ HKEY hClassKey,hIconKey;
+ TCHAR *pszIconLoc,*p;
+
+ /* using the merged view classes key for reading */
+ /* class */
+ if (!RegOpenKeyExA(HKEY_CLASSES_ROOT,pszClassName,0,KEY_QUERY_VALUE,&hClassKey)) {
+ /* default icon */
+ if (!RegOpenKeyEx(hClassKey,_T("DefaultIcon"),0,KEY_QUERY_VALUE,&hIconKey)) {
+ /* extract icon */
+ pszIconLoc=GetRegStrValue(hIconKey,NULL);
+ if(pszIconLoc!=NULL) {
+ p=_tcsrchr(pszIconLoc,_T(','));
+ if(p!=NULL) {
+ *(p++)=0;
+ ExtractIconEx(pszIconLoc,_ttoi(p),NULL,&hIcon,1);
+ }
+ mir_free(pszIconLoc);
+ }
+ RegCloseKey(hIconKey);
+ }
+ RegCloseKey(hClassKey);
+ }
+
+ return hIcon;
+}
+
+/************************* Extension ******************************/
+
+/*
+ * Add a new file extension to the class list.
+ * The file extension needs to be associated with a class
+ * that has been registered previously.
+ * Multiple file extensions can be assigned to the same class.
+ * The class contains most settings as the run command etc.
+ */
+
+// pszMimeType is allowed to be NULL
+BOOL AddRegFileExt(const char *pszFileExt,const char *pszClassName,const char *pszMimeType,BOOL fIsText)
+{
+ BOOL fSuccess=FALSE;
+ HKEY hRootKey,hExtKey,hOpenWithKey;
+
+ /* some error checking for disallowed values (to avoid errors in registry) */
+ if(strchr(pszFileExt,'\\')!=NULL || strchr(pszFileExt,' ')!=NULL)
+ return FALSE;
+
+ /* try to open interactive user's classes key */
+ if (!IsWinVer2000Plus() || RegOpenKeyEx(HKEY_CURRENT_USER,_T("Software\\Classes"),0,KEY_CREATE_SUB_KEY,&hRootKey))
+ hRootKey=HKEY_CLASSES_ROOT;
+
+ /* file ext */
+ if (!RegCreateKeyExA(hRootKey,pszFileExt,0,NULL,0,KEY_SET_VALUE|KEY_QUERY_VALUE|KEY_CREATE_SUB_KEY,NULL,&hExtKey,NULL)) {
+ TCHAR *pszPrevClass;
+ /* backup previous app */
+ BackupRegTree(hRootKey,pszFileExt,"bak_");
+ /* remove any no-open flag */
+ RegDeleteValue(hExtKey,_T("NoOpen"));
+ /* open with progids */
+ pszPrevClass=GetRegStrValue(hExtKey,NULL);
+ if(pszPrevClass!=NULL && !IsRegStrValueA(hExtKey,NULL,pszClassName))
+ if (!RegCreateKeyEx(hExtKey,_T("OpenWithProgids"),0,NULL,0,KEY_SET_VALUE,NULL,&hOpenWithKey,NULL)) {
+ /* previous class (backup) */
+ RegSetValueEx(hOpenWithKey,pszPrevClass,0,REG_NONE,NULL,0);
+ RegCloseKey(hOpenWithKey);
+ }
+ mir_free(pszPrevClass); /* does NULL check */
+ /* class name */
+ fSuccess=!RegSetValueExA(hExtKey,NULL,0,REG_SZ,(BYTE*)pszClassName,lstrlenA(pszClassName)+1);
+ /* mime type e.g. "application/x-icq" */
+ if(pszMimeType!=NULL) RegSetValueExA(hExtKey,"Content Type",0,REG_SZ,(BYTE*)pszMimeType,lstrlenA(pszMimeType)+1);
+ /* perceived type e.g. text (WinXP+) */
+ if(fIsText) RegSetValueEx(hExtKey,_T("PerceivedType"),0,REG_SZ,(BYTE*)_T("text"),5*sizeof(TCHAR));
+ RegCloseKey(hExtKey);
+ }
+
+ if(hRootKey!=HKEY_CLASSES_ROOT)
+ RegCloseKey(hRootKey);
+ return fSuccess;
+}
+
+void RemoveRegFileExt(const char *pszFileExt,const char *pszClassName)
+{
+ HKEY hRootKey,hExtKey,hSubKey;
+ DWORD nOpenWithCount;
+ TCHAR *pszPrevClassName=NULL;
+ BOOL fRestored=FALSE;
+
+ /* try to open interactive user's classes key */
+ if (!IsWinVer2000Plus() || RegOpenKeyEx(HKEY_CURRENT_USER,_T("Software\\Classes"),0,DELETE,&hRootKey))
+ hRootKey=HKEY_CLASSES_ROOT;
+
+ /* file ext */
+ if (!RegOpenKeyExA(hRootKey,pszFileExt,0,KEY_QUERY_VALUE|KEY_SET_VALUE|DELETE,&hExtKey)) {
+ /* class name (the important part) */
+ if (!RestoreRegTree(hRootKey,pszFileExt,"bak_")) {
+ pszPrevClassName=GetRegStrValue(hExtKey,NULL);
+ if(pszPrevClassName!=NULL) {
+ /* previous class name still exists? */
+ /* using the merged view classes key for reading */
+ if (!RegOpenKeyEx(HKEY_CLASSES_ROOT,pszPrevClassName,0,KEY_QUERY_VALUE,&hSubKey)) {
+ fRestored=TRUE;
+ RegCloseKey(hSubKey);
+ } else RegDeleteValue(hExtKey,NULL);
+ mir_free(pszPrevClassName);
+ }
+ }
+ if(pszPrevClassName==NULL) RegDeleteValue(hExtKey,NULL);
+ /* open with progids (remove if empty) */
+ nOpenWithCount=0;
+ if (!RegOpenKeyEx(hExtKey,_T("OpenWithProgids"),0,KEY_SET_VALUE|KEY_QUERY_VALUE,&hSubKey)) {
+ /* remove current class (if set by another app) */
+ RegDeleteValueA(hSubKey,pszClassName);
+ RegQueryInfoKey(hSubKey,NULL,NULL,NULL,NULL,NULL,NULL,NULL,&nOpenWithCount,NULL,NULL,NULL);
+ RegCloseKey(hSubKey);
+ }
+ if (!nOpenWithCount) RegDeleteKey(hExtKey,_T("OpenWithProgids")); /* delete if no values */
+ RegCloseKey(hExtKey);
+ } else DeleteRegTreeBackup(pszFileExt,"bak_");
+ if (!fRestored) RegDeleteKeyA(hRootKey,pszFileExt); /* try to remove it all */
+
+ if(hRootKey!=HKEY_CLASSES_ROOT)
+ RegCloseKey(hRootKey);
+}
+
+/*
+ * Test if a given file extension belongs to the given class name.
+ * If it does not belong to the class name, it got reassigned and thus
+ * overwritten by another application.
+ */
+
+BOOL IsRegFileExt(const char *pszFileExt,const char *pszClassName)
+{
+ BOOL fSuccess=FALSE;
+ HKEY hExtKey;
+
+ /* using the merged view classes key for reading */
+ /* file ext */
+ if (!RegOpenKeyExA(HKEY_CLASSES_ROOT,pszFileExt,0,KEY_QUERY_VALUE,&hExtKey)) {
+ /* class name */
+ /* it is enough to check if the class is right */
+ fSuccess=IsRegStrValueA(hExtKey,NULL,pszClassName);
+ RegCloseKey(hExtKey);
+ }
+ return fSuccess;
+}
+
+/************************* Mime Type ******************************/
+
+/*
+ * Add a given mime type to the global mime database.
+ */
+
+// returns TRUE if the mime type was not yet registered on the system,
+// it needs to be removed when the file extension gets removed
+BOOL AddRegMimeType(const char *pszMimeType,const char *pszFileExt,const TCHAR *pszDescription)
+{
+ BOOL fSuccess=FALSE;
+ HKEY hRootKey,hDbKey,hTypeKey;
+
+ /* some error checking for disallowed values (to avoid errors in registry) */
+ if(strchr(pszMimeType,'\\')!=NULL || strchr(pszMimeType,' ')!=NULL)
+ return FALSE;
+
+ /* try to open interactive user's classes key */
+ if (!IsWinVer2000Plus() || RegOpenKeyEx(HKEY_CURRENT_USER,_T("Software\\Classes"),0,KEY_QUERY_VALUE,&hRootKey))
+ hRootKey=HKEY_CLASSES_ROOT;
+
+ /* database */
+ if (!RegOpenKeyEx(hRootKey,_T("MIME\\Database\\Content Type"),0,KEY_CREATE_SUB_KEY,&hDbKey)) {
+ /* mime type */
+ if (!RegCreateKeyExA(hDbKey,pszMimeType,0,NULL,0,KEY_QUERY_VALUE|KEY_SET_VALUE,NULL,&hTypeKey,NULL)) {
+ /* file ext */
+ if(RegQueryValueExA(hTypeKey,"Extension",NULL,NULL,NULL,NULL)) /* only set if not present */
+ fSuccess=!RegSetValueExA(hTypeKey,"Extension",0,REG_SZ,(BYTE*)pszFileExt,lstrlenA(pszFileExt)+1);
+ RegCloseKey(hTypeKey);
+ /* Opera support */
+ Opera6_AddKnownMimeType(pszMimeType,pszFileExt,pszDescription);
+ }
+ RegCloseKey(hDbKey);
+ }
+
+ if(hRootKey!=HKEY_CLASSES_ROOT)
+ RegCloseKey(hRootKey);
+ return fSuccess;
+}
+
+void RemoveRegMimeType(const char *pszMimeType,const char *pszFileExt)
+{
+ HKEY hRootKey,hDbKey,hTypeKey;
+ BOOL fDelete=TRUE;
+
+ /* try to open interactive user's classes key */
+ if (!IsWinVer2000Plus() || RegOpenKeyEx(HKEY_CURRENT_USER,_T("Software\\Classes"),0,KEY_QUERY_VALUE,&hRootKey))
+ hRootKey=HKEY_CLASSES_ROOT;
+
+ /* database */
+ if (!RegOpenKeyEx(hRootKey,_T("MIME\\Database\\Content Type"),0,DELETE,&hDbKey)) {
+ /* mime type */
+ if (!RegOpenKeyExA(hDbKey,pszMimeType,0,KEY_QUERY_VALUE,&hTypeKey)) {
+ /* file ext */
+ fDelete=IsRegStrValueA(hTypeKey,_T("Extension"),pszFileExt);
+ RegCloseKey(hTypeKey);
+ }
+ if(fDelete) RegDeleteKeyA(hDbKey,pszMimeType);
+ RegCloseKey(hDbKey);
+ }
+
+ if(hRootKey!=HKEY_CLASSES_ROOT)
+ RegCloseKey(hRootKey);
+}
+
+/************************* Open-With App **************************/
+
+/*
+ * Add Miranda as an option to the advanced "Open With..." dialog.
+ */
+
+// pszDdeCmd is allowed to be NULL
+void AddRegOpenWith(const TCHAR *pszAppFileName,BOOL fAllowOpenWith,const TCHAR *pszAppName,const TCHAR *pszIconLoc,const TCHAR *pszRunCmd,const TCHAR *pszDdeCmd,const TCHAR *pszDdeApp,const TCHAR *pszDdeTopic)
+{
+ HKEY hRootKey,hAppsKey,hExeKey,hShellKey,hVerbKey,hDdeKey;
+
+ /* try to open interactive user's classes key */
+ if (!IsWinVer2000Plus() || RegOpenKeyEx(HKEY_CURRENT_USER,_T("Software\\Classes"),0,KEY_QUERY_VALUE,&hRootKey))
+ hRootKey=HKEY_CLASSES_ROOT;
+
+ /* database */
+ if (!RegCreateKeyEx(hRootKey,_T("Applications"),0,NULL,0,KEY_CREATE_SUB_KEY,NULL,&hAppsKey,NULL)) {
+ /* filename */
+ if (!RegCreateKeyEx(hAppsKey,pszAppFileName,0,NULL,0,KEY_SET_VALUE|KEY_CREATE_SUB_KEY,NULL,&hExeKey,NULL)) {
+ /* appname */
+ RegSetValueEx(hExeKey,NULL,0,REG_SZ,(BYTE*)pszAppName,(lstrlen(pszAppName)+1)*sizeof(TCHAR));
+ /* no open-with flag */
+ if(fAllowOpenWith) RegDeleteValue(hExeKey,_T("NoOpenWith"));
+ else RegSetValueEx(hExeKey,_T("NoOpenWith"),0,REG_SZ,NULL,0);
+ /* default icon */
+ if(pszIconLoc!=NULL) SetRegSubKeyStrDefValue(hExeKey,_T("DefaultIcon"),pszIconLoc);
+ /* shell */
+ if (!RegCreateKeyEx(hExeKey,_T("shell"),0,NULL,0,KEY_SET_VALUE|KEY_CREATE_SUB_KEY,NULL,&hShellKey,NULL)) {
+ /* default verb (when empty "open" is used) */
+ RegSetValueEx(hShellKey,NULL,0,REG_SZ,(BYTE*)_T("open"),5*sizeof(TCHAR));
+ /* verb */
+ if (!RegCreateKeyEx(hShellKey,_T("open"),0,NULL,0,KEY_SET_VALUE|KEY_CREATE_SUB_KEY,NULL,&hVerbKey,NULL)) {
+ /* friendly appname (mui string) */
+ RegSetValueEx(hVerbKey,_T("FriendlyAppName"),0,REG_SZ,(BYTE*)pszAppName,(lstrlen(pszAppName)+1)*sizeof(TCHAR));
+ /* command */
+ SetRegSubKeyStrDefValue(hVerbKey,_T("command"),pszRunCmd);
+ /* ddeexec */
+ if(pszDdeCmd!=NULL)
+ if (!RegCreateKeyEx(hVerbKey,_T("ddeexec"),0,NULL,0,KEY_SET_VALUE|KEY_CREATE_SUB_KEY,NULL,&hDdeKey,NULL)) {
+ /* command */
+ RegSetValueEx(hDdeKey,NULL,0,REG_SZ,(BYTE*)pszDdeCmd,(lstrlen(pszDdeCmd)+1)*sizeof(TCHAR));
+ /* application */
+ SetRegSubKeyStrDefValue(hDdeKey,_T("application"),pszDdeApp);
+ /* topic */
+ SetRegSubKeyStrDefValue(hDdeKey,_T("topic"),pszDdeTopic);
+ RegCloseKey(hDdeKey);
+ }
+ RegCloseKey(hVerbKey);
+ }
+ RegCloseKey(hShellKey);
+ }
+ RegCloseKey(hExeKey);
+ }
+ RegCloseKey(hAppsKey);
+ }
+
+ if(hRootKey!=HKEY_CLASSES_ROOT)
+ RegCloseKey(hRootKey);
+}
+
+void RemoveRegOpenWith(const TCHAR *pszAppFileName)
+{
+ HKEY hRootKey,hAppsKey,hExeKey,hShellKey,hVerbKey,hDdeKey;
+
+ /* try to open interactive user's classes key */
+ if (!IsWinVer2000Plus() || RegOpenKeyEx(HKEY_CURRENT_USER,_T("Software\\Classes"),0,KEY_QUERY_VALUE,&hRootKey))
+ hRootKey=HKEY_CLASSES_ROOT;
+
+ /* applications */
+ if (!RegOpenKeyEx(hRootKey,_T("Applications"),0,DELETE,&hAppsKey)) {
+ /* filename */
+ if (!RegOpenKeyEx(hAppsKey,pszAppFileName,0,DELETE,&hExeKey)) {
+ /* default icon */
+ RegDeleteKey(hExeKey,_T("DefaultIcon"));
+ /* shell */
+ if (!RegOpenKeyEx(hExeKey,_T("shell"),0,DELETE,&hShellKey)) {
+ /* verb */
+ if (!RegOpenKeyEx(hShellKey,_T("open"),0,DELETE,&hVerbKey)) {
+ /* command */
+ RegDeleteKey(hVerbKey,_T("command"));
+ /* ddeexec */
+ if (!RegOpenKeyEx(hVerbKey,_T("ddeexec"),0,DELETE,&hDdeKey)) {
+ /* application */
+ RegDeleteKey(hDdeKey,_T("application"));
+ /* topic */
+ RegDeleteKey(hDdeKey,_T("topic"));
+ RegCloseKey(hDdeKey);
+ }
+ RegDeleteKey(hVerbKey,_T("ddeexec"));
+ RegCloseKey(hVerbKey);
+ }
+ RegDeleteKey(hShellKey,_T("open"));
+ RegCloseKey(hShellKey);
+ }
+ RegDeleteKey(hExeKey,_T("shell"));
+ /* supported types */
+ RegDeleteKey(hExeKey,_T("SupportedTypes"));
+ RegCloseKey(hExeKey);
+ }
+ RegDeleteKey(hAppsKey,pszAppFileName);
+ RegCloseKey(hAppsKey);
+ }
+
+ if(hRootKey!=HKEY_CLASSES_ROOT)
+ RegCloseKey(hRootKey);
+}
+
+/*
+ * Tell the "Open With..." dialog we support a given file extension.
+ */
+
+void AddRegOpenWithExtEntry(const TCHAR *pszAppFileName,const char *pszFileExt,const TCHAR *pszFileDesc)
+{
+ HKEY hRootKey,hAppsKey,hExeKey,hTypesKey;
+
+ /* try to open interactive user's classes key */
+ if (!IsWinVer2000Plus() || RegOpenKeyEx(HKEY_CURRENT_USER,_T("Software\\Classes"),0,KEY_QUERY_VALUE,&hRootKey))
+ hRootKey=HKEY_CLASSES_ROOT;
+
+ /* applications */
+ if (!RegOpenKeyEx(hRootKey,_T("Applications"),0,KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS,&hAppsKey)) {
+ /* filename */
+ if (!RegOpenKeyEx(hAppsKey,pszAppFileName,0,KEY_CREATE_SUB_KEY,&hExeKey)) {
+ /* supported types */
+ if (!RegCreateKeyEx(hExeKey,_T("SupportedTypes"),0,NULL,0,KEY_SET_VALUE,NULL,&hTypesKey,NULL)) {
+ TCHAR *ptszFileExt;
+ ptszFileExt=a2t(pszFileExt);
+ if(ptszFileExt!=NULL)
+ RegSetValueEx(hTypesKey,ptszFileExt,0,REG_SZ,(BYTE*)pszFileDesc,(lstrlen(pszFileDesc)+1)*sizeof(TCHAR));
+ mir_free(ptszFileExt); /* does NULL check */
+ RegCloseKey(hTypesKey);
+ }
+ RegCloseKey(hExeKey);
+ }
+ RegCloseKey(hAppsKey);
+ }
+
+ if(hRootKey!=HKEY_CLASSES_ROOT)
+ RegCloseKey(hRootKey);
+}
+
+void RemoveRegOpenWithExtEntry(const TCHAR *pszAppFileName,const char *pszFileExt)
+{
+ HKEY hRootKey,hAppsKey,hExeKey,hTypesKey;
+
+ /* try to open interactive user's classes key */
+ if (!IsWinVer2000Plus() || RegOpenKeyEx(HKEY_CURRENT_USER,_T("Software\\Classes"),0,KEY_QUERY_VALUE,&hRootKey))
+ hRootKey=HKEY_CLASSES_ROOT;
+
+ /* applications */
+ if (!RegOpenKeyEx(hRootKey,_T("Applications"),0,KEY_QUERY_VALUE,&hAppsKey)) {
+ /* filename */
+ if (!RegOpenKeyEx(hAppsKey,pszAppFileName,0,KEY_QUERY_VALUE,&hExeKey)) {
+ /* supported types */
+ if (!RegOpenKeyEx(hExeKey,_T("SupportedTypes"),0,KEY_SET_VALUE,&hTypesKey)) {
+ RegDeleteValueA(hTypesKey,pszFileExt);
+ RegCloseKey(hTypesKey);
+ }
+ RegCloseKey(hExeKey);
+ }
+ RegCloseKey(hAppsKey);
+ }
+
+ if(hRootKey!=HKEY_CLASSES_ROOT)
+ RegCloseKey(hRootKey);
+}
+
+/************************* Autostart ******************************/
+
+/*
+ * Add Miranda to the autostart list in the registry.
+ */
+
+BOOL AddRegRunEntry(const TCHAR *pszAppName,const TCHAR *pszRunCmd)
+{
+ BOOL fSuccess=FALSE;
+ HKEY hRunKey;
+
+ /* run */
+ if (!RegCreateKeyEx(HKEY_CURRENT_USER,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Run"),0,NULL,0,KEY_SET_VALUE,NULL,&hRunKey,NULL)) {
+ /* appname */
+ fSuccess=!RegSetValueEx(hRunKey,pszAppName,0,REG_SZ,(BYTE*)pszRunCmd,(lstrlen(pszRunCmd)+1)*sizeof(TCHAR));
+ RegCloseKey(hRunKey);
+ }
+ return fSuccess;
+}
+
+BOOL RemoveRegRunEntry(const TCHAR *pszAppName,const TCHAR *pszRunCmd)
+{
+ LONG res;
+ HKEY hRunKey;
+
+ /* run */
+ res=RegOpenKeyEx(HKEY_CURRENT_USER,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Run"),0,KEY_QUERY_VALUE|KEY_SET_VALUE,&hRunKey);
+ if (!res) {
+ /* appname */
+ if(IsRegStrValue(hRunKey,pszAppName,pszRunCmd))
+ res=RegDeleteValue(hRunKey,pszAppName); /* only remove if current */
+ RegCloseKey(hRunKey);
+ }
+ return res==ERROR_SUCCESS || res==ERROR_FILE_NOT_FOUND;
+}
+
+/*
+ * Check if the autostart item belongs to the current instance of Miranda.
+ */
+
+BOOL IsRegRunEntry(const TCHAR *pszAppName,const TCHAR *pszRunCmd)
+{
+ BOOL fState=FALSE;
+ HKEY hRunKey;
+
+ /* Run */
+ if (!RegOpenKeyEx(HKEY_CURRENT_USER,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Run"),0,KEY_QUERY_VALUE,&hRunKey)) {
+ /* appname */
+ fState=IsRegStrValue(hRunKey,pszAppName,pszRunCmd);
+ RegCloseKey(hRunKey);
+ }
+ return fState;
+}
diff --git a/plugins/AssocMgr/src/reg.h b/plugins/AssocMgr/src/reg.h
new file mode 100644
index 0000000000..98884a3224
--- /dev/null
+++ b/plugins/AssocMgr/src/reg.h
@@ -0,0 +1,51 @@
+/*
+
+'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.
+*/
+
+/* Backup to DB */
+void CleanupRegTreeBackupSettings(void);
+/* Class */
+BOOL AddRegClass(const char *pszClassName,const TCHAR *pszTypeDescription,const TCHAR *pszIconLoc,const TCHAR *pszAppName,const TCHAR *pszRunCmd,const TCHAR *pszDdeCmd,const TCHAR *pszDdeApp,const TCHAR *pszDdeTopic,const TCHAR *pszVerbDesc,BOOL fBrowserAutoOpen,BOOL fUrlProto,BOOL fIsShortcut);
+BOOL RemoveRegClass(const char *pszClassName);
+BOOL IsRegClass(const char *pszClassName,const TCHAR *pszRunCmd);
+HICON LoadRegClassSmallIcon(const char *pszClassName);
+/* Extension */
+BOOL AddRegFileExt(const char *pszFileExt,const char *pszClassName,const char *pszMimeType,BOOL fIsText);
+void RemoveRegFileExt(const char *pszFileExt,const char *pszClassName);
+BOOL IsRegFileExt(const char *pszFileExt,const char *pszClassName);
+/* Mime Type */
+BOOL AddRegMimeType(const char *pszMimeType,const char *pszFileExt,const TCHAR *pszDescription);
+void RemoveRegMimeType(const char *pszMimeType,const char *pszFileExt);
+/* Open-With App */
+void AddRegOpenWith(const TCHAR *pszAppFileName,BOOL fAllowOpenWith,const TCHAR *pszAppName,const TCHAR *pszIconLoc,const TCHAR *pszRunCmd,const TCHAR *pszDdeCmd,const TCHAR *pszDdeApp,const TCHAR *pszDdeTopic);
+void RemoveRegOpenWith(const TCHAR *pszAppFileName);
+void AddRegOpenWithExtEntry(const TCHAR *pszAppFileName,const char *pszFileExt,const TCHAR *pszFileDesc);
+void RemoveRegOpenWithExtEntry(const TCHAR *pszAppFileName,const char *pszFileExt);
+/* Autostart */
+BOOL AddRegRunEntry(const TCHAR *pszAppName,const TCHAR *pszRunCmd);
+BOOL RemoveRegRunEntry(const TCHAR *pszAppName,const TCHAR *pszRunCmd);
+BOOL IsRegRunEntry(const TCHAR *pszAppName,const TCHAR *pszRunCmd);
+
+/* Strings */
+char *MakeFileClassName(const char *pszFileExt);
+char *MakeUrlClassName(const char *pszUrl);
+TCHAR *MakeRunCommand(BOOL fMirExe,BOOL fFixedDbProfile);
+TCHAR *MakeIconLocation(HMODULE hModule,WORD nIconResID);
+TCHAR *MakeAppFileName(BOOL fMirExe); \ No newline at end of file
diff --git a/plugins/AssocMgr/src/resource.h b/plugins/AssocMgr/src/resource.h
new file mode 100644
index 0000000000..2eda2da237
--- /dev/null
+++ b/plugins/AssocMgr/src/resource.h
@@ -0,0 +1,24 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by resource.rc
+//
+#define IDI_MIRANDA 101
+#define IDI_MIRANDAFILE 102
+#define IDD_OPT_ASSOCLIST 103
+#define IDC_HEADERTEXT 1001
+#define IDC_ASSOCLIST 1002
+#define IDC_AUTOSTART 1003
+#define IDC_ONLYWHILERUNNING 1004
+#define IDC_MISCLABEL 1005
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 104
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1006
+#define _APS_NEXT_SYMED_VALUE 2001
+#endif
+#endif
diff --git a/plugins/AssocMgr/src/test.cpp b/plugins/AssocMgr/src/test.cpp
new file mode 100644
index 0000000000..73112af5a5
--- /dev/null
+++ b/plugins/AssocMgr/src/test.cpp
@@ -0,0 +1,869 @@
+/*
+
+'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.
+*/
+
+// -- Includes
+#include "common.h"
+
+/*
+#define AIM_SUPPORT_TEST
+#define ICQ_SUPPORT_TEST
+#define YAHOO_SUPPORT_TEST
+#define MSN_SUPPORT_TEST
+#define GG_SUPPORT_TEST
+#define JABBER_SUPPORT_TEST
+*/
+
+// -----------------------------------------
+
+extern HINSTANCE hInst;
+
+/* a copy of those in m_assocmgr.h, as we did not #include those helpers */
+__inline static int AssocMgr_AddNewFileTypeT(const char *ext,const char *mime,const TCHAR *desc,const TCHAR *verb,HINSTANCE hinst,UINT iconid,const char *service,DWORD flags)
+{
+ FILETYPEDESC ftd;
+ ftd.cbSize=sizeof(FILETYPEDESC);
+ ftd.pszFileExt=ext;
+ ftd.pszMimeType=mime;
+ ftd.ptszDescription=desc;
+ ftd.hInstance=hinst;
+ ftd.nIconResID=iconid;
+ ftd.ptszVerbDesc=verb;
+ ftd.pszService=service;
+ ftd.flags=flags|FTDF_TCHAR;
+ return CallService(MS_ASSOCMGR_ADDNEWFILETYPE,0,(LPARAM)&ftd);
+}
+
+static int __inline AssocMgr_AddNewUrlType(const char *prefix,const char *desc,HINSTANCE hinst,UINT iconid,const char *service,DWORD flags)
+{
+ URLTYPEDESC utd;
+ utd.cbSize=sizeof(URLTYPEDESC);
+ utd.pszProtoPrefix=prefix;
+ utd.pszDescription=desc;
+ utd.hInstance=hinst;
+ utd.nIconResID=iconid;
+ utd.pszService=service;
+ utd.flags=flags&~UTDF_UNICODE;
+ return CallService(MS_ASSOCMGR_ADDNEWURLTYPE,0,(LPARAM)&utd);
+}
+
+// -----------------------------------------
+
+#ifdef AIM_SUPPORT_TEST
+
+#define AIM_PROTOCOL_NAME "AIM"
+#define IDI_AOL 28
+#define MOD_KEY_CL "CList"
+#define AIM_KEY_NL "NotOnList"
+struct oscar_data { HINSTANCE hInstance; } static conn;
+static __inline HANDLE find_contact(const char *nick) { nick; return NULL; }
+static __inline HANDLE add_contact(const char *nick) { nick; return (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); }
+static __inline void aim_gchat_joinrequest(const char *room, int exchange) { room; exchange; MessageBoxA(NULL,"Join group chat!",room,MB_OK); return; }
+#include <m_protosvc.h>
+#include <m_addcontact.h>
+#include <m_message.h>
+
+#include "m_assocmgr.h"
+static HANDLE hHookModulesLoaded;
+static HANDLE hServiceParseLink;
+
+static int ServiceParseAimLink(WPARAM wParam,LPARAM lParam)
+{
+ char *arg=(char*)lParam;
+ UNREFERENCED_PARAMETER(wParam);
+ if(arg==NULL) return 1; /* sanity check */
+ /* skip leading prefix */
+ arg=strchr(arg,':');
+ if(arg==NULL) return 1; /* parse failed */
+ for (++arg;*arg=='/';++arg);
+ /*
+ add user: aim:addbuddy?screenname=NICK&groupname=GROUP
+ send message: aim:goim?screenname=NICK&message=MSG
+ open chatroom: aim:gochat?roomname=ROOM&exchange=NUM
+ */
+ /* add a contact to the list */
+ if (!_strnicmp(arg,"addbuddy?",9)) {
+ char *tok,*sn=NULL,*group=NULL;
+ ADDCONTACTSTRUCT acs;
+ PROTOSEARCHRESULT psr;
+ if (*(arg+=9)==0) return 1; /* parse failed */
+ tok=strtok(arg,"&"); /* first token */
+ while(tok!=NULL) {
+ if (!_strnicmp(tok,"screenname=",11) && *(tok+11)!=0)
+ sn=Netlib_UrlDecode(tok+11);
+ if (!_strnicmp(tok,"groupname=",10) && *(tok+10)!=0)
+ group=Netlib_UrlDecode(tok+10); /* group is currently ignored */
+ tok=strtok(NULL,"&"); /* next token */
+ }
+ if(sn==NULL) return 1; /* parse failed */
+ if(find_contact(sn)==NULL) { /* does not yet check if sn is current user */
+ acs.handleType=HANDLE_SEARCHRESULT;
+ acs.szProto=AIM_PROTOCOL_NAME;
+ acs.psr=&psr;
+ ZeroMemory(&psr,sizeof(PROTOSEARCHRESULT));
+ psr.cbSize=sizeof(PROTOSEARCHRESULT);
+ psr.nick=sn;
+ CallService(MS_ADDCONTACT_SHOW,(WPARAM)NULL,(LPARAM)&acs);
+ }
+ return 0;
+ }
+ /* send a message to a contact */
+ else if (!_strnicmp(arg,"goim?",5)) {
+ char *tok,*sn=NULL,*msg=NULL;
+ HANDLE hContact;
+ if (*(arg+=5)==0) return 1; /* parse failed */
+ tok=strtok(arg,"&"); /* first token */
+ while(tok!=NULL) {
+ if (!_strnicmp(tok,"screenname=",11) && *(tok+11)!=0)
+ sn=Netlib_UrlDecode(tok+11);
+ if (!_strnicmp(tok,"message=",8) && *(tok+8)!=0)
+ msg=Netlib_UrlDecode(tok+8);
+ tok=strtok(NULL,"&"); /* next token */
+ }
+ if(sn==NULL) return 1; /* parse failed */
+ if(ServiceExists(MS_MSG_SENDMESSAGE)) {
+ hContact=find_contact(sn);
+ if(hContact==NULL) {
+ hContact=add_contact(sn); /* does not yet check if sn is current user */
+ if(hContact!=NULL)
+ DBWriteContactSettingByte(hContact,MOD_KEY_CL,AIM_KEY_NL,1);
+ }
+ if(hContact!=NULL)
+ CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)msg);
+ }
+ return 0;
+ }
+ /* open a chatroom */
+ else if (!_strnicmp(arg,"gochat?",7)) {
+ char *tok,*rm=NULL;
+ int exchange=0;
+ if (*(arg+=7)==0) return 1; /* parse failed */
+ tok=strtok(arg,"&"); /* first token */
+ while(tok!=NULL) {
+ if (!_strnicmp(tok,"roomname=",9) && *(tok+9)!=0)
+ rm=Netlib_UrlDecode(tok+9);
+ if (!_strnicmp(tok,"exchange=",9))
+ exchange=atoi(Netlib_UrlDecode(tok+9));
+ tok=strtok(NULL,"&"); /* next token */
+ }
+ if(rm==NULL || exchange<=0) return 1; /* parse failed */
+ aim_gchat_joinrequest(rm,exchange);
+ return 0;
+ }
+ return 1; /* parse failed */
+}
+
+static int AimLinksModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ char service_name[MAXMODULELABELLENGTH];
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ mir_snprintf(service_name,sizeof(service_name),"%s%s",AIM_PROTOCOL_NAME,"ParseAimLink");
+ /* or "AOL Instant Messenger Links" */
+ AssocMgr_AddNewUrlType("aim:",Translate("AIM Link Protocol"),conn.hInstance,IDI_AOL,service_name,0);
+ return 0;
+}
+
+void aim_links_init()
+{
+ char service_name[MAXMODULELABELLENGTH];
+ //LOG(LOG_DEBUG,"Links: init");
+ mir_snprintf(service_name,sizeof(service_name),"%s%s",AIM_PROTOCOL_NAME,"ParseAimLink");
+ hServiceParseLink=CreateServiceFunction(service_name,ServiceParseAimLink);
+ hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,AimLinksModulesLoaded);
+}
+
+void aim_links_destroy()
+{
+ //LOG(LOG_DEBUG,"Links: destroy");
+ UnhookEvent(hHookModulesLoaded);
+ DestroyServiceFunction(hServiceParseLink);
+}
+#endif
+
+// -----------------------------------------
+
+#ifdef ICQ_SUPPORT_TEST
+
+#define gpszICQProtoName "ICQ"
+#define IDI_ICQ 101
+#define hInst GetModuleHandleA("ICQ")
+static __inline HANDLE HContactFromUIN(DWORD dwUIN,int *Added) { dwUIN; Added; return (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); }
+static __inline HANDLE ICQFindFirstContact(void) { return NULL; }
+static __inline HANDLE ICQFindNextContact(HANDLE hContact) { hContact; return NULL; }
+static __inline void AddToCache(HANDLE hContact,DWORD dwUin) { hContact; dwUin; }
+static __inline DWORD ICQGetContactSettingUIN(HANDLE hContact) { hContact; return 0; }
+static __inline HANDLE HandleFromCacheByUin(DWORD dwUin) { dwUin; return NULL; }
+#include <m_protosvc.h>
+#include <m_addcontact.h>
+#include <m_message.h>
+typedef struct {
+ PROTOSEARCHRESULT hdr;
+ DWORD uin;
+ BYTE auth;
+ char* uid;
+} ICQSEARCHRESULT;
+
+#include "m_assocmgr.h"
+static HANDLE hHookModulesLoaded;
+static HANDLE hServiceOpenFile;
+
+static void TrimString(char *str)
+{
+ int len,start;
+ len=lstrlenA(str);
+ while(str[0]!='\0' && (unsigned char)str[len-1]<=' ') str[--len]=0;
+ for(start=0;str[start] && (unsigned char)str[start]<=' ';++start);
+ MoveMemory(str,str+start,len-start+1);
+}
+
+static BOOL IsEmpty(const char *str)
+{
+ int i;
+ for(i=0;str[i]!='\0';i++)
+ if(str[i]!=' ' && str[i]!='\r' && str[i]!='\n')
+ return FALSE;
+ return TRUE;
+}
+
+#define ICQFILE_MESSAGEUSER 1
+#define ICQFILE_ADDUSER 2
+typedef struct {
+ int type;
+ char uin[64];
+ char email[128];
+ char nick[128];
+ char firstName[64];
+ char lastName[64];
+} ICQFILEINFO;
+
+static HANDLE IsHContactFromUIN(DWORD uin)
+{
+ HANDLE hContact;
+ DWORD dwUin;
+ hContact=HandleFromCacheByUin(uin);
+ if(hContact!=NULL) return hContact;
+ hContact=ICQFindFirstContact();
+ while(hContact!=NULL) {
+ dwUin=ICQGetContactSettingUIN(hContact);
+ if(dwUin==uin) {
+ AddToCache(hContact,dwUin);
+ return hContact;
+ }
+ hContact=ICQFindNextContact(hContact);
+ }
+ return NULL;
+}
+
+static void AddIcqUser(ICQFILEINFO *info)
+{
+ ADDCONTACTSTRUCT acs;
+ ICQSEARCHRESULT psr;
+ DWORD uin;
+ /* check that uin does not belong to current user */
+ uin=atoi(info->uin);
+ if(uin!=0 && uin!=ICQGetContactSettingUIN(NULL)) {
+ /* not yet present? */
+ if(IsHContactFromUIN(uin)==NULL) {
+ acs.handleType=HANDLE_SEARCHRESULT;
+ acs.szProto=gpszICQProtoName;
+ acs.psr=(PROTOSEARCHRESULT*)&psr;
+ ZeroMemory(&psr,sizeof(ICQSEARCHRESULT));
+ psr.hdr.cbSize=sizeof(ICQSEARCHRESULT);
+ if(info->nick[0]) psr.hdr.nick=info->nick;
+ else psr.hdr.nick=info->uin;
+ if(info->email[0]) psr.hdr.email=info->email;
+ if(info->firstName[0]) psr.hdr.firstName=info->firstName;
+ if(info->lastName[0]) psr.hdr.lastName=info->lastName;
+ psr.uin=uin;
+ psr.auth=1; /* authentication needed flag */
+ psr.uid=NULL; /* icq contact */
+ CallService(MS_ADDCONTACT_SHOW,(WPARAM)NULL,(LPARAM)&acs);
+ }
+ }
+}
+
+static void MessageIcqUser(ICQFILEINFO *info)
+{
+ HANDLE hContact;
+ if(ServiceExists(MS_MSG_SENDMESSAGE)) {
+ hContact=HContactFromUIN(atoi(info->uin),NULL); /* adds the contact if needed */
+ if(hContact!=NULL)
+ CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)NULL);
+ }
+}
+
+static int IcqOpenFile(WPARAM wParam,LPARAM lParam)
+{
+ TCHAR *pszFile=(TCHAR*)lParam; /* TCHAR is specified on AssocMgr_AddNewFileTypeT() */
+ FILE *fp;
+ ICQFILEINFO info;
+ char line[4096],*sep;
+ UNREFERENCED_PARAMETER(wParam);
+ /*
+ send message:
+ [ICQ Message User]
+ UIN=1234567
+
+ add user:
+ (all the .uin files I've seen only have UIN= in them)
+ [ICQ User]
+ UIN=1234567
+ Email=
+ NickName=
+ FirstName=
+ LastName=
+ */
+ if(pszFile==NULL) return 1; /* sanity check */
+ fp=_tfopen(pszFile,_T("rt"));
+ if(fp==NULL) return 1; /* open failed */
+ info.type=0;
+ while(!feof(fp)) {
+ if(fgets(line,sizeof(line),fp)==NULL) break;
+ TrimString(line);
+ if(IsEmpty(line)) continue;
+ if(line[0]=='[') {
+ ZeroMemory(&info,sizeof(info));
+ if (!lstrcmpiA(line,"[ICQ Message User]"))
+ info.type=ICQFILE_MESSAGEUSER;
+ else if (!lstrcmpiA(line,"[ICQ User]"))
+ info.type=ICQFILE_ADDUSER;
+ continue;
+ }
+ if(info.type==0) continue;
+ sep=strchr(line,'=');
+ if(sep==NULL) { info.type=0; break; } /* format error */
+ *(sep++)='\0';
+ if (!lstrcmpA("UIN",line)) lstrcpynA(info.uin,sep,sizeof(info.uin)); /* buffer safe */
+ else if (!lstrcmpA("Email",line)) lstrcpynA(info.email,sep,sizeof(info.email)); /* buffer safe */
+ else if (!lstrcmpA("NickName",line)) lstrcpynA(info.nick,sep,sizeof(info.nick)); /* buffer safe */
+ else if (!lstrcmpA("FirstName",line)) lstrcpynA(info.firstName,sep,sizeof(info.firstName)); /* buffer safe */
+ else if (!lstrcmpA("LastName",line)) lstrcpynA(info.lastName,sep,sizeof(info.lastName)); /* buffer safe */
+ }
+ fclose(fp);
+ switch(info.type) {
+ case ICQFILE_MESSAGEUSER: MessageIcqUser(&info); return 0;
+ case ICQFILE_ADDUSER: AddIcqUser(&info); return 0;
+ default: return 1; /* open failed */
+ }
+}
+
+static int IcqFilesModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ char szService[MAX_PATH+32];
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ strcat(strcpy(szService,gpszICQProtoName),"OpenFile");
+ /* .icq files are not used, just by the ProtoLink plugin */
+ //AssocMgr_AddNewFileTypeT(".icq","application/x-icq",TranslateT("ICQ Link Shortcut"),TranslateT("&Add to Contact List..."),hInst,IDI_ICQ,szService,FTDF_BROWSERAUTOOPEN|FTDF_ISTEXT|FTDF_ISSHORTCUT|FTDF_DEFAULTDISABLED);
+ AssocMgr_AddNewFileTypeT(".uin","application/x-icq",TranslateT("ICQ Link Shortcut"),TranslateT("&Add to Contact List..."),hInst,IDI_ICQ,szService,FTDF_BROWSERAUTOOPEN|FTDF_ISTEXT|FTDF_ISSHORTCUT);
+ return 0;
+}
+
+void InitIcqFiles(void)
+{
+ char szService[MAX_PATH+32];
+ strcat(strcpy(szService,gpszICQProtoName),"OpenFile");
+ hServiceOpenFile=CreateServiceFunction(szService,IcqOpenFile);
+ hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,IcqFilesModulesLoaded);
+}
+
+void UninitIcqFiles(void)
+{
+ UnhookEvent(hHookModulesLoaded);
+ DestroyServiceFunction(hServiceOpenFile);
+}
+
+#undef hInst
+#endif
+
+// -----------------------------------------
+
+#ifdef YAHOO_SUPPORT_TEST
+
+#define yahooProtocolName "YAHOO"
+#define IDI_YAHOO 10101
+#define hinstance GetModuleHandleA("YAHOO")
+static __inline HANDLE getbuddyH(const char *yahoo_id) { yahoo_id; return NULL; }
+static __inline HANDLE add_buddy(const char *yahoo_id,const char *yahoo_name,DWORD flags) { yahoo_id; yahoo_name; flags; return (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); }
+#include <m_protosvc.h>
+#include <m_addcontact.h>
+#include <m_message.h>
+
+#include "m_assocmgr.h"
+static HANDLE hHookModulesLoaded;
+static HANDLE hServiceParseYmsgrLink;
+
+static int ServiceParseYmsgrLink(WPARAM wParam,LPARAM lParam)
+{
+ char *arg=(char*)lParam;
+ UNREFERENCED_PARAMETER(wParam);
+ if(arg==NULL) return 1; /* sanity check */
+ /*
+ add user: ymsgr:addfriend?ID
+ send message: ymsgr:sendim?ID&m=MESSAGE
+ add chatroom: ymsgr:chat?ROOM
+ */
+ /* skip leading prefix */
+ arg=strchr(arg,':');
+ if(arg==NULL) return 1; /* parse failed */
+ for (++arg;*arg=='/';++arg);
+ /* add a contact to the list */
+ if (!_strnicmp(arg,"addfriend?",10)) {
+ char *tok,*id=NULL;
+ ADDCONTACTSTRUCT acs;
+ PROTOSEARCHRESULT psr;
+ if (*(arg+=10)==0) return 1; /* parse failed */
+ tok=strtok(arg,"&"); /* first token */
+ if(tok!=NULL) id=Netlib_UrlDecode(tok);
+ if(id==NULL || *id==0) return 1; /* parse failed */
+ if(getbuddyH(id)==NULL) { /* does not yet check if id is current user */
+ acs.handleType=HANDLE_SEARCHRESULT;
+ acs.szProto=yahooProtocolName;
+ acs.psr=&psr;
+ ZeroMemory(&psr,sizeof(PROTOSEARCHRESULT));
+ psr.cbSize=sizeof(PROTOSEARCHRESULT);
+ psr.nick=id;
+ CallService(MS_ADDCONTACT_SHOW,(WPARAM)NULL,(LPARAM)&acs);
+ }
+ return 0;
+ }
+ /* send a message to a contact */
+ else if (!_strnicmp(arg,"sendim?",7)) {
+ char *tok,*id=NULL,*msg=NULL;
+ HANDLE hContact;
+ if (*(arg+=7)==0) return 1; /* parse failed */
+ tok=strtok(arg,"&"); /* first token */
+ if(tok!=NULL) id=tok;
+ while(tok!=NULL) {
+ if (!_strnicmp(tok,"m=",2) && *(tok+2)!=0)
+ msg=Netlib_UrlDecode(tok+2);
+ tok=strtok(NULL,"&"); /* next token */
+ }
+ if(id==NULL || *id==0) return 1; /* parse failed */
+ if(ServiceExists(MS_MSG_SENDMESSAGE)) { /* does not yet check if sn is current user */
+ hContact=add_buddy(id,id,PALF_TEMPORARY); /* ensure contact is on list */
+ if(hContact!=NULL)
+ CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)msg);
+ }
+ return 0;
+ }
+ /* open a chatroom */
+ else if (!_strnicmp(arg,"chat?",5)) {
+ char *tok,*rm=NULL;
+ if (*(arg+=5)==0) return 1; /* parse failed */
+ tok=strtok(arg,"&"); /* first token */
+ if(tok!=NULL) rm=Netlib_UrlDecode(tok);
+ if(rm==NULL) return 1; /* parse failed */
+ /* not yet implemented (rm contains name of chatroom)*/
+ return 0;
+ }
+ return 1; /* parse failed */
+}
+
+static int YmsgrLinksModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ char szService[MAXMODULELABELLENGTH];
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ mir_snprintf(szService,sizeof(szService),"%s%s",yahooProtocolName,"ParseYmsgrLink");
+ AssocMgr_AddNewUrlType("ymsgr:",Translate("Yahoo Link Protocol"),hinstance,IDI_YAHOO,szService,0);
+ return 0;
+}
+
+void YmsgrLinksInit(void)
+{
+ char szService[MAXMODULELABELLENGTH];
+ mir_snprintf(szService,sizeof(szService),"%s%s",yahooProtocolName,"ParseYmsgrLink");
+ hServiceParseYmsgrLink=CreateServiceFunction(szService,ServiceParseYmsgrLink);
+ hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,YmsgrLinksModulesLoaded);
+}
+
+void YmsgrLinksUninit(void)
+{
+ UnhookEvent(hHookModulesLoaded);
+ DestroyServiceFunction(hServiceParseYmsgrLink);
+}
+
+#undef hinstance
+#endif
+
+// -----------------------------------------
+
+#ifdef MSN_SUPPORT_TEST
+
+#define msnProtocolName "MSN"
+#define IDI_MSN 101
+#define hInst GetModuleHandleA("MSN")
+static __inline HANDLE MSN_HContactFromEmailT(const char *msnEmail) { msnEmail; return NULL; }
+static __inline HANDLE MSN_HContactFromEmail(const char *msnEmail,const char *msnNick,int addIfNeeded,int temporary) { msnEmail; msnNick; addIfNeeded; temporary; return (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); }
+#include <m_protosvc.h>
+#include <m_addcontact.h>
+#include <m_message.h>
+
+#include "m_assocmgr.h"
+static HANDLE hHookModulesLoaded;
+static HANDLE hServiceParseMsnimLink;
+
+static int ServiceParseMsnimLink(WPARAM wParam,LPARAM lParam)
+{
+ char *arg=(char*)lParam;
+ UNREFERENCED_PARAMETER(wParam);
+ if(arg==NULL) return 1; /* sanity check */
+ /*
+ add user: msnim:add?contact=netpassport@emailaddress.com
+ send message: msnim:chat?contact=netpassport@emailaddress.com
+ voice chat: msnim:voice?contact=netpassport@emailaddress.com
+ video chat: msnim:video?contact=netpassport@emailaddress.com
+ */
+ /* skip leading prefix */
+ arg=strchr(arg,':');
+ if(arg==NULL) return 1; /* parse failed */
+ for (++arg;*arg=='/';++arg);
+ /* add a contact to the list */
+ if (!_strnicmp(arg,"add?",4)) {
+ char *tok,*email=NULL;
+ ADDCONTACTSTRUCT acs;
+ PROTOSEARCHRESULT psr;
+ if (*(arg+=4)==0) return 1; /* parse failed */
+ tok=strtok(arg,"&"); /* first token */
+ while(tok!=NULL) {
+ if (!_strnicmp(tok,"contact=",8) && *(tok+11)!=0)
+ email=Netlib_UrlDecode(tok+11);
+ tok=strtok(NULL,"&"); /* next token */
+ }
+ if(email==NULL || *email==0) return 1; /* parse failed */
+ if(MSN_HContactFromEmailT(email)==NULL) { /* does not yet check if email is current user */
+ acs.handleType=HANDLE_SEARCHRESULT;
+ acs.szProto=AIM_PROTOCOL_NAME;
+ acs.psr=&psr;
+ ZeroMemory(&psr,sizeof(PROTOSEARCHRESULT));
+ psr.cbSize=sizeof(PROTOSEARCHRESULT);
+ psr.nick=email;
+ psr.email=email;
+ CallService(MS_ADDCONTACT_SHOW,(WPARAM)NULL,(LPARAM)&acs);
+ }
+ return 0;
+ }
+ /* send a message to a contact */
+ /* "voice" and "video" not yet implemented, perform same action as "chat" */
+ else if (!_strnicmp(arg,"chat?",5) || !_strnicmp(arg,"voice?",6) || !_strnicmp(arg,"video?",6)) {
+ char *tok,*email=NULL;
+ HANDLE hContact;
+ if (*(arg+=5)==0) return 1; /* parse failed */
+ if (*arg=='?' && *(++arg)==0) return 1; /* for "voice?" and "video?" */
+ tok=strtok(arg,"&"); /* first token */
+ while(tok!=NULL) {
+ if (!_strnicmp(tok,"contact=",8) && *(tok+11)!=0)
+ email=Netlib_UrlDecode(tok+11);
+ tok=strtok(NULL,"&"); /* next token */
+ }
+ if(email==NULL || *email==0) return 1; /* parse failed */
+ if(ServiceExists(MS_MSG_SENDMESSAGE)) {
+ hContact=MSN_HContactFromEmail(email,email,TRUE,TRUE); /* does not yet check if email is current user */
+ if(hContact!=NULL)
+ CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)NULL);
+ }
+ return 0;
+ }
+ return 1; /* parse failed */
+}
+
+static int MsnLinksModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ char szService[MAXMODULELABELLENGTH];
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ mir_snprintf(szService,sizeof(szService),"%s%s",msnProtocolName,"ParseMsnimLink");
+ AssocMgr_AddNewUrlType("msnim:",Translate("MSN Link Protocol"),hInst,IDI_MSN,szService,0);
+ return 0;
+}
+
+int LoadMsnLinks(void)
+{
+ char szService[MAXMODULELABELLENGTH];
+ mir_snprintf(szService,sizeof(szService),"%s%s",msnProtocolName,"ParseMsnimLink");
+ hServiceParseMsnimLink=CreateServiceFunction(szService,ServiceParseMsnimLink);
+ hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,MsnLinksModulesLoaded);
+ return 0;
+}
+
+void UnloadMsnLinks(void)
+{
+ UnhookEvent(hHookModulesLoaded);
+ DestroyServiceFunction(hServiceParseMsnimLink);
+}
+
+#undef hInst
+#endif
+
+// -----------------------------------------
+
+#ifdef GG_SUPPORT_TEST
+
+#define GG_PROTO "GG"
+#define IDI_GG 251
+#define hInstance GetModuleHandleA("GG")
+typedef DWORD uin_t;
+static __inline HANDLE gg_getcontact(uin_t uin,int create,int inlist,char *szNick) { uin; create; inlist; szNick; return (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); }
+#include <m_protosvc.h>
+#include <m_addcontact.h>
+#include <m_message.h>
+
+#include "m_assocmgr.h"
+static HANDLE hHookModulesLoaded;
+static HANDLE hServiceParseLink;
+
+static int ServiceParseLink(WPARAM wParam,LPARAM lParam)
+{
+ char *arg=(char*)lParam;
+ UNREFERENCED_PARAMETER(wParam);
+ if(arg==NULL) return 1; /* sanity check */
+ /* send message: gg:UID */
+ /* skip leading prefix */
+ arg=strchr(arg,':');
+ if(arg==NULL) return 1; /* parse failed */
+ for (++arg;*arg=='/';++arg);
+ /* send a message to a contact */
+ { HANDLE hContact;
+ if(ServiceExists(MS_MSG_SENDMESSAGE)) {
+ hContact=gg_getcontact(atoi(arg),TRUE,FALSE,arg);
+ if(hContact!=NULL)
+ CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)NULL);
+ }
+ }
+ return 0;
+}
+
+static int LinksModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ char szService[MAXMODULELABELLENGTH];
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ mir_snprintf(szService,sizeof(szService),"%s%s",GG_PROTO,"ParseMsnimLink");
+ AssocMgr_AddNewUrlType("gg:",Translate("Gadu-Gadu Link Protocol"),hInstance,IDI_GG,szService,0);
+ return 0;
+}
+
+void gg_registerlinks(void)
+{
+ char szService[MAXMODULELABELLENGTH];
+ mir_snprintf(szService,sizeof(szService),"%s%s",GG_PROTO,"ParseLink");
+ hServiceParseLink=CreateServiceFunction(szService,ServiceParseLink);
+ hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,LinksModulesLoaded);
+}
+
+void gg_unregisterlinks(void)
+{
+ UnhookEvent(hHookModulesLoaded);
+ DestroyServiceFunction(hServiceParseLink);
+}
+
+#undef hInstance
+#endif
+
+// -----------------------------------------
+
+#ifdef JABBER_SUPPORT_TEST
+
+#define jabberProtoName "JABBER"
+#define IDI_JABBER 102
+#define hInst GetModuleHandleA("JABBER")
+static __inline HANDLE JabberHContactFromJID(const char *jid) { jid; return NULL; }
+static __inline HANDLE JabberDBCreateContact(char *jid,char *nick,BOOL temporary,BOOL stripResource) { jid; nick; temporary; stripResource; return (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); }
+#include <m_protosvc.h>
+#include <m_addcontact.h>
+#include <m_message.h>
+
+#include "m_assocmgr.h"
+static HANDLE hHookModulesLoaded;
+static HANDLE hServiceParseXmppURI;
+
+static int ServiceParseXmppURI(WPARAM wParam,LPARAM lParam)
+{
+ char *arg=(char*)lParam;
+ char *jid;
+ UNREFERENCED_PARAMETER(wParam);
+ if(arg==NULL) return 1; /* sanity check */
+ /* skip leading prefix */
+ arg=strchr(arg,':');
+ if(arg==NULL) return 1; /* parse failed */
+ for (++arg;*arg=='/';++arg);
+ /*
+ complete specification: http://www.xmpp.org/extensions/xep-0147.html
+ send message: xmpp:JID?message;subject=TEXT&body=TEXT
+ add user: xmpp:JID?roster
+ remove user: xmpp:JID?remove
+ */
+ /* user id */
+ arg=strchr(jid=arg,'?');
+ if(arg==NULL) arg+=lstrlenA(arg); /* points to terminating nul */
+ else *(arg++)=0;
+ if (*jid==0) return 1; /* parse failed */
+ /* send a message to a contact */
+ else if (*arg==0 || (!_strnicmp(arg,"message",7) && (*(arg+7)==';' || *(arg+7)==0))) {
+ char *tok,*subj=NULL,*body=NULL;
+ HANDLE hContact;
+ char msg[1024];
+ arg+=7;
+ while(*arg==';') ++arg;
+ tok=strtok(arg,"&"); /* first token */
+ while(tok!=NULL) {
+ if (!_strnicmp(tok,"subject=",8) && *(tok+8)!=0)
+ subj=Netlib_UrlDecode(tok+8);
+ if (!_strnicmp(tok,"body=",5) && *(tok+5)!=0)
+ body=Netlib_UrlDecode(tok+5);
+ tok=strtok(NULL,"&"); /* next token */
+ }
+ if(ServiceExists(MS_MSG_SENDMESSAGE)) {
+ hContact=JabberDBCreateContact(jid,jid,TRUE,FALSE);
+ if(subj!=NULL && body!=NULL) {
+ mir_snprintf(msg,sizeof(msg),"%.128s %s",subj,body);
+ body=msg;
+ } else if(body==NULL) body=subj;
+ if(hContact!=NULL)
+ CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)body);
+ }
+ return 0;
+ }
+ /* add user to contact list */
+ else if (!_strnicmp(arg,"roster",6) && (*(arg+6)==';' || *(arg+6)==0)) {
+ ADDCONTACTSTRUCT acs;
+ PROTOSEARCHRESULT psr;
+ if(JabberHContactFromJID(jid)==NULL) { /* does not yet check if jid belongs to current user */
+ acs.handleType=HANDLE_SEARCHRESULT;
+ acs.szProto=jabberProtoName;
+ acs.psr=&psr;
+ ZeroMemory(&psr,sizeof(PROTOSEARCHRESULT));
+ psr.cbSize=sizeof(PROTOSEARCHRESULT);
+ psr.nick=jid;
+ CallService(MS_ADDCONTACT_SHOW,(WPARAM)NULL,(LPARAM)&acs);
+ }
+ return 0;
+ }
+ /* remove user from contact list */
+ else if (!_strnicmp(arg,"remove",6) && (*(arg+6)==';' || *(arg+6)==0)) {
+ HANDLE hContact;
+ hContact=JabberHContactFromJID(jid);
+ if(hContact==NULL) /* not yet implemented: show standard miranda dialog here */
+ CallService(MS_DB_CONTACT_DELETE,(WPARAM)hContact,0);
+ return 0;
+ }
+ /* add user subscription */
+ else if (!_strnicmp(arg,"subscribe",9) && (*(arg+9)==';' || *(arg+9)==0)) {
+ /* not yet implemented */
+ return 0;
+ }
+ /* remove user subscription */
+ else if (!_strnicmp(arg,"unsubscribe",11) && (*(arg+11)==';' || *(arg+11)==0)) {
+ /* not yet implemented */
+ return 0;
+ }
+ return 1; /* parse failed */
+}
+
+static int JabberLinksModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ char szService[MAXMODULELABELLENGTH];
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ mir_snprintf(szService,sizeof(szService),"%s%s",jabberProtoName,"ParseXmppURI");
+ AssocMgr_AddNewUrlType("xmpp:",Translate("Jabber Link Protocol"),hInst,IDI_JABBER,szService,0);
+ return 0;
+}
+
+int JabberLinksInit()
+{
+ char szService[MAXMODULELABELLENGTH];
+ mir_snprintf(szService,sizeof(szService),"%s%s",jabberProtoName,"ParseXmppURI");
+ hServiceParseXmppURI=CreateServiceFunction(szService,ServiceParseXmppURI);
+ hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,JabberLinksModulesLoaded);
+ return 0;
+}
+
+int JabberLinksUninit()
+{
+ UnhookEvent(hHookModulesLoaded);
+ DestroyServiceFunction(hServiceParseXmppURI);
+ return 0;
+}
+
+#undef hInst
+#endif
+
+// -----------------------------------------
+
+/*
+static HANDLE hServiceTest;
+static int TestingService(WPARAM wParam,LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ MessageBoxEx(NULL,(TCHAR*)lParam,_T("Testing Service"),MB_OK|MB_SETFOREGROUND|MB_TOPMOST|MB_TASKMODAL,LANGIDFROMLCID((LCID)CallService(MS_LANGPACK_GETLOCALE,0,0)));
+ return 0;
+}
+*/
+
+void InitTest(void)
+{
+ #ifdef AIM_SUPPORT_TEST
+ conn.hInstance=GetModuleHandleA("AIM");
+ aim_links_init();
+ #endif
+ #ifdef ICQ_SUPPORT_TEST
+ InitIcqFiles();
+ #endif
+ #ifdef YAHOO_SUPPORT_TEST
+ YmsgrLinksInit();
+ #endif
+ #ifdef MSN_SUPPORT_TEST
+ LoadMsnLinks();
+ #endif
+ #ifdef GG_SUPPORT_TEST
+ gg_registerlinks();
+ #endif
+ #ifdef JABBER_SUPPORT_TEST
+ JabberLinksInit();
+ #endif
+ //hServiceTest=CreateServiceFunction("AssocMgr/TestingService",TestingService);
+ //AssocMgr_AddNewFileTypeT(".mir",NULL,TranslateT("Miranda Installer Package (demo purpose)"),TranslateT("&Install"),hInst,IDI_MIRANDAFILE,"AssocMgr/TestingService",0);
+}
+
+void UninitTest(void)
+{
+ #ifdef AIM_SUPPORT_TEST
+ aim_links_destroy();
+ #endif
+ #ifdef ICQ_SUPPORT_TEST
+ UninitIcqFiles();
+ #endif
+ #ifdef YAHOO_SUPPORT_TEST
+ YmsgrLinksUninit();
+ #endif
+ #ifdef MSN_SUPPORT_TEST
+ UnloadMsnLinks();
+ #endif
+ #ifdef GG_SUPPORT_TEST
+ gg_unregisterlinks();
+ #endif
+ #ifdef JABBER_SUPPORT_TEST
+ JabberLinksUninit();
+ #endif
+ //DestroyServiceFunction(hServiceTest);
+}
diff --git a/plugins/AssocMgr/src/test.h b/plugins/AssocMgr/src/test.h
new file mode 100644
index 0000000000..638dea164b
--- /dev/null
+++ b/plugins/AssocMgr/src/test.h
@@ -0,0 +1,24 @@
+/*
+
+'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.
+*/
+
+/* Misc */
+void InitTest(void);
+void UninitTest(void);
diff --git a/plugins/AssocMgr/src/utils.cpp b/plugins/AssocMgr/src/utils.cpp
new file mode 100644
index 0000000000..e6414eeb02
--- /dev/null
+++ b/plugins/AssocMgr/src/utils.cpp
@@ -0,0 +1,194 @@
+/*
+
+'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"
+
+DWORD WINAPI GetModuleFileNameWorkaround(HMODULE hModule,TCHAR *pszFileName,DWORD nSize)
+{
+ /* GetModuleFileName() commonly crashes when hModule=NULL and
+ * miranda32.exe was compiled without version resource */
+ if(hModule==NULL) hModule=GetModuleHandle(NULL);
+ if(hModule==NULL) return 0;
+ return GetModuleFileName(hModule,pszFileName,nSize);
+}
+
+/************************* String Conv ****************************/
+
+// mir_free() the return value
+WCHAR* a2u(const char *pszAnsi,BOOL fMirCp)
+{
+ UINT codepage,cch;
+ WCHAR *psz;
+
+ if(pszAnsi==NULL) return NULL;
+ codepage=fMirCp?CallService(MS_LANGPACK_GETCODEPAGE,0,0):CP_ACP;
+ cch=MultiByteToWideChar(codepage,0,pszAnsi,-1,NULL,0);
+ if (!cch) return NULL;
+
+ psz=(WCHAR*)mir_alloc(cch*sizeof(WCHAR));
+ if(psz!=NULL && !MultiByteToWideChar(codepage,0,pszAnsi,-1,psz,cch)) {
+ mir_free(psz);
+ return NULL;
+ }
+ return psz;
+}
+
+// mir_free() the return value
+char* u2a(const WCHAR *pszUnicode,BOOL fMirCp)
+{
+ UINT codepage,cch;
+ char *psz;
+ DWORD flags;
+
+ if(pszUnicode==NULL) return NULL;
+ codepage=fMirCp?CallService(MS_LANGPACK_GETCODEPAGE,0,0):CP_ACP;
+ /* without WC_COMPOSITECHECK some characters might get out strange (see MS blog) */
+ cch=WideCharToMultiByte(codepage,flags=WC_COMPOSITECHECK,pszUnicode,-1,NULL,0,NULL,NULL);
+ if (!cch) cch=WideCharToMultiByte(codepage,flags=0,pszUnicode,-1,NULL,0,NULL,NULL);
+ if (!cch) return NULL;
+
+ psz=(char*)mir_alloc(cch);
+ if(psz!=NULL && !WideCharToMultiByte(codepage,flags,pszUnicode,-1,psz,cch,NULL,NULL)) {
+ mir_free(psz);
+ return NULL;
+ }
+ return psz;
+}
+
+// mir_free() the return value
+TCHAR* s2t(const void *pszStr,DWORD fUnicode,BOOL fMirCp)
+{
+
+ if(fUnicode) return mir_wstrdup((WCHAR*)pszStr);
+ return a2u((char*)pszStr,fMirCp);
+
+}
+
+// mir_free() the return value
+void* t2s(const TCHAR *pszStr,DWORD fUnicode,BOOL fMirCp)
+{
+
+ if (!fUnicode) return (void*)u2a(pszStr,fMirCp);
+ return (void*)mir_wstrdup(pszStr);
+
+}
+
+/************************* Database *******************************/
+
+struct EnumPrefixSettingsParams {
+ char **settings;
+ int nSettingsCount;
+ const char *pszPrefix;
+ int nPrefixLen;
+};
+
+static int EnumPrefixSettingsProc(const char *pszSetting,LPARAM lParam)
+{
+ struct EnumPrefixSettingsParams *param=(struct EnumPrefixSettingsParams*)lParam;
+ if (!strncmp(pszSetting,param->pszPrefix,param->nPrefixLen)) {
+ char **buf;
+ /* resize storage array */
+ buf = (char**)mir_realloc(param->settings,(param->nSettingsCount+1)*sizeof(char*));
+ if(buf!=NULL) {
+ param->settings=buf;
+ buf[param->nSettingsCount]=mir_strdup(pszSetting);
+ if(buf[param->nSettingsCount]!=NULL) ++param->nSettingsCount;
+ }
+ }
+ return 0;
+}
+
+// mir_free() the returned pSettings after use
+BOOL EnumDbPrefixSettings(const char *pszModule,const char *pszSettingPrefix,char ***pSettings,int *pnSettingsCount)
+{
+ DBCONTACTENUMSETTINGS dbces;
+ struct EnumPrefixSettingsParams param;
+ dbces.szModule=pszModule;
+ dbces.pfnEnumProc=EnumPrefixSettingsProc;
+ dbces.lParam=(LPARAM)&param;
+ param.settings=NULL;
+ param.nSettingsCount=0;
+ param.pszPrefix=pszSettingPrefix;
+ param.nPrefixLen=lstrlenA(pszSettingPrefix);
+ CallService(MS_DB_CONTACT_ENUMSETTINGS,0,(LPARAM)&dbces);
+ *pnSettingsCount=param.nSettingsCount;
+ *pSettings=param.settings;
+ return param.nSettingsCount!=0;
+}
+
+/************************* Error Output ***************************/
+
+static void MessageBoxIndirectFree(void *param)
+{
+ MSGBOXPARAMSA *mbp = (MSGBOXPARAMSA*)param;
+ MessageBoxIndirectA(mbp);
+ mir_free((char*)mbp->lpszCaption); /* does NULL check */
+ mir_free((char*)mbp->lpszText); /* does NULL check */
+ mir_free(mbp);
+}
+
+void ShowInfoMessage(BYTE flags,const char *pszTitle,const char *pszTextFmt,...)
+{
+ char szText[256]; /* max for systray */
+ MSGBOXPARAMSA *mbp;
+
+ va_list va;
+ va_start(va,pszTextFmt);
+ mir_vsnprintf(szText,SIZEOF(szText),pszTextFmt,va);
+ va_end(va);
+
+ if(ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) {
+ MIRANDASYSTRAYNOTIFY msn;
+ msn.cbSize=sizeof(msn);
+ msn.szProto=NULL;
+ msn.szInfoTitle=(char*)pszTitle;
+ msn.szInfo=(char*)szText;
+ msn.uTimeout=30000; /* max timeout */
+ msn.dwInfoFlags=flags;
+ if (!CallServiceSync(MS_CLIST_SYSTRAY_NOTIFY,0,(LPARAM)&msn))
+ return; /* success */
+ }
+
+ mbp=(MSGBOXPARAMSA*)mir_calloc(sizeof(*mbp));
+ if(mbp==NULL) return;
+ mbp->cbSize=sizeof(*mbp);
+ mbp->lpszCaption=mir_strdup(pszTitle);
+ mbp->lpszText=mir_strdup(szText);
+ mbp->dwStyle=MB_OK|MB_SETFOREGROUND|MB_TASKMODAL;
+ mbp->dwLanguageId=LANGIDFROMLCID((LCID)CallService(MS_LANGPACK_GETLOCALE,0,0));
+ switch(flags&NIIF_ICON_MASK) {
+ case NIIF_INFO: mbp->dwStyle|=MB_ICONINFORMATION; break;
+ case NIIF_WARNING: mbp->dwStyle|=MB_ICONWARNING; break;
+ case NIIF_ERROR: mbp->dwStyle|=MB_ICONERROR;
+ }
+ mir_forkthread(MessageBoxIndirectFree, mbp);
+}
+
+// LocalFree() the return value
+char* GetWinErrorDescription(DWORD dwLastError)
+{
+ char *buf=NULL;
+ DWORD flags=FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM;
+ if (!FormatMessageA(flags,NULL,dwLastError,LANGIDFROMLCID((LCID)CallService(MS_LANGPACK_GETLOCALE,0,0)),(char*)&buf,0,NULL))
+ if(GetLastError()==ERROR_RESOURCE_LANG_NOT_FOUND)
+ FormatMessageA(flags,NULL,dwLastError,0,(char*)&buf,0,NULL);
+ return buf;
+}
diff --git a/plugins/AssocMgr/src/utils.h b/plugins/AssocMgr/src/utils.h
new file mode 100644
index 0000000000..f65a29ebec
--- /dev/null
+++ b/plugins/AssocMgr/src/utils.h
@@ -0,0 +1,39 @@
+/*
+
+'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.
+*/
+
+DWORD WINAPI GetModuleFileNameWorkaround(HMODULE hModule,TCHAR *pszFileName,DWORD nSize);
+
+/* String Conv */
+WCHAR* a2u(const char *pszAnsi,BOOL fMirCp);
+char* u2a(const WCHAR *pszUnicode,BOOL fMirCp);
+TCHAR* s2t(const void *pszStr,DWORD fUnicode,BOOL fMirCp);
+void* t2s(const TCHAR *pszStr,DWORD fUnicode,BOOL fMirCp);
+
+ #define t2a(s) u2a(s,FALSE)
+ #define a2t(s) a2u(s,FALSE)
+
+
+/* Database */
+BOOL EnumDbPrefixSettings(const char *pszModule,const char *pszSettingPrefix,char ***pSettings,int *pnSettingsCount);
+
+/* Error Output */
+void ShowInfoMessage(BYTE flags,const char *pszTitle,const char *pszTextFmt,...);
+char* GetWinErrorDescription(DWORD dwLastError);
diff --git a/plugins/AssocMgr/src/version.h b/plugins/AssocMgr/src/version.h
new file mode 100644
index 0000000000..9d68defbef
--- /dev/null
+++ b/plugins/AssocMgr/src/version.h
@@ -0,0 +1,41 @@
+/*
+
+'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.
+*/
+
+#define __MAJOR_VERSION 0
+#define __MINOR_VERSION 1
+#define __RELEASE_NUM 1
+#define __BUILD_NUM 0
+
+#define __FILEVERSION_STRING __MAJOR_VERSION,__MINOR_VERSION,__RELEASE_NUM,__BUILD_NUM
+#define __FILEVERSION_DOTS __MAJOR_VERSION.__MINOR_VERSION.__RELEASE_NUM.__BUILD_NUM
+
+#define __STRINGIFY_IMPL(x) #x
+#define __STRINGIFY(x) __STRINGIFY_IMPL(x)
+#define __VERSION_STRING __STRINGIFY(__FILEVERSION_DOTS)
+
+#define __PLUGIN_NAME "File Association Manager"
+#define __INTERNAL_NAME "AssocMgr"
+#define __FILENAME "AssocMgr.dll"
+#define __DESCRIPTION "Handles file type associations and URLs like aim, ymsgr, xmpp, wpmsg, gg, tlen."
+#define __AUTHOR "H. Herkenrath"
+#define __AUTHOREMAIL "hrathh@users.sourceforge.net"
+#define __AUTHORWEB "http://addons.miranda-im.org/details.php?action=viewfile&id=3457"
+#define __COPYRIGHT "© 2005-2007 H. Herkenrath"