summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorKirill Volinsky <mataes2007@gmail.com>2012-05-19 20:07:57 +0000
committerKirill Volinsky <mataes2007@gmail.com>2012-05-19 20:07:57 +0000
commitd1cb574222bac3b31988d00481ccf7e49d8692f6 (patch)
treeb15899fdab0610dc9288b04e86ce8cf0ba87a7a0 /plugins
parenta8941afe8f3a416e126becd7f363a42c1f26cd31 (diff)
added AssocMgr
git-svn-id: http://svn.miranda-ng.org/main/trunk@86 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins')
-rw-r--r--plugins/AssocMgr/AssocMgr.sln23
-rw-r--r--plugins/AssocMgr/Info_Src.txt30
-rw-r--r--plugins/AssocMgr/License_Appendix.txt64
-rw-r--r--plugins/AssocMgr/assoclist.c1104
-rw-r--r--plugins/AssocMgr/assoclist.h33
-rw-r--r--plugins/AssocMgr/assocmgr.vcxproj162
-rw-r--r--plugins/AssocMgr/assocmgr.vcxproj.filters153
-rw-r--r--plugins/AssocMgr/common.h55
-rw-r--r--plugins/AssocMgr/dde.c293
-rw-r--r--plugins/AssocMgr/dde.h30
-rw-r--r--plugins/AssocMgr/docs/AssocMgr-Developer.txt47
-rw-r--r--plugins/AssocMgr/docs/AssocMgr-License.txt278
-rw-r--r--plugins/AssocMgr/docs/AssocMgr-Readme.txt159
-rw-r--r--plugins/AssocMgr/docs/AssocMgr-Translation.txt86
-rw-r--r--plugins/AssocMgr/main.c175
-rw-r--r--plugins/AssocMgr/reg.c1298
-rw-r--r--plugins/AssocMgr/reg.h51
-rw-r--r--plugins/AssocMgr/res/emptyfile.icobin0 -> 10134 bytes
-rw-r--r--plugins/AssocMgr/res/miranda.icobin0 -> 22486 bytes
-rw-r--r--plugins/AssocMgr/res/mirandafile.icobin0 -> 10134 bytes
-rw-r--r--plugins/AssocMgr/resource.h24
-rw-r--r--plugins/AssocMgr/resource.rc117
-rw-r--r--plugins/AssocMgr/test.c869
-rw-r--r--plugins/AssocMgr/test.h24
-rw-r--r--plugins/AssocMgr/utils.c199
-rw-r--r--plugins/AssocMgr/utils.h42
-rw-r--r--plugins/AssocMgr/version.h40
-rw-r--r--plugins/AssocMgr/version.rc51
28 files changed, 5407 insertions, 0 deletions
diff --git a/plugins/AssocMgr/AssocMgr.sln b/plugins/AssocMgr/AssocMgr.sln
new file mode 100644
index 0000000000..b6c81c1a2f
--- /dev/null
+++ b/plugins/AssocMgr/AssocMgr.sln
@@ -0,0 +1,23 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AssocMgr", "assocmgr.vcxproj", "{065889B7-478C-8E11-E93B-54A6D5A21564}"
+EndProject
+Global
+ GlobalSection(DPCodeReviewSolutionGUID) = preSolution
+ DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000}
+ EndGlobalSection
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {065889B7-478C-8E11-E93B-54A6D5A21564}.Debug|Win32.ActiveCfg = Debug|Win32
+ {065889B7-478C-8E11-E93B-54A6D5A21564}.Debug|Win32.Build.0 = Debug|Win32
+ {065889B7-478C-8E11-E93B-54A6D5A21564}.Release|Win32.ActiveCfg = Release|Win32
+ {065889B7-478C-8E11-E93B-54A6D5A21564}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/plugins/AssocMgr/Info_Src.txt b/plugins/AssocMgr/Info_Src.txt
new file mode 100644
index 0000000000..655c2f66f7
--- /dev/null
+++ b/plugins/AssocMgr/Info_Src.txt
@@ -0,0 +1,30 @@
+
+File Association Manager 0.1.1.0 for Miranda IM 0.6+
+------------------------------------------------------------------------
+ Source Code
+
+Reminder:
+'File Association Manager' is released under the terms of the
+GNU General Public License.
+See 'AssocMgr-License.txt' for more details.
+'File Association Manager' is copyright 2005-2007 by H. Herkenrath.
+
+Please notify me of any changes that improve
+'File Association Manager' or add new features.
+If you have any questions on the code, feel free
+to contact me at my email address.
+
+ H. Herkenrath (hrathh at users.sourceforge.net)
+
+
+Notes
+------------------------------------------------------------------------
+The following files need to be changed to bump the version number:
+
+Info_Src.txt (1 place)
+version.h (4 places)
+m_assocmgr.h (1 place)
+m_assocmgr.inc (1 place)
+docs\AssocMgr-Readme.txt (3 places)
+docs\AssocMgr-Translation.txt (2 places)
+docs\AssocMgr-Developer.txt (1 place)
diff --git a/plugins/AssocMgr/License_Appendix.txt b/plugins/AssocMgr/License_Appendix.txt
new file mode 100644
index 0000000000..c590c3f5fb
--- /dev/null
+++ b/plugins/AssocMgr/License_Appendix.txt
@@ -0,0 +1,64 @@
+
+Excecpt of GNU General Public License (Appendix):
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/plugins/AssocMgr/assoclist.c b/plugins/AssocMgr/assoclist.c
new file mode 100644
index 0000000000..71e6c7c34f
--- /dev/null
+++ b/plugins/AssocMgr/assoclist.c
@@ -0,0 +1,1104 @@
+/*
+
+'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 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 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 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 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 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 *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 InvokeFileHandler(const TCHAR *pszFileName)
+{
+ char *pszClassName,*pszFileExt;
+ TCHAR *p;
+ int res=CALLSERVICE_NOTFOUND;
+
+ /* find extension */
+ p=_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 InvokeUrlHandler(const TCHAR *pszUrl)
+{
+ char *pszClassName,*pszProtoPrefix,*p;
+ int 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 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);
+#if defined(_UNICODE)
+ ListView_SetUnicodeFormat(hwndList,TRUE);
+#endif
+ 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;
+#if defined(_UNICODE)
+ case WM_NOTIFYFORMAT:
+ SetWindowLong(hwndDlg,DWL_MSGRESULT,NFR_UNICODE);
+ return TRUE;
+#endif
+ 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) {
+ SetWindowLong(hwndDlg,DWL_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);
+ SetWindowLong(hwndDlg,DWL_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;
+ UNREFERENCED_PARAMETER(lParam);
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize=sizeof(odp);
+ odp.hInstance=hInst;
+ odp.pszTemplate=MAKEINTRESOURCEA(IDD_OPT_ASSOCLIST);
+ odp.position=900000100; /* network opts = 900000000 */
+ odp.ptszGroup=_T("Services"); /* autotranslated */
+ odp.ptszTitle=_T("Associations"); /* autotranslated */
+ odp.flags=ODPF_BOLDGROUPS|ODPF_EXPERTONLY|ODPF_TCHAR;
+ odp.pfnDlgProc=AssocListOptDlgProc;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&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/assoclist.h b/plugins/AssocMgr/assoclist.h
new file mode 100644
index 0000000000..8e8d5cc5da
--- /dev/null
+++ b/plugins/AssocMgr/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 InvokeFileHandler(const TCHAR *pszFileName);
+int InvokeUrlHandler(const TCHAR *pszUrl);
+/* Misc */
+void InitAssocList(void);
+void UninitAssocList(void);
diff --git a/plugins/AssocMgr/assocmgr.vcxproj b/plugins/AssocMgr/assocmgr.vcxproj
new file mode 100644
index 0000000000..780e11cdbf
--- /dev/null
+++ b/plugins/AssocMgr/assocmgr.vcxproj
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <SccProjectName />
+ <SccLocalPath />
+ <ProjectName>AssocMgr</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(SolutionDir)$(Configuration)/Plugins\</OutDir>
+ <IntDir>$(SolutionDir)$(Configuration)/Obj/$(ProjectName)\</IntDir>
+ <IgnoreImportLibrary>true</IgnoreImportLibrary>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(SolutionDir)$(Configuration)/Plugins\</OutDir>
+ <IntDir>$(SolutionDir)$(Configuration)/Obj/$(ProjectName)\</IntDir>
+ <IgnoreImportLibrary>true</IgnoreImportLibrary>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <Optimization>Disabled</Optimization>
+ <WarningLevel>Level3</WarningLevel>
+ <MinimalRebuild>true</MinimalRebuild>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <AdditionalIncludeDirectories>../../include;../ExternalAPI;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG;UNICODE;WIN32;_WINDOWS;_USRDLL;STRICT;ASSOCMGR_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ </ClCompile>
+ <Midl>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <TypeLibraryName>.\temp/Debug/Unicode\assocmgr.tlb</TypeLibraryName>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ </Midl>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>..\..\include\msapi</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake />
+ <Link>
+ <LinkDLL>true</LinkDLL>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <BaseAddress>0x24300000</BaseAddress>
+ <AdditionalDependencies>comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <StringPooling>true</StringPooling>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <Optimization>Full</Optimization>
+ <WarningLevel>Level3</WarningLevel>
+ <AdditionalIncludeDirectories>../../include;../ExternalAPI;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>NDEBUG;UNICODE;WIN32;_WINDOWS;_USRDLL;STRICT;ASSOCMGR_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ </ClCompile>
+ <Midl>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <TypeLibraryName>.\temp/Release/Unicode\assocmgr.tlb</TypeLibraryName>
+ <MkTypLibCompatible>false</MkTypLibCompatible>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ </Midl>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>..\..\include\msapi</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake />
+ <Link>
+ <LinkDLL>true</LinkDLL>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Windows</SubSystem>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <BaseAddress>0x24300000</BaseAddress>
+ <AdditionalDependencies>comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="assoclist.c" />
+ <ClCompile Include="dde.c" />
+ <ClCompile Include="main.c" />
+ <ClCompile Include="reg.c" />
+ <ClCompile Include="test.c" />
+ <ClCompile Include="utils.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="assoclist.h" />
+ <ClInclude Include="common.h" />
+ <ClInclude Include="dde.h" />
+ <ClInclude Include="m_assocmgr.h" />
+ <ClInclude Include="reg.h" />
+ <ClInclude Include="test.h" />
+ <ClInclude Include="utils.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="version.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="res\miranda.ico" />
+ <CustomBuild Include="res\mirandafile.ico" />
+ <CustomBuild Include="docs\AssocMgr-Developer.txt" />
+ <CustomBuild Include="docs\AssocMgr-License.txt" />
+ <CustomBuild Include="docs\AssocMgr-Readme.txt" />
+ <CustomBuild Include="docs\AssocMgr-Translation.txt" />
+ <CustomBuild Include="m_assocmgr.inc">
+ <FileType>Document</FileType>
+ </CustomBuild>
+ <CustomBuild Include="Info_Src.txt" />
+ <CustomBuild Include="License_Appendix.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/AssocMgr/assocmgr.vcxproj.filters b/plugins/AssocMgr/assocmgr.vcxproj.filters
new file mode 100644
index 0000000000..7cf75682ed
--- /dev/null
+++ b/plugins/AssocMgr/assocmgr.vcxproj.filters
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{deefcd93-f7b0-4946-a73e-cdbbc1d23aac}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{390c6322-88b0-42e4-8b84-73e5345e945b}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{db2d32fd-f303-490e-bc40-ca67fd6fee73}</UniqueIdentifier>
+ <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
+ </Filter>
+ <Filter Include="Documentation">
+ <UniqueIdentifier>{6f8e30e0-bc03-46e7-b039-e0fbd019afb2}</UniqueIdentifier>
+ <Extensions>txt</Extensions>
+ </Filter>
+ <Filter Include="SDK">
+ <UniqueIdentifier>{cc674aae-3e65-4f1c-959f-c95acfe7111a}</UniqueIdentifier>
+ <Extensions>*.h</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="assoclist.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="dde.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="reg.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="test.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="utils.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="assoclist.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="common.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="dde.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="m_assocmgr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="reg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="test.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="utils.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Resource Files</Filter>
+ </ClInclude>
+ <ClInclude Include="version.h">
+ <Filter>Resource Files</Filter>
+ </ClInclude>
+ <ClInclude Include="include\m_addcontact.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\m_clist.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\m_database.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\m_icq.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\m_langpack.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\m_message.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\m_options.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\m_plugins.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\m_protosvc.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\m_skin.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\m_system.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\m_utils.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\newpluginapi.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\statusmodes.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ <ClInclude Include="include\win2k.h">
+ <Filter>SDK</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="res\miranda.ico">
+ <Filter>Resource Files</Filter>
+ </CustomBuild>
+ <CustomBuild Include="res\mirandafile.ico">
+ <Filter>Resource Files</Filter>
+ </CustomBuild>
+ <CustomBuild Include="docs\AssocMgr-Developer.txt">
+ <Filter>Documentation</Filter>
+ </CustomBuild>
+ <CustomBuild Include="docs\AssocMgr-License.txt">
+ <Filter>Documentation</Filter>
+ </CustomBuild>
+ <CustomBuild Include="docs\AssocMgr-Readme.txt">
+ <Filter>Documentation</Filter>
+ </CustomBuild>
+ <CustomBuild Include="docs\AssocMgr-Translation.txt">
+ <Filter>Documentation</Filter>
+ </CustomBuild>
+ <CustomBuild Include="m_assocmgr.inc">
+ <Filter>Documentation</Filter>
+ </CustomBuild>
+ <CustomBuild Include="Info_Src.txt">
+ <Filter>Documentation</Filter>
+ </CustomBuild>
+ <CustomBuild Include="License_Appendix.txt">
+ <Filter>Documentation</Filter>
+ </CustomBuild>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/AssocMgr/common.h b/plugins/AssocMgr/common.h
new file mode 100644
index 0000000000..912f3fd9d0
--- /dev/null
+++ b/plugins/AssocMgr/common.h
@@ -0,0 +1,55 @@
+/*
+
+'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 _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>
+
+#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/dde.c b/plugins/AssocMgr/dde.c
new file mode 100644
index 0000000000..7be9f1fbcf
--- /dev/null
+++ b/plugins/AssocMgr/dde.c
@@ -0,0 +1,293 @@
+/*
+
+'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(TCHAR *pszFilePath)
+{
+ /* 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(TCHAR *pszUrl)
+{
+ /* 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)&hCommand)) {
+ #if defined(_UNICODE)
+ /* ANSI execute command can't happen for shell */
+ if(IsWindowUnicode((HWND)wParam)) {
+ #endif
+ pszCommand=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);
+ }
+ #if defined(_UNICODE)
+ }
+ #endif
+ 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)&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/dde.h b/plugins/AssocMgr/dde.h
new file mode 100644
index 0000000000..9ca8a340ae
--- /dev/null
+++ b/plugins/AssocMgr/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/docs/AssocMgr-Developer.txt b/plugins/AssocMgr/docs/AssocMgr-Developer.txt
new file mode 100644
index 0000000000..9448e6ade0
--- /dev/null
+++ b/plugins/AssocMgr/docs/AssocMgr-Developer.txt
@@ -0,0 +1,47 @@
+
+File Association Manager 0.1.1.0 for Miranda IM 0.6+
+------------------------------------------------------------------------
+ Developer Information
+
+ Contents: -------------------------------
+ | Translation, Services (API),
+ | Debug Symbols, Coding Language, Rebase Info
+
+Translation
+-----------------
+ Translation strings can be found in 'AssocMgr-Translation.txt'.
+
+Services (API)
+-----------------
+ For more information about which service functions are provided by
+ 'File Association Manager' and about how they can be used by your
+ plugin please refer to 'm_assocmgr.h".
+ If you would like to use Delphi/Pascal please refer to 'm_assocmgr.inc'.
+
+ If you have any questions or extension whishes please send me an e-mail:
+ hrathh at users.sourceforge.net
+
+Debug Symbols
+-----------------
+ Debug symbols are also available for debugging purposes.
+ Copy the PDB-files into the same directory as the
+ corresponding DLL-files.
+ To debug crashes the supplied MAP-file file might be helpful.
+
+Coding Language
+-----------------
+ 'File Association Manager' is written with Microsoft Visual C++ 6.0 SP6.
+ Used Microsoft Platform SDK: 5.2.3790.1830.15 (Svr2003 SP1).
+
+Rebase Info
+-----------------
+ 'File Association Manager' has set its base address to:
+ 0x24300000
+
+ Please avoid using this base address for your plugins because
+ it will slow down the startup of Miranda IM a little bit.
+
+ Using Microsoft Visual C++, the base address can be configured at:
+ 'Project' -> 'Settings' -> 'Linker' -> 'Output' -> 'Base Address'
+
+H. Herkenrath (hrathh at users.sourceforge.net)
diff --git a/plugins/AssocMgr/docs/AssocMgr-License.txt b/plugins/AssocMgr/docs/AssocMgr-License.txt
new file mode 100644
index 0000000000..a726a52df1
--- /dev/null
+++ b/plugins/AssocMgr/docs/AssocMgr-License.txt
@@ -0,0 +1,278 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
diff --git a/plugins/AssocMgr/docs/AssocMgr-Readme.txt b/plugins/AssocMgr/docs/AssocMgr-Readme.txt
new file mode 100644
index 0000000000..7695790522
--- /dev/null
+++ b/plugins/AssocMgr/docs/AssocMgr-Readme.txt
@@ -0,0 +1,159 @@
+
+File Association Manager 0.1.1.0
+------------------------------------------------------------------------
+ Plugin for Miranda IM 0.6 and +
+
+ Plugin Info: ----------------------------
+ | Version: 0.1.1.0
+ | Filename: assocmgr.dll
+ | Author: H. Herkenrath (hrathh at users.sourceforge.net)
+ | Description: Handles file types and URLs like aim,
+ | ymsgr, xmpp, wpmsg, gg, tlen.
+
+ Contents: -------------------------------
+ | Features, Requirements, Usage, Installation, Browsers,
+ | Bugs and Wishes, To-Do List, Version History,
+ | Thanks, Translation, License and Copyright
+
+Features
+----------------------
+ + Allows file types and URLs to be opened using Miranda IM and plugins
+ + If Miranda IM is is not running when an associated file or URL is opened
+ it gets invoked automatically
+ + You can add contacts easily from websites and forums
+ + Adds an option to start Miranda IM automatically with your Computer
+ + Offers the possibility to associate the file types only while running
+ + Provides register services for other plugins (very easy to use)
+ + Supports an unlimited amount of URLs or file types to be registered
+ + Full Unicode support
+ + All registry keys are cleanly generated and can completely be removed
+ + Keeps a backup of overwritten file extensions and restores it
+ + Backup gets restored only if it is still valid
+ + Auto-cleanup for old database settings of uninstalled plugins
+ + Automatic installation of all files, just unzip into Plugins directory
+ + Supported plugins: Database Editor++
+
+Requirements
+----------------------
+ -> Miranda IM 0.6+:
+ Miranda IM is needed in version 0.6 or later.
+
+Usage
+----------------------
+ The registered associations of 'File Association Manager' can be
+ configured here: 'Options' -> 'Services' -> 'Associations'
+ You need to have other plugins installed that actually
+ offer URL protocols or file type associations.
+ This plugin only offers the registration service, it does not
+ add any file associations on its own.
+
+Installation
+----------------------
+ Find 'miranda32.exe' on your computer.
+
+ Just copy all the contents of the zip-file as they are into the 'Plugins' directory
+ in the Miranda IM folder.
+ 'File Association Manager' will detect the files and move them into the appropriate
+ directories on it's first run.
+
+ You can also do all the installation by hand, if you want to:
+
+ Main Plugin: Copy the file 'assocmgr.dll' into the 'Plugins' subdirectory
+ in the Miranda IM folder.
+
+ The Unicode version of the plugin will only work on Windows NT/2000/XP,
+ Windows Server 2003, Windows Vista or later with an installed
+ Unicode version of Miranda IM.
+ To use it on Windows 95/98/Me, please download the ANSI version of the plugin.
+
+ Documentation: The txt-files should be moved along with the SDK-zip into the
+ 'Docs' directory in the Miranda IM folder.
+
+ That's it!
+
+Bugs and Wishes
+----------------------
+ Feel free to mail me your wishes about 'File Association Manager' and tell
+ me all the bugs you may find.
+ My email address is: hrathh at users.sourceforge.net
+
+To-Do List (random ideas)
+----------------------
+ - draw a better mirandafile icon...anyone?
+ - outsource UrlDecode() function into MS_NETLIB_URLDECODE (MS_NETLIB_URLENCODE already exits)
+ add nice wrapper Netlib_UrlDecode() as this needs to be called in each urlparser service (substrings)
+ - test using different browsers
+ - remove demo purpose code (after some time)
+ - dreaming of an installer.dll plugin that registers on
+ mir-files to unzip them...anyone?
+
+Version History
+----------------------
+ 0.1.1.0 - keeps a backup of overwritten file extensions and restores it
+ - backup only is restored if it is still valid
+ - auto-cleanup for old database settings of uninstalled plugins
+ - workaround for WinXP bug causing an scrolling glitch
+ on ListView controls with groups
+ - added FTDF_ISSHORTCUT flag for file types
+ - list gets sorted using langpack locale
+ - fixes for Win9x/NT4
+ - minor improvements
+ 0.1.0.3 - really fixed crash on options
+ - minor fixes
+ 0.1.0.2 - fixed crash on options
+ 0.1.0.1 - added v0.8 support
+ - minor improvements
+ 0.1.0.0 - Initial release
+
+Thanks
+----------------------
+ * To Jacek_FH who wrote the ProtoLink plugin which first
+ summed up all link handling, but was not extensible.
+ * To egoDust who wrote the unfinished icqwm plugin to
+ handle icq-files
+ * To rainwater for the aim links part of the AIM TOC protocol
+ * To Sergey Gershovich (Jazzy) who wrote the Autorun plugin
+
+Translation
+----------------------
+ Translation strings for language pack maintainers can be found
+ in 'AssocMgr-Translation.txt' of the SDK package.
+
+License and Copyright
+----------------------
+ 'File Association Manager'" is released under the terms of the
+ GNU General Public License.
+ See "AssocMgr-License.txt" for more details.
+
+ 'File Association Manager' is copyright 2005-2007 by H. Herkenrath.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+H. Herkenrath (hrathh at users.sourceforge.net)
diff --git a/plugins/AssocMgr/docs/AssocMgr-Translation.txt b/plugins/AssocMgr/docs/AssocMgr-Translation.txt
new file mode 100644
index 0000000000..46e992516a
--- /dev/null
+++ b/plugins/AssocMgr/docs/AssocMgr-Translation.txt
@@ -0,0 +1,86 @@
+
+File Association Manager 0.1.1.0 for Miranda IM 0.6+
+------------------------------------------------------------------------
+ Translator Information
+
+ Contents: -------------------------------
+ | General Info, German Strings
+
+
+General Info
+-----------------------------
+ 'File Association Manager' can be translated with the Miranda IM
+ language files.
+
+ Put the following strings in a file called 'langpack_<language>.txt'
+ in the Miranda IM directory and translate them into your
+ language.
+ If you need more info on Miranda IM language files visit:
+ http://miranda.svn.sourceforge.net/viewvc/*checkout*/miranda/trunk/miranda/i18n/readme.txt
+
+German Strings
+-----------------------------
+
+; --- Plugin: File Association Manager 0.1.1.0 (German) ---
+; Translation by hrathh
+; Please report any mistakes or missing strings in here.
+
+[Handles file type associations and URLs like aim, ymsgr, xmpp, wpmsg, gg, tlen.]
+Verwaltet Verknüpfungen mit Dateien und URLs wie aim, ymsgr, xmpp, wpmsg, gg, tlen.
+[File Association Manager Plugin]
+Verknüpfungsmanager-Plugin
+[The File Association Manager Plugin can not be loaded. It requires Miranda IM %s or later.]
+Das Verknüpfungsmanager-Plugin kann nicht geladen werden. Es benötigt Miranda IM %s oder höher.
+
+; Options
+;[Services]
+[Associations]
+Verknüpfungen
+[Registered associations for Miranda IM:]
+Verknüpfungen, die mit Miranda IM assoziiert sind:
+[Type]
+Typ
+[%hs files]
+%hs-Dateien
+;[%hs:]
+[Description]
+Beschreibung
+[URLs on Websites]
+URLs auf Websiten
+[File Types]
+Dateitypen
+[Miscellaneous]
+Sonstiges
+[&Start Miranda IM automatically when the Computer starts (using current profile)]
+Miranda IM automatisch bei &Systemstart ausführen (mit dem aktuellen Profil)
+[Only associate with Miranda IM while it is &running]
+Nur mit Miranda IM verknüpfen solange das Programm &ausgeführt wird
+
+; Errors
+[File Association Error]
+Verknüpfungsproblem
+[There was an error writing to the registry to modify the file/url associations.\nReason: %s]
+Es trat ein Fehler auf beim Schreiben in die Systemregistrierung. Die Datei- bzw. URL-Verknüpfungen konnten nicht verandert werden.\nGrund: %s
+[Unknown]
+Unbekannt
+[Autostart Error]
+Autostart-Fehler
+[There was an error writing to the registry to modify the autostart list.\n\nReason: %s]
+Es trat ein Fehler auf beim Schreiben in die Systemregistrierung. Der Autostarteintrag konnte nicht verandert werden.\n\nGrund: %s
+[Miranda IM could not open the file]
+Miranda IM konnte die angebene Datei nicht öffnen
+[Miranda IM was not able to open "%s".\n\nThere is no registered handler for this file type.]
+Die Datei "%s" konnte von Miranda IM nicht geöffnet werden.\n\nFür den Dateityp ist kein Handler definiert.
+[Miranda IM was not able to open "%s".\n\nThe file could not be processed.]
+Die Datei "%s" konnte von Miranda IM nicht geöffnet werden.\n\nBeim Verarbeiten der Datei trat ein Fehler auf.
+[Miranda IM could not open URL]
+Miranda IM konnte die angegebene URL nicht öffnen
+[Miranda IM was not able to open "%s".\n\nThere is no registered handler for this URL type.]
+Die URL "%s" konnte von Miranda IM nicht geöffnet werden.\n\nFur den URL-Typ ist kein Handler definiert.
+[Miranda IM was not able to open "%s".\n\nThe given URL is invalid and can not be parsed.]
+Die Datei "%s" konnte von Miranda IM nicht geöffnet werden.\n\nDie URL is ungültig und kann nicht ausgewertet werden.
+
+; ---
+
+
+H. Herkenrath (hrathh at users.sourceforge.net)
diff --git a/plugins/AssocMgr/main.c b/plugins/AssocMgr/main.c
new file mode 100644
index 0000000000..60d6a8b5a0
--- /dev/null
+++ b/plugins/AssocMgr/main.c
@@ -0,0 +1,175 @@
+/*
+
+'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;
+PLUGINLINK *pluginLink;
+struct MM_INTERFACE mmi;
+struct UTF8_INTERFACE utfi;
+static HANDLE hHookModulesLoaded;
+int hLangpack;
+
+static char szPluginInfoEmail[]=PLUGIN_EMAIL;
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+ "File Association Manager",
+ PLUGIN_VERSION,
+#if defined(_DEBUG)
+ "Development build not intended for release. ("__DATE__")", /* autotranslated */
+#else
+ "Handles file type associations and URLs like aim, ymsgr, xmpp, wpmsg, gg, tlen.", /* autotranslated */
+#endif
+ "H. Herkenrath",
+ szPluginInfoEmail, /* @ will be set later */
+ "© 2005-2007 H. Herkenrath",
+ PLUGIN_WEBSITE,
+ UNICODE_AWARE,
+ 0,
+#if defined(_UNICODE)
+ // {52685CD7-0EC7-44c1-A1A6-381612418202}
+ {0x52685cd7,0xec7,0x44c1,{0xa1,0xa6,0x38,0x16,0x12,0x41,0x82,0x2}},
+#else
+ // {48692828-D4BA-43b5-BF81-44F384811569}
+ {0x48692828,0xd4ba,0x43b5,{0xbf,0x81,0x44,0xf3,0x84,0x81,0x15,0x69}}
+#endif
+};
+static const MUUID interfaces[]={MIID_ASSOCMGR,MIID_AUTORUN,MIID_LAST};
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,void *pReserved)
+{
+ UNREFERENCED_PARAMETER(pReserved);
+ if(fdwReason==DLL_PROCESS_ATTACH) {
+ /* Do not call this from a DLL that is linked to the static C run-time library (CRT).
+ * The static CRT requires DLL_THREAD_ATTACH and DLL_THREAD_DETATCH notifications
+ * to function properly. */
+ DisableThreadLibraryCalls(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;
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+__declspec(dllexport) const PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion)
+{
+ if(mirandaVersion<PLUGIN_MAKE_VERSION(0,1,0,1)) return NULL;
+ pluginInfo.cbSize=sizeof(PLUGININFO); /* needed as v0.6 does equality check */
+ szPluginInfoEmail[PLUGIN_EMAIL_ATT_POS-1]='@'; /* email obfuscated */
+ return (PLUGININFO*)&pluginInfo; /* header is the same */
+}
+
+__declspec(dllexport) const PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ UNREFERENCED_PARAMETER(mirandaVersion);
+ pluginInfo.cbSize=sizeof(PLUGININFOEX);
+ szPluginInfoEmail[PLUGIN_EMAIL_ATT_POS-1]='@'; /* email obfuscated */
+ return &pluginInfo;
+}
+
+__declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+__declspec(dllexport) int Load(PLUGINLINK *link)
+{
+ pluginLink=link;
+ mir_getLP(&pluginInfo);
+
+ /* existance of MS_SYSTEM_GETVERSION and MS_LANGPACK_TRANSLATESTRING
+ * is checked in MirandaPluginInfo().
+ * Not placed in MirandaPluginInfo() to avoid MessageBoxes on plugin options.
+ * Using ANSI as LANG_UNICODE might not be supported. */
+ if(CallService(MS_SYSTEM_GETVERSION,0,0)<NEEDED_MIRANDA_VERSION) {
+ char szText[256];
+ mir_snprintf(szText,sizeof(szText),Translate("The File Association Manager Plugin can not be loaded. It requires Miranda IM %hs or later."),NEEDED_MIRANDA_VERSION_STR);
+ MessageBoxA(NULL,szText,Translate("File Association Manager Plugin"),MB_OK|MB_ICONINFORMATION|MB_SETFOREGROUND|MB_TOPMOST|MB_TASKMODAL);
+ return 1;
+ }
+
+ if(!ServiceExists(MS_DB_CONTACT_GETSETTING_STR)) return 1; /* dbx3x v0.5.1.0 */
+ if(mir_getMMI(&mmi)) return 1;
+ if(mir_getUTFI(&utfi)) return 1;
+ 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;
+}
+
+__declspec(dllexport) int Unload(void)
+{
+ UninitTest();
+ UninitDde();
+ UninitAssocList();
+ UnhookEvent(hHookModulesLoaded);
+ return 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/plugins/AssocMgr/reg.c b/plugins/AssocMgr/reg.c
new file mode 100644
index 0000000000..a4fadd2ef4
--- /dev/null
+++ b/plugins/AssocMgr/reg.c
@@ -0,0 +1,1298 @@
+/*
+
+'File Association Manager'-Plugin for Miranda IM
+
+Copyright (C) 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (AssocMgr-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "common.h"
+
+#ifdef _DEBUG
+/* Debug: Ensure all registry calls do succeed and have valid parameters.
+ * Shows a details message box otherwise. */
+static __inline LONG regchk(LONG res,const char *pszFunc,const void *pszInfo,BOOL fInfoUnicode,const char *pszFile,unsigned int nLine)
+{
+ if(res!=ERROR_SUCCESS && res!=ERROR_FILE_NOT_FOUND && res!=ERROR_NO_MORE_ITEMS) {
+ TCHAR szMsg[1024],*pszInfo2;
+ char *pszErr;
+ pszErr=GetWinErrorDescription(res);
+ pszInfo2=s2t(pszInfo,fInfoUnicode,FALSE); /* does NULL check */
+ mir_sntprintf(szMsg,SIZEOF(szMsg),_T("Access failed:\n%.64hs(%.128s)\n%.250hs(%u)\n%.256hs (%u)"),pszFunc,pszInfo2,pszFile,nLine,pszErr,res);
+ MessageBox(NULL,szMsg,_T("Registry Warning"),MB_OK|MB_ICONINFORMATION|MB_SETFOREGROUND|MB_TOPMOST|MB_TASKMODAL);
+ if(pszErr!=NULL) LocalFree(pszErr);
+ mir_free(pszInfo2); /* does NULL check */
+ }
+ return res;
+}
+#undef RegCloseKey
+#define RegCloseKey(hKey) \
+ regchk(RegCloseKey(hKey),"RegCloseKey",NULL,FALSE,__FILE__,__LINE__)
+#undef RegOpenKeyExA
+#define RegOpenKeyExA(hKey,szSubkey,opt,rights,phKey) \
+ regchk(RegOpenKeyExA(hKey,szSubkey,opt,rights,phKey),"RegOpenKeyExA",szSubkey,FALSE,__FILE__,__LINE__)
+#undef RegCreateKeyExA
+#define RegCreateKeyExA(hKey,szSubkey,x,y,opt,rights,sec,phKey,pDisp) \
+ regchk(RegCreateKeyExA(hKey,szSubkey,x,y,opt,rights,sec,phKey,pDisp),"RegCreateKeyExA",szSubkey,FALSE,__FILE__,__LINE__)
+#undef RegDeleteKeyA
+#define RegDeleteKeyA(hKey,szName) \
+ regchk(RegDeleteKeyA(hKey,szName),"RegDeleteKeyA",szName,FALSE,__FILE__,__LINE__)
+#undef RegSetValueExA
+#define RegSetValueExA(hSubKey,szName,x,type,pVal,size) \
+ regchk(RegSetValueExA(hSubKey,szName,x,type,pVal,size),"RegSetValueExA",szName,FALSE,__FILE__,__LINE__)
+#undef RegQueryValueExA
+#define RegQueryValueExA(hKey,szName,x,pType,pVal,pSize) \
+ regchk(RegQueryValueExA(hKey,szName,x,pType,pVal,pSize),"RegQueryValueExA",szName,FALSE,__FILE__,__LINE__)
+#undef RegQueryInfoKeyA
+#define RegQueryInfoKeyA(hKey,x,y,z,pnKeys,pnKeyLen,a,pnVals,pnNames,pnValLen,sec,pTime) \
+ regchk(RegQueryInfoKeyA(hKey,x,y,z,pnKeys,pnKeyLen,a,pnVals,pnNames,pnValLen,sec,pTime),"RegQueryInfoKeyA",NULL,FALSE,__FILE__,__LINE__)
+#undef RegEnumKeyExA
+#define RegEnumKeyExA(hKey,idx,pName,pnName,x,y,z,pTime) \
+ regchk(RegEnumKeyExA(hKey,idx,pName,pnName,x,y,z,pTime),"RegEnumKeyExA",NULL,FALSE,__FILE__,__LINE__)
+#undef RegDeleteValueA
+#define RegDeleteValueA(hKey,szName) \
+ regchk(RegDeleteValueA(hKey,szName),"RegDeleteValueA",szName,FALSE,__FILE__,__LINE__)
+#undef RegOpenKeyExW
+#define RegOpenKeyExW(hKey,szSubkey,x,sam,phKey) \
+ regchk(RegOpenKeyExW(hKey,szSubkey,x,sam,phKey),"RegOpenKeyExW",szSubkey,TRUE,__FILE__,__LINE__)
+#undef RegCreateKeyExW
+#define RegCreateKeyExW(hKey,szSubkey,x,y,z,rights,p,phKey,q) \
+ regchk(RegCreateKeyExW(hKey,szSubkey,x,y,z,rights,p,phKey,q),"RegCreateKeyExW",szSubkey,TRUE,__FILE__,__LINE__)
+#undef RegDeleteKeyW
+#define RegDeleteKeyW(hKey,szName) \
+ regchk(RegDeleteKeyW(hKey,szName),"RegDeleteKeyW",szName,TRUE,__FILE__,__LINE__)
+#undef RegSetValueExW
+#define RegSetValueExW(hSubKey,szName,x,type,pVal,size) \
+ regchk(RegSetValueExW(hSubKey,szName,x,type,pVal,size),"RegSetValueExW",szName,TRUE,__FILE__,__LINE__)
+#undef RegQueryValueExW
+#define RegQueryValueExW(hKey,szName,x,pType,pVal,pSize) \
+ regchk(RegQueryValueExW(hKey,szName,x,pType,pVal,pSize),"RegQueryValueExW",szName,TRUE,__FILE__,__LINE__)
+#undef RegQueryInfoKeyW
+#define RegQueryInfoKeyW(hKey,x,y,z,pnKeys,pnKeyLen,a,pnVals,pnNames,pnValLen,sec,pTime) \
+ regchk(RegQueryInfoKeyW(hKey,x,y,z,pnKeys,pnKeyLen,a,pnVals,pnNames,pnValLen,sec,pTime),"RegQueryInfoKeyW",NULL,TRUE,__FILE__,__LINE__)
+#undef RegEnumKeyExW
+#define RegEnumKeyExW(hKey,idx,pName,pnName,x,y,z,pTime) \
+ regchk(RegEnumKeyExW(hKey,idx,pName,pnName,x,y,z,pTime),"RegEnumKeyExW",NULL,TRUE,__FILE__,__LINE__)
+#undef RegDeleteValueW
+#define RegDeleteValueW(hKey,szName) \
+ regchk(RegDeleteValueW(hKey,szName),"RegDeleteValueW",szName,TRUE,__FILE__,__LINE__)
+#endif // _DEBUG
+
+/************************* Strings ********************************/
+
+// mir_free() the return value
+char *MakeFileClassName(const char *pszFileExt)
+{
+ char *pszClass;
+ pszClass=(char*)mir_alloc((lstrlenA(pszFileExt)+12)*sizeof(TCHAR));
+ if(pszClass!=NULL)
+ /* using correctly formated PROGID */
+ wsprintfA(pszClass,"miranda%sfile",pszFileExt); /* includes dot, buffer safe */
+ return pszClass;
+}
+
+// mir_free() the return value
+char *MakeUrlClassName(const char *pszUrl)
+{
+ char *pszClass;
+ pszClass=mir_strdup(pszUrl);
+ if(pszClass!=NULL)
+ /* remove trailing : */
+ pszClass[lstrlenA(pszClass)-1]=0;
+ return pszClass;
+}
+
+static BOOL IsFileClassName(const char *pszClassName,const char **ppszFileExt)
+{
+ *ppszFileExt=strchr(pszClassName,'.');
+ return *ppszFileExt!=NULL;
+}
+
+// mir_free() the return value
+TCHAR *MakeRunCommand(BOOL fMirExe,BOOL fFixedDbProfile)
+{
+ extern HINSTANCE hInst;
+ TCHAR szExe[MAX_PATH],*pszFmt,*pszRunCmd=NULL;
+ char szDbFile[MAX_PATH];
+ int cch;
+ if(!fFixedDbProfile || !CallService(MS_DB_GETPROFILENAME,SIZEOF(szDbFile),(LPARAM)szDbFile))
+ if(GetModuleFileNameWorkaround(fMirExe?NULL:hInst,szExe,SIZEOF(szExe))) {
+ /* db file */
+ if(!fFixedDbProfile)
+ lstrcpyA(szDbFile,"%1"); /* buffer safe */
+ /* size */
+ cch=lstrlen(szExe)+lstrlenA(szDbFile);
+ if(fMirExe) {
+ /* run command for miranda32.exe */
+ cch+=7;
+ pszFmt=_T("\"%s\" \"%hs\"");
+ } else {
+ DWORD len;
+ /* run command for rundll32.exe calling WaitForDDE */
+ cch+=28;
+ pszFmt=_T("rundll32.exe %s,WaitForDDE \"%hs\"");
+ /* ensure the command line is not too long */
+ GetShortPathName(szExe,szExe,SIZEOF(szExe));
+ /* surround by quotes if failed */
+ len=lstrlen(szExe);
+ if(_tcschr(szExe,_T(' '))!=NULL && (len+2)<SIZEOF(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(_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)) {
+ #ifdef _UNICODE
+ WriteDbBackupData(*param->ppszDbPrefix,dwType,pData,cbData);
+ #else
+ if(!(dwType&REGF_ANSI)) /* sanity check, never happens */
+ WriteDbBackupData(*param->ppszDbPrefix,dwType&REGF_ANSI,pData,cbData);
+ #endif
+ }
+ mir_free(ptszName);
+ }
+ }
+ }
+ if(res==ERROR_NO_MORE_ITEMS) res=ERROR_SUCCESS;
+ }
+ mir_free(pData); /* does NULL check */
+ /* enum subkeys */
+ if(param->level<32 && pszName!=NULL) {
+ ++param->level; /* can be max 32 levels deep (after prefix), restriction of RegCreateKeyEx() */
+ index=0;
+ while(!res) {
+ cchName=nMaxSubKeyLen+1;
+ if((res=RegEnumKeyExA(hKey,index++,pszName,&cchName,NULL,NULL,NULL,NULL))==ERROR_SUCCESS) {
+ (*param->ppszDbPrefix)[nDbPrefixLen]=0;
+ BackupRegTree_Worker(hKey,pszName,param); /* recursion */
+ }
+ }
+ }
+ if(res==ERROR_NO_MORE_ITEMS) res=ERROR_SUCCESS;
+ mir_free(pszName); /* does NULL check */
+ }
+ RegCloseKey(hKey);
+ }
+}
+
+static void BackupRegTree(HKEY hKey,const char *pszSubKey,const char *pszDbPrefix)
+{
+ struct BackupRegTreeParam param;
+ DWORD dwDbPrefixSize;
+ param.level=0;
+ param.pdwDbPrefixSize=&dwDbPrefixSize;
+ param.ppszDbPrefix=(char**)&pszDbPrefix;
+ pszDbPrefix=mir_strdup(pszDbPrefix);
+ if(pszDbPrefix!=NULL) {
+ dwDbPrefixSize=lstrlenA(pszDbPrefix)+1;
+ BackupRegTree_Worker(hKey,pszSubKey,&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(hShellKey);
+ }
+ RegCloseKey(hVerbKey);
+ }
+ 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/reg.h b/plugins/AssocMgr/reg.h
new file mode 100644
index 0000000000..98884a3224
--- /dev/null
+++ b/plugins/AssocMgr/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/res/emptyfile.ico b/plugins/AssocMgr/res/emptyfile.ico
new file mode 100644
index 0000000000..df2689ae2c
--- /dev/null
+++ b/plugins/AssocMgr/res/emptyfile.ico
Binary files differ
diff --git a/plugins/AssocMgr/res/miranda.ico b/plugins/AssocMgr/res/miranda.ico
new file mode 100644
index 0000000000..f7791c3e0b
--- /dev/null
+++ b/plugins/AssocMgr/res/miranda.ico
Binary files differ
diff --git a/plugins/AssocMgr/res/mirandafile.ico b/plugins/AssocMgr/res/mirandafile.ico
new file mode 100644
index 0000000000..2a9be258d7
--- /dev/null
+++ b/plugins/AssocMgr/res/mirandafile.ico
Binary files differ
diff --git a/plugins/AssocMgr/resource.h b/plugins/AssocMgr/resource.h
new file mode 100644
index 0000000000..2eda2da237
--- /dev/null
+++ b/plugins/AssocMgr/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/resource.rc b/plugins/AssocMgr/resource.rc
new file mode 100644
index 0000000000..8c87596f46
--- /dev/null
+++ b/plugins/AssocMgr/resource.rc
@@ -0,0 +1,117 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include <winres.h>
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Englisch (GB) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE MOVEABLE PURE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE MOVEABLE PURE
+BEGIN
+ "#include <winres.h>\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE MOVEABLE PURE
+BEGIN
+ "#include ""version.rc""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPT_ASSOCLIST DIALOGEX 0, 0, 301, 227
+STYLE DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Registered associations for Miranda IM:",
+ IDC_HEADERTEXT,3,0,295,8,SS_NOPREFIX
+ CONTROL "",IDC_ASSOCLIST,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER |
+ WS_BORDER | WS_TABSTOP,3,10,295,169
+ GROUPBOX "Miscellaneous",IDC_MISCLABEL,3,183,295,41,WS_GROUP
+ CONTROL "&Start Miranda IM automatically when the Computer starts (using current profile)",
+ IDC_AUTOSTART,"Button",BS_AUTOCHECKBOX | BS_TOP |
+ WS_TABSTOP,10,195,284,10
+ CONTROL "Only associate with Miranda IM while it is &running",
+ IDC_ONLYWHILERUNNING,"Button",BS_AUTOCHECKBOX | BS_TOP |
+ WS_TABSTOP,10,208,284,10
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_MIRANDA ICON DISCARDABLE "res/miranda.ico"
+IDI_MIRANDAFILE ICON DISCARDABLE "res/mirandafile.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_OPT_ASSOCLIST, DIALOG
+ BEGIN
+ VERTGUIDE, 3
+ VERTGUIDE, 10
+ VERTGUIDE, 294
+ VERTGUIDE, 298
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // Englisch (GB) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/plugins/AssocMgr/test.c b/plugins/AssocMgr/test.c
new file mode 100644
index 0000000000..0da578d4ec
--- /dev/null
+++ b/plugins/AssocMgr/test.c
@@ -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/test.h b/plugins/AssocMgr/test.h
new file mode 100644
index 0000000000..638dea164b
--- /dev/null
+++ b/plugins/AssocMgr/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/utils.c b/plugins/AssocMgr/utils.c
new file mode 100644
index 0000000000..d5ae8b7327
--- /dev/null
+++ b/plugins/AssocMgr/utils.c
@@ -0,0 +1,199 @@
+/*
+
+'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 defined(_UNICODE)
+ if(fUnicode) return mir_wstrdup((WCHAR*)pszStr);
+ return a2u((char*)pszStr,fMirCp);
+#else
+ if(fUnicode) return u2a((WCHAR*)pszStr,fMirCp);
+ return mir_strdup((char*)pszStr);
+#endif
+}
+
+// mir_free() the return value
+void* t2s(const TCHAR *pszStr,DWORD fUnicode,BOOL fMirCp)
+{
+#if defined(_UNICODE)
+ if(!fUnicode) return (void*)u2a(pszStr,fMirCp);
+ return (void*)mir_wstrdup(pszStr);
+#else
+ if(fUnicode) return (void*)a2u(pszStr,fMirCp);
+ return (void*)mir_strdup(pszStr);
+#endif
+}
+
+/************************* 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=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(MSGBOXPARAMSA *mbp)
+{
+ 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/utils.h b/plugins/AssocMgr/utils.h
new file mode 100644
index 0000000000..0c2682c4a8
--- /dev/null
+++ b/plugins/AssocMgr/utils.h
@@ -0,0 +1,42 @@
+/*
+
+'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);
+#ifdef _UNICODE
+ #define t2a(s) u2a(s,FALSE)
+ #define a2t(s) a2u(s,FALSE)
+#else
+ #define t2a(s) mir_strdup(s)
+ #define a2t(s) mir_strdup(s)
+#endif
+
+/* 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/version.h b/plugins/AssocMgr/version.h
new file mode 100644
index 0000000000..ce9485a55f
--- /dev/null
+++ b/plugins/AssocMgr/version.h
@@ -0,0 +1,40 @@
+/*
+
+'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 NEEDED_MIRANDA_VERSION PLUGIN_MAKE_VERSION(0,6,0,0)
+#define NEEDED_MIRANDA_VERSION_STR "0.6"
+#define PLUGIN_VERSION PLUGIN_MAKE_VERSION(0,1,1,0)
+#define FILE_VERSION 0,1,1,0
+
+#if defined(_DEBUG)
+ #define FILE_VERSION_STR "0.1.1.1 alpha"
+#else
+ #define FILE_VERSION_STR "0.1.1.0"
+#endif
+
+#define PLUGIN_EMAIL "hrathh users.sourceforge.net"
+#define PLUGIN_EMAIL_ATT_POS 7 /* position of the @-sign in the email adress above */
+
+#if defined(_UNICODE)
+ #define PLUGIN_WEBSITE "http://addons.miranda-im.org/details.php?action=viewfile&id=3458"
+#else
+ #define PLUGIN_WEBSITE "http://addons.miranda-im.org/details.php?action=viewfile&id=3457"
+#endif
diff --git a/plugins/AssocMgr/version.rc b/plugins/AssocMgr/version.rc
new file mode 100644
index 0000000000..4234d8d7ec
--- /dev/null
+++ b/plugins/AssocMgr/version.rc
@@ -0,0 +1,51 @@
+#ifndef _MAC
+
+#include "version.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION FILE_VERSION
+ PRODUCTVERSION FILE_VERSION
+ FILEFLAGSMASK 0x0L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "Comments", "Licensed under the terms of the GNU General Public License"
+ VALUE "FileDescription", "File Association Manager Plugin for Miranda IM"
+ VALUE "FileVersion", FILE_VERSION_STR
+#ifdef _UNICODE
+ VALUE "InternalName", "AssocMgr (Unicode)"
+#else
+ VALUE "InternalName", "AssocMgr"
+#endif
+ VALUE "LegalCopyright", "Copyright © 2005-2007 H. Herkenrath"
+ VALUE "OriginalFilename", "assocmgr.dll"
+ VALUE "ProductName", "File Association Manager"
+ VALUE "ProductVersion", FILE_VERSION_STR
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+
+