diff options
author | Vadim Dashevskiy <watcherhd@gmail.com> | 2012-07-20 10:43:41 +0000 |
---|---|---|
committer | Vadim Dashevskiy <watcherhd@gmail.com> | 2012-07-20 10:43:41 +0000 |
commit | 6e7980b0ad162bbb526b3d52acbc1639c2b11760 (patch) | |
tree | 3affd52d5c43ac3b06507ff56358635767949004 /plugins/Import/src | |
parent | da9f6e8a856fc87172fb0d5997c607b4a930c102 (diff) |
Import, KeyboardNotify: changed folder structure
git-svn-id: http://svn.miranda-ng.org/main/trunk@1071 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/Import/src')
-rw-r--r-- | plugins/Import/src/ICQserver.cpp | 74 | ||||
-rw-r--r-- | plugins/Import/src/ICQserver.h | 62 | ||||
-rw-r--r-- | plugins/Import/src/import.h | 108 | ||||
-rw-r--r-- | plugins/Import/src/main.cpp | 543 | ||||
-rw-r--r-- | plugins/Import/src/mirabilis.cpp | 1493 | ||||
-rw-r--r-- | plugins/Import/src/mirabilis.h | 200 | ||||
-rw-r--r-- | plugins/Import/src/miranda.cpp | 1445 | ||||
-rw-r--r-- | plugins/Import/src/mirandadb0700.h | 142 | ||||
-rw-r--r-- | plugins/Import/src/mirandahistory.cpp | 208 | ||||
-rw-r--r-- | plugins/Import/src/progress.cpp | 100 | ||||
-rw-r--r-- | plugins/Import/src/resource.h | 64 | ||||
-rw-r--r-- | plugins/Import/src/version.h | 5 | ||||
-rw-r--r-- | plugins/Import/src/wizard.cpp | 216 |
13 files changed, 4660 insertions, 0 deletions
diff --git a/plugins/Import/src/ICQserver.cpp b/plugins/Import/src/ICQserver.cpp new file mode 100644 index 0000000000..372c0cb92c --- /dev/null +++ b/plugins/Import/src/ICQserver.cpp @@ -0,0 +1,74 @@ +/*
+
+Import plugin for Miranda IM
+
+Copyright (C) 2001-2005 Martin Öberg, Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+// ==============
+// == INCLUDES ==
+// ==============
+
+#include "import.h"
+
+#include "ICQserver.h"
+#include "resource.h"
+
+// ====================
+// ====================
+// == IMPLEMENTATION ==
+// ====================
+// ====================
+
+BOOL CALLBACK ICQserverPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ SendMessage(GetParent(hdlg),WIZM_DISABLEBUTTON,0,0);
+ SendMessage(GetParent(hdlg),WIZM_ENABLEBUTTON,1,0);
+ SendMessage(GetParent(hdlg),WIZM_DISABLEBUTTON,2,0);
+ TranslateDialogDefault(hdlg);
+ ICQserverImport();
+ return TRUE;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_FINISHED,(LPARAM)FinishedPageProc);
+ break;
+ case IDCANCEL:
+ PostMessage(GetParent(hdlg),WM_CLOSE,0,0);
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static void ICQserverImport()
+{
+ // Clear last update stamp
+ DBDeleteContactSetting(NULL, szICQModuleName[ iICQAccount ], "SrvLastUpdate");
+ DBDeleteContactSetting(NULL, szICQModuleName[ iICQAccount ], "SrvRecordCount");
+
+ // Enable contacts downloading
+ DBWriteContactSettingByte(NULL, szICQModuleName[ iICQAccount ], "UseServerCList", 1);
+ DBWriteContactSettingByte(NULL, szICQModuleName[ iICQAccount ], "AddServerNew", 1);
+ DBWriteContactSettingByte(NULL, szICQModuleName[ iICQAccount ], "UseServerNicks", 1);
+ DBWriteContactSettingByte(NULL, szICQModuleName[ iICQAccount ], "ServerAddRemove", 1);
+}
diff --git a/plugins/Import/src/ICQserver.h b/plugins/Import/src/ICQserver.h new file mode 100644 index 0000000000..864890a978 --- /dev/null +++ b/plugins/Import/src/ICQserver.h @@ -0,0 +1,62 @@ +/*
+
+Import plugin for Miranda IM
+
+Copyright (C) 2001,2002,2003,2004 Martin Öberg, Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+
+
+#ifndef ICQSERVER_H
+#define ICQSERVER_H
+
+#include <windows.h>
+
+// ======================
+// == GLOBAL FUNCTIONS ==
+// ======================
+
+// =====================
+// == LOCAL FUNCTIONS ==
+// =====================
+
+// Main function
+static void ICQserverImport();
+
+// GUI callbacks
+INT_PTR CALLBACK FinishedPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+
+
+// ======================
+// == GLOBAL VARIABLES ==
+// ======================
+
+extern int cICQAccounts;
+extern char ** szICQModuleName;
+extern TCHAR ** tszICQAccountName;
+extern int iICQAccount;
+
+// =====================
+// == LOCAL VARIABLES ==
+// =====================
+
+// =============
+// == DEFINES ==
+// =============
+
+#endif
\ No newline at end of file diff --git a/plugins/Import/src/import.h b/plugins/Import/src/import.h new file mode 100644 index 0000000000..d0cb865c02 --- /dev/null +++ b/plugins/Import/src/import.h @@ -0,0 +1,108 @@ +/*
+
+Import plugin for Miranda IM
+
+Copyright (C) 2001,2002,2003,2004 Martin Öberg, Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+
+//#define _LOGGING 1
+
+#define _CRT_SECURE_NO_WARNINGS
+#define _CRT_NONSTDC_NO_DEPRECATE
+
+#define MIRANDA_VER 0x0A00
+
+#define WINVER 0x0501
+#define _WIN32_WINNT 0x0501
+#define _WIN32_IE 0x0501
+
+#include <m_stdhdr.h>
+
+#include <windows.h>
+#include <commctrl.h> // datetimepicker
+
+#include <stddef.h>
+#include <time.h>
+#include <io.h>
+
+#include <win2k.h>
+#include <newpluginapi.h>
+#include <m_langpack.h>
+#include <m_system.h>
+#include <m_database.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_protomod.h>
+#include <m_utils.h>
+#include <m_findadd.h>
+#include <m_clist.h>
+
+// ** Global constants
+
+#define IMPORT_MODULE "MIMImport" // Module name
+#define IMPORT_SERVICE "MIMImport/Import" // Service for menu item
+
+// Keys
+#define IMP_KEY_FR "FirstRun" // First run
+
+
+#define WIZM_GOTOPAGE (WM_USER+10) //wParam=resource id, lParam=dlgproc
+#define WIZM_DISABLEBUTTON (WM_USER+11) //wParam=0:back, 1:next, 2:cancel
+#define WIZM_SETCANCELTEXT (WM_USER+12) //lParam=(char*)newText
+#define WIZM_ENABLEBUTTON (WM_USER+13) //wParam=0:back, 1:next, 2:cancel
+
+#define PROGM_SETPROGRESS (WM_USER+10) //wParam=0..100
+#define PROGM_ADDMESSAGE (WM_USER+11) //lParam=(char*)szText
+#define SetProgress(n) SendMessage(hdlgProgress,PROGM_SETPROGRESS,n,0)
+
+#define ICQOSCPROTONAME "ICQ"
+#define MSNPROTONAME "MSN"
+#define YAHOOPROTONAME "YAHOO"
+#define NSPPROTONAME "NET_SEND"
+#define ICQCORPPROTONAME "ICQ Corp"
+#define AIMPROTONAME "AIM"
+
+// Import type
+#define IMPORT_CONTACTS 0
+#define IMPORT_ALL 1
+#define IMPORT_CUSTOM 2
+
+// Custom import options
+#define IOPT_ADDUNKNOWN 1
+#define IOPT_MSGSENT 2
+#define IOPT_MSGRECV 4
+#define IOPT_URLSENT 8
+#define IOPT_URLRECV 16
+#define IOPT_AUTHREQ 32
+#define IOPT_ADDED 64
+#define IOPT_FILESENT 128
+#define IOPT_FILERECV 256
+#define IOPT_OTHERSENT 512
+#define IOPT_OTHERRECV 1024
+#define IOPT_SYSTEM 2048
+#define IOPT_CONTACTS 4096
+#define IOPT_GROUPS 8192
+
+void AddMessage( const char* fmt, ... );
+
+int CreateGroup(BYTE type, const char* name, HANDLE hContact);
+
+extern HWND hdlgProgress;
+
+extern DWORD nDupes, nContactsCount, nMessagesCount, nGroupsCount, nSkippedEvents, nSkippedContacts;
diff --git a/plugins/Import/src/main.cpp b/plugins/Import/src/main.cpp new file mode 100644 index 0000000000..8b7524c040 --- /dev/null +++ b/plugins/Import/src/main.cpp @@ -0,0 +1,543 @@ +/*
+
+Import plugin for Miranda IM
+
+Copyright (C) 2001-2005 Martin Öberg, Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "import.h"
+#include "version.h"
+#include "resource.h"
+
+void FreeVariant( DBVARIANT* dbv );
+void WriteVariant( HANDLE hContact, const char* module, const char* var, DBVARIANT* dbv );
+
+BOOL IsDuplicateEvent(HANDLE hContact, DBEVENTINFO dbei);
+
+
+int nImportOption;
+int nCustomOptions;
+
+int cICQAccounts = 0;
+char ** szICQModuleName = NULL;
+TCHAR ** tszICQAccountName = NULL;
+int iICQAccount = 0;
+
+static HANDLE hHookModulesLoaded = NULL;
+static HANDLE hHookOnExit = NULL;
+static HANDLE hImportService = NULL;
+
+
+INT_PTR CALLBACK WizardDlgProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+
+HINSTANCE hInst;
+
+static HWND hwndWizard = NULL;
+int hLangpack;
+
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+ "Import contacts and messages",
+ __VERSION_DWORD,
+ "Imports contacts and messages from Mirabilis ICQ and Miranda IM.",
+ "Miranda team",
+ "info@miranda-im.org",
+ "© 2000-2010 Martin Öberg, Richard Hughes, Dmitry Kuzkin, George Hazan",
+ "http://www.miranda-im.org",
+ UNICODE_AWARE, //{2D77A746-00A6-4343-BFC5-F808CDD772EA}
+ {0x2d77a746, 0xa6, 0x4343, { 0xbf, 0xc5, 0xf8, 0x8, 0xcd, 0xd7, 0x72, 0xea }}
+};
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
+{
+ hInst = hinstDLL;
+ return TRUE;
+}
+
+static INT_PTR ImportCommand(WPARAM wParam,LPARAM lParam)
+{
+ if (IsWindow(hwndWizard)) {
+ SetForegroundWindow(hwndWizard);
+ SetFocus(hwndWizard);
+ }
+ else hwndWizard = CreateDialog(hInst, MAKEINTRESOURCE(IDD_WIZARD), NULL, WizardDlgProc);
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// MirandaPluginInfoEx - returns an information about a plugin
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// MirandaInterfaces - returns the protocol interface to the core
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_IMPORT, MIID_LAST};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Performs a primary set of actions upon plugin loading
+
+static int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ int nProtocols = 0;
+ int n;
+ PROTOCOLDESCRIPTOR **ppProtos = NULL;
+
+ if (DBGetContactSettingByte(NULL, IMPORT_MODULE, IMP_KEY_FR, 0))
+ return 0;
+
+ // Only autorun import wizard if at least one protocol is installed
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&nProtocols, (LPARAM)&ppProtos);
+ for (n=0; n < nProtocols; n++) {
+ if (ppProtos[n]->type == PROTOTYPE_PROTOCOL) {
+ CallService(IMPORT_SERVICE, 0, 0);
+ DBWriteContactSettingByte(NULL, IMPORT_MODULE, IMP_KEY_FR, 1);
+ break;
+ } }
+ return 0;
+}
+
+static int OnExit(WPARAM wParam, LPARAM lParam)
+{
+ if ( hwndWizard )
+ SendMessage(hwndWizard, WM_CLOSE, 0, 0);
+ return 0;
+}
+
+extern "C" __declspec(dllexport) int Load(void)
+{
+ mir_getLP( &pluginInfo );
+
+ hImportService = CreateServiceFunction(IMPORT_SERVICE, ImportCommand);
+ {
+ CLISTMENUITEM mi;
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+ mi.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_IMPORT));
+ mi.pszName = LPGEN("&Import...");
+ mi.position = 500050000;
+ mi.pszService = IMPORT_SERVICE;
+ Menu_AddMainMenuItem(&mi);
+ }
+ hHookModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ hHookOnExit = HookEvent(ME_SYSTEM_OKTOEXIT, OnExit);
+ {
+ INITCOMMONCONTROLSEX icex;
+ icex.dwSize = sizeof(icex);
+ icex.dwICC = ICC_DATE_CLASSES;
+ InitCommonControlsEx(&icex);
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Unload a plugin
+
+extern "C" __declspec(dllexport) int Unload(void)
+{
+ if (hHookModulesLoaded)
+ UnhookEvent(hHookModulesLoaded);
+ if (hHookOnExit)
+ UnhookEvent(hHookOnExit);
+ if (hImportService)
+ DestroyServiceFunction(hImportService);
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+BOOL IsProtocolLoaded(char* pszProtocolName)
+{
+ return CallService(MS_PROTO_ISPROTOCOLLOADED, 0, (LPARAM)pszProtocolName) ? TRUE : FALSE;
+}
+
+BOOL EnumICQAccounts()
+{
+ int count, i = 0;
+ PROTOACCOUNT ** accs;
+
+ while (cICQAccounts)
+ {
+ cICQAccounts--;
+ free(szICQModuleName[cICQAccounts]);
+ free(tszICQAccountName[cICQAccounts]);
+ }
+
+ ProtoEnumAccounts(&count, &accs);
+ szICQModuleName = (char**)realloc(szICQModuleName, count * sizeof(char**));
+ tszICQAccountName = (TCHAR**)realloc(tszICQAccountName, count * sizeof(TCHAR**));
+ while (i < count)
+ {
+ if ((0 == strcmp(ICQOSCPROTONAME, accs[i]->szProtoName)) && accs[i]->bIsEnabled)
+ {
+ szICQModuleName[cICQAccounts] = strdup(accs[i]->szModuleName);
+ tszICQAccountName[cICQAccounts] = _tcsdup(accs[i]->tszAccountName);
+ cICQAccounts++;
+ }
+ i++;
+ }
+ return cICQAccounts != 0;
+}
+
+void FreeICQAccountsList()
+{
+ while (cICQAccounts)
+ {
+ cICQAccounts--;
+ free(szICQModuleName[cICQAccounts]);
+ free(tszICQAccountName[cICQAccounts]);
+ }
+
+ if (szICQModuleName)
+ free(szICQModuleName);
+ if (tszICQAccountName)
+ free(tszICQAccountName);
+
+ szICQModuleName = NULL;
+ tszICQAccountName = NULL;
+}
+
+HANDLE HContactFromNumericID(char* pszProtoName, char* pszSetting, DWORD dwID)
+{
+ char* szProto;
+ HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact != NULL)
+ {
+ if (DBGetContactSettingDword(hContact, pszProtoName, pszSetting, 0) == dwID)
+ {
+ szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (szProto != NULL && !strcmp(szProto, pszProtoName))
+ return hContact;
+ }
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+ return INVALID_HANDLE_VALUE;
+}
+
+HANDLE HContactFromID(char* pszProtoName, char* pszSetting, char* pszID)
+{
+ DBVARIANT dbv;
+ HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact != NULL) {
+ char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if ( !lstrcmpA(szProto, pszProtoName)) {
+ if (DBGetContactSettingString(hContact, pszProtoName, pszSetting, &dbv) == 0) {
+ if (strcmp(pszID, dbv.pszVal) == 0) {
+ DBFreeVariant(&dbv);
+ return hContact;
+ }
+ DBFreeVariant(&dbv);
+ }
+ }
+
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+ return INVALID_HANDLE_VALUE;
+}
+
+HANDLE HistoryImportFindContact(HWND hdlgProgress, char* szModuleName, DWORD uin, int addUnknown)
+{
+ HANDLE hContact = HContactFromNumericID(szModuleName, "UIN", uin);
+ if (hContact == NULL) {
+ AddMessage( LPGEN("Ignored event from/to self"));
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (hContact != INVALID_HANDLE_VALUE)
+ return hContact;
+
+ if (!addUnknown)
+ return INVALID_HANDLE_VALUE;
+
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_ADD, 0, 0);
+ CallService(MS_PROTO_ADDTOCONTACT, (WPARAM)hContact, (LPARAM)szModuleName);
+ DBWriteContactSettingDword(hContact, szModuleName, "UIN", uin);
+ AddMessage( LPGEN("Added contact %u (found in history)"), uin );
+ return hContact;
+}
+
+HANDLE AddContact(HWND hdlgProgress, char* pszProtoName, char* pszUniqueSetting,
+ DBVARIANT* id, DBVARIANT* nick, DBVARIANT* group)
+{
+ HANDLE hContact;
+ char szid[ 40 ];
+ char* pszUserID = ( id->type == DBVT_DWORD ) ? _ltoa( id->dVal, szid, 10 ) : id->pszVal;
+
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_ADD, 0, 0);
+ if ( CallService(MS_PROTO_ADDTOCONTACT, (WPARAM)hContact, (LPARAM)pszProtoName) != 0) {
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0);
+ AddMessage( LPGEN("Failed to add %s contact %s"), pszProtoName, pszUserID );
+ FreeVariant( id );
+ FreeVariant( nick );
+ FreeVariant( group );
+ return INVALID_HANDLE_VALUE;
+ }
+
+ WriteVariant( hContact, pszProtoName, pszUniqueSetting, id );
+
+ if ( group->type )
+ CreateGroup( group->type, group->pszVal, hContact );
+
+ if ( nick->type && nick->pszVal[0] ) {
+ WriteVariant( hContact, "CList", "MyHandle", nick );
+ if (nick->type == DBVT_UTF8) {
+ char *tmp = mir_utf8decodeA(nick->pszVal);
+ AddMessage( LPGEN("Added %s contact %s, '%s'"), pszProtoName, pszUserID, tmp );
+ mir_free(tmp);
+ }
+ else AddMessage( LPGEN("Added %s contact %s, '%s'"), pszProtoName, pszUserID, nick->pszVal );
+ }
+ else AddMessage( LPGEN("Added %s contact %s"), pszProtoName, pszUserID );
+
+ FreeVariant( id );
+ FreeVariant( nick );
+ FreeVariant( group );
+ return hContact;
+}
+
+// ------------------------------------------------
+// Creates a group with a specified name in the
+// Miranda contact list.
+// If contact is specified adds it to group
+// ------------------------------------------------
+// Returns 1 if successful and 0 when it fails.
+int CreateGroup(BYTE type, const char* name, HANDLE hContact)
+{
+ int groupId;
+ TCHAR *tmp, *tszGrpName;
+ char groupIdStr[11];
+ size_t cbName;
+
+ if (type == DBVT_UTF8)
+ tmp = mir_utf8decodeT( name );
+ else if (type == DBVT_WCHAR)
+ tmp = mir_u2t(( wchar_t* )name );
+ else
+ tmp = mir_a2t( name );
+
+ if ( tmp == NULL )
+ return 0;
+
+ cbName = _tcslen(tmp);
+ tszGrpName = (TCHAR*)_alloca(( cbName+2 )*sizeof( TCHAR ));
+ tszGrpName[0] = 1 | GROUPF_EXPANDED;
+ _tcscpy( tszGrpName+1, tmp );
+ mir_free( tmp );
+
+ // Check for duplicate & find unused id
+ for (groupId = 0; ; groupId++) {
+ DBVARIANT dbv;
+ itoa(groupId, groupIdStr,10);
+ if (DBGetContactSettingTString(NULL, "CListGroups", groupIdStr, &dbv))
+ break;
+
+ if ( !lstrcmp(dbv.ptszVal + 1, tszGrpName + 1 )) {
+ if (hContact)
+ DBWriteContactSettingTString( hContact, "CList", "Group", tszGrpName+1 );
+ else {
+ char *str = mir_t2a(tszGrpName + 1);
+ AddMessage( LPGEN("Skipping duplicate group %s."), str);
+ mir_free(str);
+ }
+
+ DBFreeVariant(&dbv);
+ return 0;
+ }
+
+ DBFreeVariant(&dbv);
+ }
+
+ DBWriteContactSettingTString( NULL, "CListGroups", groupIdStr, tszGrpName );
+
+ if (hContact)
+ DBWriteContactSettingTString( hContact, "CList", "Group", tszGrpName+1 );
+
+ return 1;
+}
+
+// Returns TRUE if the event already exist in the database
+BOOL IsDuplicateEvent(HANDLE hContact, DBEVENTINFO dbei)
+{
+ static DWORD dwPreviousTimeStamp = -1;
+ static HANDLE hPreviousContact = INVALID_HANDLE_VALUE;
+ static HANDLE hPreviousDbEvent = NULL;
+
+ HANDLE hExistingDbEvent;
+ DWORD dwEventTimeStamp;
+ DBEVENTINFO dbeiExisting;
+
+ // get last event
+ if (!(hExistingDbEvent = (HANDLE)CallService(MS_DB_EVENT_FINDLAST, (WPARAM)hContact, 0)))
+ return FALSE;
+
+ ZeroMemory(&dbeiExisting, sizeof(dbeiExisting));
+ dbeiExisting.cbSize = sizeof(dbeiExisting);
+ CallService(MS_DB_EVENT_GET, (WPARAM)hExistingDbEvent, (LPARAM)&dbeiExisting);
+ dwEventTimeStamp = dbeiExisting.timestamp;
+
+ // compare with last timestamp
+ if (dbei.timestamp > dwEventTimeStamp)
+ {
+ // remember event
+ hPreviousDbEvent = hExistingDbEvent;
+ dwPreviousTimeStamp = dwEventTimeStamp;
+ return FALSE;
+ }
+
+ if (hContact != hPreviousContact)
+ {
+ hPreviousContact = hContact;
+ // remember event
+ hPreviousDbEvent = hExistingDbEvent;
+ dwPreviousTimeStamp = dwEventTimeStamp;
+
+ // get first event
+ if (!(hExistingDbEvent = (HANDLE)CallService(MS_DB_EVENT_FINDFIRST, (WPARAM)hContact, 0)))
+ return FALSE;
+
+ ZeroMemory(&dbeiExisting, sizeof(dbeiExisting));
+ dbeiExisting.cbSize = sizeof(dbeiExisting);
+ CallService(MS_DB_EVENT_GET, (WPARAM)hExistingDbEvent, (LPARAM)&dbeiExisting);
+ dwEventTimeStamp = dbeiExisting.timestamp;
+
+ // compare with first timestamp
+ if (dbei.timestamp <= dwEventTimeStamp)
+ {
+ // remember event
+ dwPreviousTimeStamp = dwEventTimeStamp;
+ hPreviousDbEvent = hExistingDbEvent;
+
+ if ( dbei.timestamp != dwEventTimeStamp )
+ return FALSE;
+ }
+
+ }
+ // check for equal timestamps
+ if (dbei.timestamp == dwPreviousTimeStamp)
+ {
+ ZeroMemory(&dbeiExisting, sizeof(dbeiExisting));
+ dbeiExisting.cbSize = sizeof(dbeiExisting);
+ CallService(MS_DB_EVENT_GET, (WPARAM)hPreviousDbEvent, (LPARAM)&dbeiExisting);
+
+ if ((dbei.timestamp == dbeiExisting.timestamp) &&
+ (dbei.eventType == dbeiExisting.eventType) &&
+ (dbei.cbBlob == dbeiExisting.cbBlob) &&
+ ((dbei.flags&DBEF_SENT) == (dbeiExisting.flags&DBEF_SENT)))
+ return TRUE;
+
+ // find event with another timestamp
+ hExistingDbEvent = (HANDLE)CallService(MS_DB_EVENT_FINDNEXT, (WPARAM)hPreviousDbEvent, 0);
+ while (hExistingDbEvent != NULL)
+ {
+ ZeroMemory(&dbeiExisting, sizeof(dbeiExisting));
+ dbeiExisting.cbSize = sizeof(dbeiExisting);
+ CallService(MS_DB_EVENT_GET, (WPARAM)hExistingDbEvent, (LPARAM)&dbeiExisting);
+
+ if (dbeiExisting.timestamp != dwPreviousTimeStamp)
+ {
+ // use found event
+ hPreviousDbEvent = hExistingDbEvent;
+ dwPreviousTimeStamp = dbeiExisting.timestamp;
+ break;
+ }
+
+ hPreviousDbEvent = hExistingDbEvent;
+ hExistingDbEvent = (HANDLE)CallService(MS_DB_EVENT_FINDNEXT, (WPARAM)hExistingDbEvent, 0);
+ }
+ }
+
+ hExistingDbEvent = hPreviousDbEvent;
+
+ if (dbei.timestamp <= dwPreviousTimeStamp)
+ {
+ // look back
+ while (hExistingDbEvent != NULL)
+ {
+ ZeroMemory(&dbeiExisting, sizeof(dbeiExisting));
+ dbeiExisting.cbSize = sizeof(dbeiExisting);
+ CallService(MS_DB_EVENT_GET, (WPARAM)hExistingDbEvent, (LPARAM)&dbeiExisting);
+
+ if (dbei.timestamp > dbeiExisting.timestamp)
+ {
+ // remember event
+ hPreviousDbEvent = hExistingDbEvent;
+ dwPreviousTimeStamp = dbeiExisting.timestamp;
+ return FALSE;
+ }
+
+ // Compare event with import candidate
+ if ((dbei.timestamp == dbeiExisting.timestamp) &&
+ (dbei.eventType == dbeiExisting.eventType) &&
+ (dbei.cbBlob == dbeiExisting.cbBlob) &&
+ ((dbei.flags&DBEF_SENT) == (dbeiExisting.flags&DBEF_SENT)))
+ {
+ // remember event
+ hPreviousDbEvent = hExistingDbEvent;
+ dwPreviousTimeStamp = dbeiExisting.timestamp;
+ return TRUE;
+ }
+
+ // Get previous event in chain
+ hExistingDbEvent = (HANDLE)CallService(MS_DB_EVENT_FINDPREV, (WPARAM)hExistingDbEvent, 0);
+ }
+
+ }
+ else
+ {
+ // look forward
+ while (hExistingDbEvent != NULL)
+ {
+ ZeroMemory(&dbeiExisting, sizeof(dbeiExisting));
+ dbeiExisting.cbSize = sizeof(dbeiExisting);
+ CallService(MS_DB_EVENT_GET, (WPARAM)hExistingDbEvent, (LPARAM)&dbeiExisting);
+
+ if (dbei.timestamp < dbeiExisting.timestamp)
+ {
+ // remember event
+ hPreviousDbEvent = hExistingDbEvent;
+ dwPreviousTimeStamp = dbeiExisting.timestamp;
+ return FALSE;
+ }
+
+ // Compare event with import candidate
+ if ((dbei.timestamp == dbeiExisting.timestamp) &&
+ (dbei.eventType == dbeiExisting.eventType) &&
+ (dbei.cbBlob == dbeiExisting.cbBlob) &&
+ ((dbei.flags&DBEF_SENT) == (dbeiExisting.flags&DBEF_SENT)))
+ {
+ // remember event
+ hPreviousDbEvent = hExistingDbEvent;
+ dwPreviousTimeStamp = dbeiExisting.timestamp;
+ return TRUE;
+ }
+
+ // Get next event in chain
+ hExistingDbEvent = (HANDLE)CallService(MS_DB_EVENT_FINDNEXT, (WPARAM)hExistingDbEvent, 0);
+ }
+
+ }
+ // reset last event
+ hPreviousContact = INVALID_HANDLE_VALUE;
+ return FALSE;
+}
diff --git a/plugins/Import/src/mirabilis.cpp b/plugins/Import/src/mirabilis.cpp new file mode 100644 index 0000000000..1f032827af --- /dev/null +++ b/plugins/Import/src/mirabilis.cpp @@ -0,0 +1,1493 @@ +/*
+
+Import plugin for Miranda IM
+
+Copyright (C) 2001-2005 Martin Öberg, Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+// ==============
+// == INCLUDES ==
+// ==============
+
+#include "import.h"
+#include "mirabilis.h"
+#include "resource.h"
+
+BOOL IsDuplicateEvent(HANDLE hContact, DBEVENTINFO dbei);
+BOOL IsProtocolLoaded(char* pszProtocolName);
+HANDLE HContactFromNumericID(char* pszProtoName, char* pszSetting, DWORD dwID);
+HANDLE AddContact(HWND hdlgProgress, char* pszProtoName, char* pszUniqueSetting, DBVARIANT* id, DBVARIANT* nick, DBVARIANT* group);
+
+// ====================
+// ====================
+// == IMPLEMENTATION ==
+// ====================
+// ====================
+
+static void SearchForDatabases(HWND hdlg, const TCHAR *dbPath, const TCHAR *type)
+{
+ HANDLE hFind;
+ WIN32_FIND_DATA fd;
+ TCHAR szSearchPath[MAX_PATH];
+ TCHAR szRootName[MAX_PATH],*str2;
+
+ int i;
+
+ wsprintf(szSearchPath, _T("%s\\*.idx"), dbPath);
+ hFind=FindFirstFile(szSearchPath,&fd);
+ if(hFind!=INVALID_HANDLE_VALUE) {
+ do {
+ lstrcpy(szRootName,fd.cFileName);
+ str2=_tcsrchr(szRootName,'.');
+ if(str2!=NULL) *str2=0;
+ if(lstrlen(szRootName)>3 && !lstrcmpi(szRootName+lstrlen(szRootName)-3,_T("tmp")))
+ continue;
+ lstrcat(szRootName,type);
+ i=SendDlgItemMessage(hdlg,IDC_LIST,LB_ADDSTRING,0,(LPARAM)szRootName);
+ str2 = (TCHAR*)mir_alloc((lstrlen(dbPath) + 2+lstrlen(fd.cFileName))*sizeof(TCHAR));
+ wsprintf(str2, _T("%s\\%s"), dbPath, fd.cFileName);
+ SendDlgItemMessage(hdlg,IDC_LIST,LB_SETITEMDATA,i,(LPARAM)str2);
+ }
+ while( FindNextFile( hFind, &fd ));
+
+ FindClose(hFind);
+ }
+}
+
+INT_PTR CALLBACK MirabilisPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ {
+ HKEY hKey;
+ LONG lResult;
+ int i;
+ TranslateDialogDefault(hdlg);
+ if (ERROR_SUCCESS != (lResult = RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\Mirabilis\\ICQ\\DefaultPrefs"), 0, KEY_QUERY_VALUE, &hKey)))
+ lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Mirabilis\\ICQ\\DefaultPrefs"), 0, KEY_QUERY_VALUE, &hKey);
+
+ if (lResult == ERROR_SUCCESS) {
+ TCHAR dbPath[MAX_PATH];
+ DWORD cch;
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("New Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (99a)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("99b Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (99b)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("2000a Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (2000a)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("2000b Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (2000b)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("2001a Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (2001a)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("2001b Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (2001b)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("2002a Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (2002a)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("2003a Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (2003a)"));
+ }
+
+ for (i = 0; i < cICQAccounts; i++)
+ {
+ SendDlgItemMessage(hdlg, IDC_MIRABILISACCOUNT, CB_ADDSTRING, 0, (LPARAM)tszICQAccountName[i]);
+ }
+ SendDlgItemMessage(hdlg, IDC_MIRABILISACCOUNT, CB_SETCURSEL, 0, 0);
+
+ SetTimer(hdlg,1,2000,NULL);
+ SendMessage(hdlg,WM_TIMER,0,0);
+ return TRUE;
+ }
+ case WM_TIMER:
+ { HANDLE hMirabilisMutex;
+ hMirabilisMutex=OpenMutexA(MUTEX_ALL_ACCESS,FALSE,"Mirabilis ICQ Mutex");
+ if(hMirabilisMutex!=NULL) {
+ CloseHandle(hMirabilisMutex);
+ ShowWindow(GetDlgItem(hdlg,IDC_MIRABILISRUNNING),SW_SHOW);
+ }
+ else ShowWindow(GetDlgItem(hdlg,IDC_MIRABILISRUNNING),SW_HIDE);
+ }
+ break;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDC_BACK:
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_IMPORTTYPE,(LPARAM)ImportTypePageProc);
+ break;
+ case IDOK:
+ { TCHAR filename[MAX_PATH];
+ GetDlgItemText(hdlg,IDC_FILENAME,filename,SIZEOF(filename));
+ if(_taccess(filename,4)) {
+ MessageBox(hdlg,TranslateT("The given file does not exist. Please check that you have entered the name correctly."),TranslateT("Mirabilis Import"),MB_OK);
+ break;
+ }
+ lstrcpy(importFile,filename);
+ iICQAccount = SendDlgItemMessage(hdlg, IDC_MIRABILISACCOUNT, CB_GETCURSEL, 0, 0);
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_OPTIONS,(LPARAM)MirabilisOptionsPageProc);
+ break;
+ }
+ case IDCANCEL:
+ PostMessage(GetParent(hdlg),WM_CLOSE,0,0);
+ break;
+ case IDC_LIST:
+ if(HIWORD(wParam)==LBN_SELCHANGE) {
+ int sel=SendDlgItemMessage(hdlg,IDC_LIST,LB_GETCURSEL,0,0);
+ if(sel==LB_ERR) break;
+ SetDlgItemText(hdlg,IDC_FILENAME,(TCHAR*)SendDlgItemMessage(hdlg,IDC_LIST,LB_GETITEMDATA,sel,0));
+ }
+ break;
+ case IDC_OTHER:
+ { OPENFILENAME ofn;
+ TCHAR str[MAX_PATH], text[256];
+ int index;
+
+ // TranslateTS doesnt translate \0 separated strings
+ index = mir_sntprintf(text, 64, _T("%s (*.idx)"), TranslateT("Mirabilis ICQ database indexes")) + 1;
+ _tcscpy(text + index, _T("*.idx")); index += 6;
+ index += mir_sntprintf(text + index, 64, _T("%s (*.*)"), TranslateT("All Files")) + 1;
+ _tcscpy(text + index, _T("*.*")); index += 4;
+ text[index] = 0;
+
+ GetDlgItemText(hdlg,IDC_FILENAME,str,SIZEOF(str));
+ ZeroMemory(&ofn, sizeof(ofn));
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hdlg;
+ ofn.lpstrFilter = text;
+ ofn.lpstrFile = str;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_DONTADDTORECENT;
+ ofn.nMaxFile = SIZEOF(str);
+ ofn.lpstrDefExt = _T("idx");
+ if(GetOpenFileName(&ofn))
+ SetDlgItemText(hdlg,IDC_FILENAME,str);
+ break;
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ { int i;
+ for(i=SendDlgItemMessage(hdlg,IDC_LIST,LB_GETCOUNT,0,0)-1;i>=0;i--)
+ mir_free((char*)SendDlgItemMessage(hdlg,IDC_LIST,LB_GETITEMDATA,i,0));
+ break;
+ }
+ }
+ return FALSE;
+}
+
+
+INT_PTR CALLBACK MirabilisOptionsPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hdlg);
+ EnableWindow(GetDlgItem(hdlg, IDC_RADIO_ALL), TRUE);
+ EnableWindow(GetDlgItem(hdlg, IDC_STATIC_ALL), TRUE);
+ EnableWindow(GetDlgItem(hdlg, IDC_RADIO_CONTACTS), TRUE);
+ EnableWindow(GetDlgItem(hdlg, IDC_STATIC_CONTACTS), TRUE);
+ CheckDlgButton(hdlg, IDC_RADIO_ALL, BST_CHECKED);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDC_BACK:
+ PostMessage(GetParent(hdlg), WIZM_GOTOPAGE, IDD_MIRABILISDB, (LPARAM)MirabilisPageProc);
+ break;
+ case IDOK:
+ if (IsDlgButtonChecked(hdlg, IDC_RADIO_ALL)) {
+ DoImport = MirabilisImport;
+ nImportOption = IMPORT_ALL;
+ nCustomOptions = IOPT_MSGSENT|IOPT_MSGRECV|IOPT_URLSENT|IOPT_URLRECV;
+ PostMessage(GetParent(hdlg), WIZM_GOTOPAGE, IDD_PROGRESS, (LPARAM)ProgressPageProc);
+ break;
+ }
+ if (IsDlgButtonChecked(hdlg, IDC_RADIO_CONTACTS)) {
+ DoImport = MirabilisImport;
+ nImportOption = IMPORT_CONTACTS;
+ nCustomOptions = 0;
+ PostMessage(GetParent(hdlg), WIZM_GOTOPAGE, IDD_PROGRESS, (LPARAM)ProgressPageProc);
+ break;
+ }
+ break;
+ case IDCANCEL:
+ PostMessage(GetParent(hdlg), WM_CLOSE, 0, 0);
+ break;
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+static int GetHighestIndexEntry(void)
+{
+ struct TIdxIndexEntry *entry;
+ DWORD ofs;
+
+ ofs=*(PDWORD)(pIdx+12);
+ for (;;) {
+ entry=(struct TIdxIndexEntry*)(pIdx+ofs);
+ if(entry->entryIdLow==(DWORD)-2) return ((struct TIdxDatEntry*)entry)->entryId;
+ if(entry->ofsHigher>=0xF0000000) ofs=entry->ofsInHere;
+ else ofs=entry->ofsHigher;
+ }
+}
+
+static int GetIdDatOfs(DWORD id)
+{
+ struct TIdxIndexEntry *entry;
+ DWORD ofs = *(PDWORD)(pIdx+12);
+ for (;;) {
+ entry=(struct TIdxIndexEntry*)(pIdx+ofs);
+ if(entry->entryIdLow==(DWORD)-2) {
+ if(entry->entryIdHigh==id) return ((struct TIdxDatEntry*)entry)->datOfs;
+ return 0;
+ }
+ if(id<entry->entryIdLow) ofs=entry->ofsLower;
+ else if(entry->ofsHigher<0xF0000000 && id>=entry->entryIdHigh) ofs=entry->ofsHigher;
+ else ofs=entry->ofsInHere;
+ }
+ return 0;
+}
+
+static int GetDatEntryType(DWORD ofs)
+{
+ return *(int*)(pDat+ofs+4);
+}
+
+DWORD GetDBVersion()
+{
+ dwDBVersion = *(PDWORD)(pIdx+16);
+
+ switch (dwDBVersion) {
+ case DBV99A:
+ AddMessage( LPGEN("This looks like a ICQ 99a database."));
+ break;
+ case DBV99B:
+ AddMessage( LPGEN("This looks like a ICQ 99b database."));
+ break;
+ case DBV2000A:
+ AddMessage( LPGEN("This looks like a ICQ 2000a database."));
+ break;
+ case DBV2000B:
+ AddMessage( LPGEN("This looks like a ICQ 2000b database."));
+ break;
+ case DBV2001A:
+ AddMessage( LPGEN("This looks like a ICQ 2001, 2002 or 2003a database."));
+ break;
+ default:
+ AddMessage( LPGEN("This database is an unknown version."));
+ return 0;
+ }
+
+ return dwDBVersion;
+}
+
+int GetEntryVersion(WORD wSeparatorValue)
+{
+ int nVersion;
+
+ if (wSeparatorValue < ENTRYV99A)
+ nVersion = 0; // Cannot handle ICQ98 contacts
+ else if ((wSeparatorValue >= ENTRYV99A) && (wSeparatorValue < ENTRYV99B))
+ nVersion = ENTRYV99A;
+ else if ((wSeparatorValue >= ENTRYV99B) && (wSeparatorValue < ENTRYV2000A))
+ nVersion = ENTRYV99B;
+ else if ((wSeparatorValue >= ENTRYV2000A) && (wSeparatorValue < ENTRYV2000B))
+ nVersion = ENTRYV2000A;
+ else if ((wSeparatorValue >= ENTRYV2000B) && (wSeparatorValue < ENTRYV2001A))
+ nVersion = ENTRYV2000B;
+ else if ((wSeparatorValue >= ENTRYV2001A) && (wSeparatorValue < ENTRYV2001B))
+ nVersion = ENTRYV2001A;
+ else if ((wSeparatorValue >= ENTRYV2001B) && (wSeparatorValue < ENTRYV2002A))
+ nVersion = ENTRYV2001B;
+ else if (wSeparatorValue >= ENTRYV2002A)
+ nVersion = ENTRYV2002A;
+ else
+ nVersion = ENTRYVUNKNOWN; // Just in case... Skip undocumented contact versions
+
+ return nVersion;
+}
+
+DWORD ReadSubList(DWORD dwOffset)
+{
+ DWORD dwSubType, dwProperties, n;
+
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Attempting to parse sub list at offset %u."), dwOffset);
+ #endif
+
+ // Check number of properties in sub list
+ dwProperties = *(PDWORD)(pDat+dwOffset);
+ dwOffset+=4;
+
+ // Check sub list type
+ dwSubType = *(PBYTE)(pDat+dwOffset);
+ dwOffset+=1;
+
+ switch (dwSubType){
+ case 0x6B:
+ for(n=0;n<dwProperties;n++) dwOffset+=*(PWORD)(pDat+dwOffset)+2;
+ break;
+ case 0x6E:
+ for(n=0;n<dwProperties;n++) {
+ if (!(dwOffset = ReadPropertyBlock(dwOffset, NULL, NULL))) return 0;
+ }
+ break;
+ default:
+ // Unknown sub list type
+ AddMessage( LPGEN("Error: Unknown sub list type (%u) at offset %u."), dwSubType, dwOffset);
+ return 0;
+ }
+
+ return dwOffset;
+}
+
+DWORD ReadPropertyBlock(DWORD dwOffset, char* SearchWord, int* nSearchResult)
+{
+ DWORD n, dwProperties, nameOfs;
+ WORD nameLen;
+
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Attempting to parse property block at offset %u."), dwOffset );
+ #endif
+
+ // Reset search result
+ if (SearchWord) *nSearchResult = 0;
+
+ // Check number of properties in block
+ dwOffset+=2;
+ dwProperties = *(PDWORD)(pDat+dwOffset);
+
+ // Scan all properties and search for
+ // 'SearchWord' (if it has been specified).
+ dwOffset+=4;
+ for(n=0;n<dwProperties;n++) {
+ nameLen=*(PWORD)(pDat+dwOffset); // Length of current property name
+ dwOffset+=2;
+ nameOfs=dwOffset; // Save pointer to start of name
+ dwOffset+=nameLen; // dwOffset now points to property value type
+
+ if ( SearchWord ) {
+ // Is this the property we are searching for?
+ if (!lstrcmpA((char*)(pDat+nameOfs),SearchWord)) {
+ *nSearchResult = 1;
+ return dwOffset;
+ } }
+
+ // Increase 'dwOffset' to point to length of next property
+ switch(*(pDat+dwOffset)) {
+ case 0x64:
+ case 0x65: dwOffset+=2; break;
+ case 0x66:
+ case 0x67: dwOffset+=3; break;
+ case 0x68:
+ case 0x69: dwOffset+=5; break;
+ case 0x6b: dwOffset+=*(PWORD)(pDat+dwOffset+1)+3; break;
+ case 0x6d:
+ dwOffset = ReadSubList(dwOffset+1);
+ if (!dwOffset) return 0;
+ break;
+ case 0x6f: dwOffset+=*(PDWORD)(pDat+dwOffset+1)+5; break;
+ default:
+ // Unknown property value type
+ AddMessage( LPGEN("Error: Unknown datatype (%u) at offset %u."), *(pDat+dwOffset), dwOffset);
+ return 0;
+ } }
+
+ // We have reached the end without finding
+ // the property we searched for
+ if (SearchWord) *nSearchResult = 0;
+
+ // Return offset to the byte right after the
+ // property list
+ return dwOffset;
+}
+
+DWORD ReadPropertyBlockList(DWORD dwOffset, char* SearchWord, int* nSearchResult)
+{
+ DWORD dwBlocks, n;
+
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Attempting to parse property block list at offset %u."), dwOffset );
+ #endif
+
+ // Check number of blocks in list
+ dwBlocks = *(PDWORD)(pDat+dwOffset);
+
+ // Scan all blocks and search for 'SearchWord' (if
+ // it has been specified).
+ dwOffset += 4;
+ for(n = 0;n<dwBlocks;n++) {
+ if (!(dwOffset = ReadPropertyBlock(dwOffset, SearchWord, nSearchResult))) {
+ AddMessage( LPGEN("Failed to read Property block."));
+ return 0;
+ }
+ if (SearchWord)
+ // Was the property found in the block?
+ if (*nSearchResult) return dwOffset;
+ }
+
+ // We have reached the end without finding
+ // the property we searched for
+ if (SearchWord) *nSearchResult = 0;
+
+ // Return offset to the byte right after the
+ // property list
+ return dwOffset;
+}
+
+DWORD ReadWavList(DWORD dwOffset)
+{
+ DWORD dwWavEntries, n;
+ WORD wNameLen;
+
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Attempting to parse wav file list at offset %u."), dwOffset);
+ #endif
+
+ // Check number of wav entries
+ dwWavEntries = *(PDWORD)(pDat+dwOffset);
+
+ // Read entire list
+ dwOffset+=4;
+ for(n = 0;n<dwWavEntries;n++) {
+ wNameLen = *(PWORD)(pDat+dwOffset+0x0A);
+ dwOffset += wNameLen + 0x0C;
+ }
+
+ // Return the offset to the byte right
+ // after the list
+ return dwOffset;
+}
+
+DWORD FindMyDetails(void)
+{
+ DWORD dwOffset = GetIdDatOfs(1005);
+
+ if (!dwOffset) return 0;
+ if (*(PDWORD)(pDat+dwOffset+0x08) != 1005) return 0;
+ if (*(PBYTE)(pDat+dwOffset+0x0C) != 0xE4) return 0;
+ if (*(int*)(pDat+dwOffset+0x1e) != 'USER') return 0;
+ if (*(PDWORD)(pDat+dwOffset+0x22) != 6) return 0;
+
+ return dwOffset;
+}
+
+// dwOffset must point to MyDetails
+DWORD FindGroupList(DWORD dwOffset)
+{
+ DWORD n, dwPhoneEntries;
+ WORD wSeparatorValue;
+ int nFormat;
+
+ wSeparatorValue = *(PWORD)(pDat+dwOffset+0x1c);
+ nFormat = GetEntryVersion(wSeparatorValue);
+
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Attempting to parse group list, type %d."), nFormat );
+ #endif
+
+ switch (nFormat) {
+ case ENTRYV99A:
+ if (!(dwOffset = ReadWavList(dwOffset+0x54))) return 0;
+ if (!(dwOffset = ReadPropertyBlock(dwOffset+38, NULL, NULL))) return 0;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // User name
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Nick name
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // First name
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Last name
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Primary e-mail
+ dwOffset += 0x13; // Various fixed length data
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Home city
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Home state
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Additional details
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // User homepage
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Home phone number
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Notes
+ dwOffset += 0x08; // Various fixed length data
+ dwOffset += 0x04; // ++ UNKNOWN ++
+ dwPhoneEntries = *(PDWORD)(pDat+dwOffset); // Phonebook starts here
+ dwOffset += 0x04;
+ for(n = 0;n<dwPhoneEntries;n++) {
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += 2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ }
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Picture file name
+ dwOffset += 0x06; // ++ UNKNOWN ++ (8 enligt spec)
+ dwOffset += 0x06; // Various fixed length data
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Secondary e-mail
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Old e-mail
+ dwOffset += 0x04; // ++ UNKNOWN ++
+ dwOffset += 0x03; // Various fixed length data
+ dwOffset += 0x08; // ++ UNKNOWN ++
+ dwOffset += 0x03; // Various fixed length data
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Home street address
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Home fax number
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Home cell phone number
+ dwOffset += 0x04; // ++ UNKNOWN ++
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Company Div/Dept
+ dwOffset += 0x01; // Occupation
+ dwOffset += 0x04; // ++ UNKNOWN ++
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Company position
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Company name
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Work street address
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Work state
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Work city
+ dwOffset += 0x08; // Various fixed length data
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Work phone number
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Work fax number
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Work homepage
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Past background #1
+ dwOffset += 0x02; // Past background #1 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Past background #2
+ dwOffset += 0x02; // Past background #2 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Past background #3
+ dwOffset += 0x02; // Past background #3 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Affiliation #1
+ dwOffset += 0x02; // Affiliation #1 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Affiliation #2
+ dwOffset += 0x02; // Affiliation #2 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Affiliation #3
+ dwOffset += 0x02; // Affiliation #3 category
+ dwOffset += 0x14; // ++ UNKNOWN ++
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Interest #1
+ dwOffset += 0x02; // Interest #1 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Interest #2
+ dwOffset += 0x02; // Interest #2 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Interest #3
+ dwOffset += 0x02; // Interest #3 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Interest #4
+ dwOffset += 0x02; // Interest #4 category
+ dwOffset += 0x28; // ++ UNKNOWN ++
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Password
+ dwOffset += 0x04; // ++ UNKNOWN ++
+ dwOffset += 0x0E; // ++ UNKNOWN ++
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // POP3 account name
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // POP3 account password
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // POP server name
+ dwOffset += 0x15; // ++ UNKNOWN ++
+ return dwOffset;
+
+ case ENTRYV99B:
+ if (!(dwOffset = ReadWavList(dwOffset+0x2C))) return 0;
+ if (!(dwOffset = ReadPropertyBlockList(dwOffset+0x02, NULL, NULL))) return 0;
+ dwOffset += 0x08;
+ dwPhoneEntries = *(PDWORD)(pDat+dwOffset); // Phonebook
+ dwOffset += 0x04;
+ for(n = 0;n<dwPhoneEntries;n++) {
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += 2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ }
+ return dwOffset + 0x06;
+
+ case ENTRYV2000A:
+ case ENTRYV2000B:
+ case ENTRYV2002A:
+ if (!(dwOffset = ReadWavList(dwOffset+0x2C))) return 0;
+ if (!(dwOffset = ReadPropertyBlockList(dwOffset+0x02, NULL, NULL))) return 0;
+ return dwOffset + 0x06;
+
+ case ENTRYV2001A:
+ case ENTRYV2001B:
+ if (!(dwOffset = ReadPropertyBlockList(dwOffset+0x2C, NULL, NULL))) return 0;
+ return dwOffset + 0x06;
+
+ default:
+ AddMessage( LPGEN("default"));
+ return 0;
+ }
+}
+
+// ------------------------------------------------
+// Finds the name of a group with a specific GroupID.
+// ------------------------------------------------
+// dwGroupID is the GroupID of the group.
+// Returns a pointer to the name string or NULL if
+// it was not found.
+char* GetGroupName(DWORD dwGroupID)
+{
+ DWORD dwGroups, n, tmpOfs, dwOffset;
+ char* strGroupName = 0;
+ int nSearchResult;
+
+ // Check for the existence of any group
+ if (!dwGroupListOfs) return 0;
+ dwOffset = dwGroupListOfs;
+ dwGroups = *(PDWORD)(pDat + dwOffset);
+ if (dwGroups == 0) return 0;
+
+ dwOffset += 4;
+
+ // Examine all groups
+ switch (dwDBVersion) {
+ case DBV99A:
+ case DBV99B:
+ for (n = 0; n < dwGroups; n++) {
+ if (dwGroupID == *(PDWORD)(pDat + dwOffset)) {
+ if (*(PWORD)(pDat + dwOffset + 4) > 1)
+ return 6 + (char*)(pDat + dwOffset);
+
+ break;
+ }
+ else
+ // Skip to next group
+ dwOffset += *(PWORD)(pDat + dwOffset + 4) + 12;
+ }
+ break;
+
+ case DBV2000A:
+ case DBV2000B:
+ case DBV2001A:
+ for (n = 0; n < dwGroups; n++) {
+ if (tmpOfs = ReadPropertyBlock(dwOffset, "GroupID", &nSearchResult)) {
+ if (nSearchResult) {
+ if (dwGroupID == *(PDWORD)(pDat + tmpOfs + 1)) {
+ strGroupName = 3 + (char*)(pDat + ReadPropertyBlock(dwOffset, "GroupName", &nSearchResult));
+ if (nSearchResult) {
+ if ((DWORD)*(strGroupName - 2) > 1)
+ return strGroupName;
+ break;
+ } } } }
+
+ // Skip to next group
+ if ( dwOffset != ReadPropertyBlock(dwOffset, NULL, NULL))
+ break;
+ }
+ break;
+ }
+
+ // The GroupID was not found, or it was found
+ // but the group did not have a name, or there
+ // was an error during parsing.
+ return 0;
+}
+
+// ------------------------------------------------
+// Scans a group list and adds all found groups to
+// the Miranda contact list
+// ------------------------------------------------
+// dwOffset must point to the number of entries in
+// the following group list.
+// Returns the number of added groups, or -1 if an error
+// occurred
+
+int ImportGroups()
+{
+ DWORD dwGroups, n, tmpOfs, dwOffset;
+ int nImported = 0;
+ int nSearchResult, nFormat;
+ WORD wSeparatorValue;
+
+ if (!(dwOffset = FindMyDetails())) {
+ AddMessage( LPGEN("ERROR: Failed to find owner information."));
+ return -1;
+ }
+
+ wSeparatorValue = *(PWORD)(pDat + dwOffset + 0x1c);
+ nFormat = GetEntryVersion(wSeparatorValue);
+
+ dwGroupListOfs = dwOffset = FindGroupList(dwOffset);
+ if (!dwOffset) {
+ AddMessage( LPGEN("ERROR: Failed to find contact list groups."));
+ #ifdef _LOGGING
+ { // If this is a debug build, dump MyDetails block to disk
+ FILE *stream;
+ DWORD dwSize;
+ dwOffset = FindMyDetails();
+ dwSize = *(PDWORD)(pDat + dwOffset);
+ stream = fopen("import_grouplist_dump.bin", "w");
+ fwrite(pDat + dwOffset, 1, dwSize, stream);
+ fclose(stream);
+ }
+ #endif
+ return -1;
+ }
+
+ // Check number of groups
+ dwGroups = *(PDWORD)(pDat + dwOffset);
+ if (dwGroups > 0)
+ AddMessage( LPGEN("Importing groups."));
+ else {
+ AddMessage( LPGEN("This database does not contain any contact groups."));
+ return 0;
+ }
+
+ dwOffset += 4;
+
+ // Import all groups with a name
+ switch (nFormat) {
+ case ENTRYV99A:
+ case ENTRYV99B:
+ for (n = 0; n < dwGroups; n++) {
+ if (*(PWORD)(pDat+dwOffset+4) > 1) {
+ if ( CreateGroup(DBVT_ASCIIZ, (char*)(pDat + dwOffset) + 6, NULL ))
+ nImported++;
+ dwOffset += *(PWORD)(pDat + dwOffset + 4) + 12;
+ } }
+ break;
+
+ case ENTRYV2000A:
+ case ENTRYV2000B:
+ case ENTRYV2001A:
+ case ENTRYV2001B:
+ case ENTRYV2002A:
+ for (n = 0; n < dwGroups; n++) {
+ if (tmpOfs = ReadPropertyBlock(dwOffset, "GroupName", &nSearchResult)) {
+ if (nSearchResult) {
+ if (CreateGroup( DBVT_ASCIIZ, (char*)(pDat + tmpOfs + 3), NULL ))
+ nImported++;
+ } }
+
+ dwOffset = ReadPropertyBlock(dwOffset, NULL, NULL);
+ if (!dwOffset) {
+ AddMessage( LPGEN("ERROR: An error occurred while importing groups."));
+ AddMessage( LPGEN("All groups may not have not been imported."));
+ #ifdef _LOGGING
+ { // If this is a debug build, dump MyDetails block to disk
+ FILE *stream;
+ DWORD dwSize;
+ dwOffset = FindMyDetails();
+ dwSize = *(PDWORD)(pDat + dwOffset);
+ stream = fopen("import_grouplist_dump.bin", "w");
+ fwrite(pDat + dwOffset, 1, dwSize, stream);
+ fclose(stream);
+ }
+ #endif
+ return -1;
+ } }
+ break;
+
+ default:
+ return -1;
+ }
+
+ return nImported;
+}
+
+// Imports the contact at offset dwOffset
+// Returns the HANDLE of the Miranda contact
+// or INVALID_HANDLE_VALUE on failure
+
+HANDLE ImportContact(DWORD dwOffset)
+{
+ int nContactVersion, nSearchResult;
+ BYTE Status;
+ WORD wSeparatorValue;
+ DWORD dwGroup, dwUIN = 0, tmpOfs = 0;
+ char *strNickname = 0, *strGroupName = 0;
+
+ if (*(int*)(pDat + dwOffset + 4) != DATENTRY_CONTACT)
+ return INVALID_HANDLE_VALUE;
+
+ if (*(int*)(pDat + dwOffset + 0x1e) != 'USER')
+ return INVALID_HANDLE_VALUE;
+
+ #ifdef _LOGGING
+ { // If this is a debug build, dump contact to disk
+ FILE *stream;
+ DWORD dwSize;
+ dwSize = *(PDWORD)(pDat + dwOffset);
+ stream = fopen("import_last_contact.bin", "w");
+ fwrite(pDat + dwOffset, 1, dwSize, stream);
+ fclose(stream);
+ }
+ #endif
+
+ Status = *(pDat + dwOffset + 0x22);
+ wSeparatorValue = *(PWORD)(pDat + dwOffset + 0x1c);
+ nContactVersion = GetEntryVersion(wSeparatorValue);
+
+ dwGroup = *(PDWORD)(pDat + dwOffset + 0x26);
+ if (dwGroup >= 1000)
+ strGroupName = GetGroupName(dwGroup);
+
+ if (Status == 5)
+ return INVALID_HANDLE_VALUE; // Skip deleted contacts
+
+ if ((Status != 2) && (Status != 3)) {
+ AddMessage( LPGEN("Skipping inactive contact."));
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if ((nContactVersion < ENTRYV99A) || (nContactVersion == 0)) {
+ AddMessage( LPGEN("Skipping contact with unsupported version."));
+ return INVALID_HANDLE_VALUE;
+ }
+
+ switch(nContactVersion){
+ case ENTRYV99A:
+ if (!(dwOffset = ReadWavList(dwOffset + 0x54))) return INVALID_HANDLE_VALUE;
+ if (!(dwOffset = ReadPropertyBlock(dwOffset + 0x26, NULL, NULL))) return INVALID_HANDLE_VALUE;
+ // Check for custom nickname
+ if (*(PWORD)(pDat + dwOffset) > 1) strNickname = (char*)(dwOffset + pDat + 2);
+ // Find UIN
+ dwOffset += *(PWORD)(pDat + dwOffset) + 2; // Custom nick name
+ dwOffset += *(PWORD)(pDat + dwOffset) + 2; // Nick name
+ dwOffset += *(PWORD)(pDat + dwOffset) + 2; // First name
+ dwOffset += *(PWORD)(pDat + dwOffset) + 2; // Last name
+ dwOffset += *(PWORD)(pDat + dwOffset) + 2; // E-mail
+ dwUIN = *(PDWORD)(pDat + dwOffset); // UIN
+ break;
+
+ case ENTRYV99B:
+ case ENTRYV2000A:
+ case ENTRYV2000B:
+ if (!(dwOffset = ReadWavList(dwOffset + 0x2C))) return INVALID_HANDLE_VALUE;
+ tmpOfs = ReadPropertyBlockList(dwOffset + 0x02, "UIN", &nSearchResult);
+ if (nSearchResult) dwUIN = *(PDWORD)(pDat + tmpOfs + 1);
+ tmpOfs = ReadPropertyBlockList(dwOffset + 0x02, "MyDefinedHandle", &nSearchResult);
+ if (nSearchResult) strNickname = (char*)(tmpOfs + pDat + 3);
+ break;
+
+ case ENTRYV2001A:
+ case ENTRYV2001B:
+ tmpOfs = ReadPropertyBlockList(dwOffset + 0x2C, "MyDefinedHandle", &nSearchResult);
+ if (nSearchResult) strNickname = (char*)(tmpOfs + pDat + 3);
+ tmpOfs = ReadPropertyBlockList(dwOffset + 0x2C, "UIN", &nSearchResult);
+ if (nSearchResult) dwUIN = *(PDWORD)(pDat + tmpOfs + 1);
+ break;
+
+ case ENTRYV2002A:
+ tmpOfs = ReadPropertyBlockList(dwOffset + 0x32, "MyDefinedHandle", &nSearchResult);
+ if (nSearchResult) strNickname = (char*)(tmpOfs + pDat + 3);
+ tmpOfs = ReadPropertyBlockList(dwOffset + 0x32, "UIN", &nSearchResult);
+ if (nSearchResult) dwUIN = *(PDWORD)(pDat + tmpOfs + 1);
+ break;
+ }
+
+ if (!dwUIN) {
+ AddMessage( LPGEN("Skipping unrecognizable contact."));
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (dwUIN < 10000) {
+ AddMessage( LPGEN("Skipping non-ICQ contact %u."), dwUIN );
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (HContactFromNumericID( szICQModuleName[ iICQAccount ], "UIN", dwUIN) == INVALID_HANDLE_VALUE) {
+ DBVARIANT id, nick, group;
+ id.type = DBVT_DWORD; id.dVal = dwUIN;
+ if ( strNickname != NULL && strlen(strNickname) > 0 )
+ nick.type = DBVT_ASCIIZ, nick.pszVal = strNickname;
+ else
+ nick.type = DBVT_DELETED;
+ group.type = DBVT_ASCIIZ, group.pszVal = strGroupName;
+ return AddContact(hdlgProgress, szICQModuleName[ iICQAccount ], "UIN", &id, &nick, &group);
+ }
+ else {
+ if ((strNickname != NULL) && (strlen(strNickname) > 0))
+ AddMessage( LPGEN("Skipping duplicate ICQ contact %u, %s"), dwUIN, strNickname);
+ else
+ AddMessage( LPGEN("Skipping duplicate ICQ contact %u"), dwUIN);
+ }
+
+ // Failure
+ return INVALID_HANDLE_VALUE;
+}
+
+BOOL ImportMessage(DWORD dwOffset)
+{
+ struct TDatMessage *msg = (struct TDatMessage*)(pDat + dwOffset);
+ struct TDatEntryFooter *footer;
+ DBEVENTINFO dbei;
+ HANDLE hContact;
+ int nUCTOffset;
+ TIME_ZONE_INFORMATION TimeZoneInformation;
+ int nHistoryCount = 0;
+
+ // Get timestamp offset. In ICQ, event timestamps are stored
+ // as UTC + (0-TZ offset). YES! That's the negation of the
+ // timezone offset, only God and Mirabilis knows why.
+ GetTimeZoneInformation(&TimeZoneInformation);
+ nUCTOffset = -TimeZoneInformation.Bias * 60;
+
+ // Ignore messages in 'Deleted' folder
+ if (msg->filingStatus&FILING_DELETED)
+ return FALSE;
+
+ // Skip messages from non-icq contacts
+ if (msg->uin < 10000) {
+ AddMessage( LPGEN("Ignoring msg from user %d at ofs %d."), msg->uin, dwOffset );
+ return FALSE;
+ }
+
+ // Ignore received messages?
+ if (( msg->filingStatus & FILING_RECEIVED ) && !( nCustomOptions & IOPT_MSGRECV ))
+ return FALSE;
+
+ // Ignores sent messages?
+ if ( !(msg->filingStatus & FILING_RECEIVED) && !( nCustomOptions & IOPT_MSGSENT ))
+ return FALSE;
+
+ // Check if contact exists in Miranda database
+ hContact = HistoryImportFindContact(hdlgProgress, szICQModuleName[ iICQAccount ], msg->uin, nCustomOptions&IOPT_ADDUNKNOWN);
+ if (hContact == INVALID_HANDLE_VALUE)
+ return FALSE; // Contact couldn't be found/added
+
+ // Convert the event to a Miranda dbevent
+ footer = (struct TDatEntryFooter*)(pDat + dwOffset + msg->textLen + offsetof(struct TDatMessage, text));
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.flags = footer->sent == 1 ? DBEF_SENT : DBEF_READ;
+ dbei.szModule = szICQModuleName[ iICQAccount ];
+ // Convert timestamp
+ dbei.timestamp = footer->timestamp + nUCTOffset;
+ dbei.cbBlob = msg->textLen;
+ dbei.pBlob = (PBYTE)alloca(msg->textLen);
+ CopyMemory(dbei.pBlob, msg->text, dbei.cbBlob);
+ dbei.pBlob[dbei.cbBlob - 1] = 0;
+
+ // Check for duplicate entries
+ if (IsDuplicateEvent(hContact, dbei)) {
+ nDupes++;
+ }
+ else {
+ if (CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei))
+ nMessagesCount++;
+ }
+
+ return TRUE;
+}
+
+BOOL ImportExtendedMessage(DWORD dwOffset)
+{
+ struct TDatMessage *msg = (struct TDatMessage*)(pDat + dwOffset);
+ struct TDatEntryFooter *footer;
+ DBEVENTINFO dbei;
+ HANDLE hContact;
+ int nUCTOffset;
+ TIME_ZONE_INFORMATION TimeZoneInformation;
+ int nHistoryCount = 0;
+ char* pszText = 0;
+ DWORD dwRichTextOffset = 0;
+ DWORD wRichTextLength = 0;
+ DWORD wLength = 0;
+ BOOL bFreeMe = FALSE;
+
+ // Get timestamp offset. In ICQ, event timestamps are stored
+ // as UTC + (0-TZ offset). YES! That's the negation of the
+ // timezone offset, only God and Mirabilis knows why.
+ GetTimeZoneInformation(&TimeZoneInformation);
+ nUCTOffset = -TimeZoneInformation.Bias * 60;
+
+ // Ignore messages in 'Deleted' folder
+ if (msg->filingStatus&FILING_DELETED)
+ return FALSE;
+
+ // Skip messages from non-icq contacts
+ if (msg->uin < 10000) {
+ AddMessage( LPGEN("Ignoring msg from user %d at ofs %d."), msg->uin, dwOffset );
+ return FALSE;
+ }
+
+ // Ignore received messages?
+ if (( msg->filingStatus & FILING_RECEIVED) && !( nCustomOptions & IOPT_MSGRECV ))
+ return FALSE;
+
+ // Ignore sent messages?
+ if ( !( msg->filingStatus & FILING_RECEIVED ) && !( nCustomOptions & IOPT_MSGSENT ))
+ return FALSE;
+
+ // Check if contact exists in Miranda database
+ hContact = HistoryImportFindContact(hdlgProgress, szICQModuleName[ iICQAccount ], msg->uin, nCustomOptions&IOPT_ADDUNKNOWN);
+ if (hContact == INVALID_HANDLE_VALUE)
+ return FALSE; // Contact couldn't be found/added
+
+ // Find a piece of usable text content
+ if (msg->textLen <= 1) {
+ // Skip past the RTF segment
+ wRichTextLength = *(PWORD)(pDat + dwOffset + 0x2A + msg->textLen + 0x21);
+ dwRichTextOffset = dwOffset + 0x2A + msg->textLen + 0x23;
+
+ // Use the UTF-8 text segment
+ wLength = *(PWORD)(pDat + dwRichTextOffset + wRichTextLength);
+ if (wLength <= 1) {
+ AddMessage( LPGEN("Ignoring msg with no text from %d ofs %d."), msg->uin, dwOffset );
+ return FALSE;
+ }
+ pszText = _strdup((char*)pDat + dwRichTextOffset + wRichTextLength + 2);
+ bFreeMe = TRUE;
+ mir_utf8decode(pszText, NULL);
+ wLength = (DWORD)strlen(pszText)+1;
+ }
+ else {
+ // Use the ANSI text segment
+ wLength = msg->textLen;
+ pszText = (char*)(pDat + dwOffset + 0x2A);
+ }
+
+ // Convert the event to a Miranda dbevent
+ footer = (struct TDatEntryFooter*)(pDat + dwOffset + msg->textLen + offsetof(struct TDatMessage, text));
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.flags = footer->sent == 1 ? DBEF_SENT : DBEF_READ;
+ dbei.szModule = szICQModuleName[ iICQAccount ];
+ // Convert timestamp
+ dbei.timestamp = footer->timestamp + nUCTOffset;
+ dbei.cbBlob = wLength;
+ dbei.pBlob = (PBYTE)calloc(wLength,1);
+ CopyMemory(dbei.pBlob, pszText, dbei.cbBlob);
+ dbei.pBlob[dbei.cbBlob - 1] = 0;
+
+ // Check for duplicate entries
+ if (IsDuplicateEvent(hContact, dbei)) {
+ nDupes++;
+ }
+ else {
+ if (CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei))
+ nMessagesCount++;
+ }
+
+ free(dbei.pBlob);
+ if (bFreeMe)
+ free(pszText);
+
+ return TRUE;
+}
+
+BOOL ImportURLMessage(DWORD dwOffset)
+{
+ struct TDatMessage *msg = (struct TDatMessage*)(pDat + dwOffset);
+ struct TDatEntryFooter *footer;
+ DBEVENTINFO dbei;
+ HANDLE hContact;
+ int nUCTOffset;
+ TIME_ZONE_INFORMATION TimeZoneInformation;
+ int nHistoryCount = 0;
+ char *pSeparator;
+
+ // Get timestamp offset. In ICQ, event timestamps are stored
+ // as UTC + (0-TZ offset). YES! That's the negation of the
+ // timezone offset, only God and Mirabilis knows why.
+ GetTimeZoneInformation(&TimeZoneInformation);
+ nUCTOffset = -TimeZoneInformation.Bias * 60;
+
+ // Ignore URLs in 'Deleted' folder
+ if (msg->filingStatus&FILING_DELETED)
+ return FALSE;
+
+ // Skip URLs from non-icq contacts
+ if (msg->uin < 10000) {
+ AddMessage( LPGEN("Ignoring msg from user %d at ofs %d."), msg->uin, dwOffset );
+ return FALSE;
+ }
+
+ // Ignore received URLs?
+ if (( msg->filingStatus & FILING_RECEIVED ) && !( nCustomOptions & IOPT_URLRECV ))
+ return FALSE;
+
+ // Ignores sent URLs?
+ if ( !( msg->filingStatus & FILING_RECEIVED ) && !( nCustomOptions & IOPT_URLSENT ))
+ return FALSE;
+
+ // Check if contact exists in Miranda database
+ hContact = HistoryImportFindContact(hdlgProgress, szICQModuleName[ iICQAccount ], msg->uin, nCustomOptions&IOPT_ADDUNKNOWN);
+ if (hContact == INVALID_HANDLE_VALUE)
+ return FALSE; // Contact couldn't be found/added
+
+ // Convert the event to a Miranda dbevent
+ footer = (struct TDatEntryFooter*)(pDat + dwOffset + msg->textLen + offsetof(struct TDatMessage, text));
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.eventType = EVENTTYPE_URL;
+ dbei.flags = footer->sent == 1 ? DBEF_SENT : DBEF_READ;
+ dbei.szModule = szICQModuleName[ iICQAccount ];
+ // Convert timestamp
+ dbei.timestamp = footer->timestamp + nUCTOffset;
+ dbei.cbBlob = msg->textLen;
+ dbei.pBlob = (PBYTE)alloca(msg->textLen);
+ CopyMemory(dbei.pBlob, msg->text, dbei.cbBlob);
+ dbei.pBlob[dbei.cbBlob - 1] = 0;
+ // Separate URL and description
+ pSeparator = strchr((char*)dbei.pBlob, 0xFE);
+ if (pSeparator != NULL)
+ *pSeparator = 0;
+
+ // Check for duplicate entries
+ if (IsDuplicateEvent(hContact, dbei))
+ nDupes++;
+ else if (CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei))
+ nMessagesCount++;
+
+ return TRUE;
+}
+
+BOOL ImportEvent(DWORD dwOffset)
+{
+ struct TDatMessage *msg = (struct TDatMessage*)(pDat + dwOffset);
+
+ // Events have IDs > 2000
+ if (msg->hdr.entryId < 2001) {
+ AddMessage( LPGEN("Skipping event with ID < 2001."));
+ return FALSE;
+ }
+
+ // Separate code paths based on the event signature
+ switch (msg->hdr.subType) {
+
+ case SUBTYPE_MESSAGE: // All kinds of messages
+ switch (msg->type) {
+ case 1: // Normal message
+ if ((nCustomOptions&IOPT_MSGRECV) || (nCustomOptions&IOPT_MSGSENT)) {
+ return ImportMessage(dwOffset);
+ }
+ break;
+
+ case 4: // URL
+ if ((nCustomOptions&IOPT_URLSENT) || (nCustomOptions&IOPT_URLRECV)) {
+ return ImportURLMessage(dwOffset);
+ }
+ break;
+
+ case 6: // Request for authorization
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Request for auth.' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 7: // Authorization request denied
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Auth. denied' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 8: // Authorization request accepted
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Auth. accepted' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 9: // System message
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'System message', ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 12: // You were added
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'You were added' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 13: // WWWPager ?
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'WWW Pager' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 14: // Email Express ?
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Email Express' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 19: // Contact list
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Contact' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 21: // Phonecall request?
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Phonecall' msg (?), ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 26: // SMS request?
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'SMS' msg (?), ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 29: // Active list invitation ??
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 29 msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 30: // Birthday reminder
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Birthday' msg (?), ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 32: // Unknown (Tomer)
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 32 msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ default:
+ AddMessage( LPGEN("Skipping unknown 0xE0 subtype (%d), ofs %d."), msg->type, dwOffset );
+
+ #ifdef _LOGGING
+ { // If this is a debug build, dump entry to disk
+ FILE *stream;
+ DWORD dwSize = *(PDWORD)(pDat + dwOffset);
+ wsprintfA(str, "import_unknown_E0subtype_%u-%u.bin", msg->type, dwOffset);
+ stream = fopen(str, "w");
+ fwrite(pDat + dwOffset, 1, dwSize, stream);
+ fclose(stream);
+ }
+ #endif
+
+ return FALSE;
+ }
+ break;
+
+ case SUBTYPE_CHATREQUEST: // 0xE1
+ #ifdef _LOGGING
+ if (nImportOption != IMPORT_CONTACTS)
+ AddMessage( LPGEN("Skipping 'Chat request' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case SUBTYPE_FILEREQUEST: // 0xE2
+ #ifdef _LOGGING
+ if (nImportOption != IMPORT_CONTACTS)
+ AddMessage( LPGEN("Skipping file message offset %d."), dwOffset );
+ #endif
+ break;
+
+ case 0xE3: // External (IPhone, Battlecom) Maybe general voice calls?
+ #ifdef _LOGGING
+ if (nImportOption != IMPORT_CONTACTS)
+ AddMessage( LPGEN("Skipping message type 0xE3 at offset %d."), dwOffset );
+ #endif
+ break;
+
+ case 0xE4: // My details
+ break;
+ case 0xE5: // Contact
+ break;
+ case 0xE6: // Reminder
+ break;
+ case 0xE7: // Addressbook
+ break;
+ case 0xEC: // Voice message
+ break;
+ case 0xED: // Unknown, something to do with chatting and .CHT files
+ // if (importHistory) {
+ // wsprintf(str, "Skipping message type 0xED at offset %d.", dwOffset);
+ // AddMessage( LPGEN(str);
+ // }
+ break;
+ case 0xEE: // Note
+ break;
+ case 0xEF: // Event folder
+ break;
+ // case 0xF0: // Unknown
+ // if (importHistory) {
+ // wsprintf(str, "Skipping message type 0xF0 at offset %d.", dwOffset);
+ // AddMessage( LPGEN(str);
+ // }
+ // break;
+ case 0xF1: // Server list
+ break;
+ // case 0xF6: // Unknown
+ // if (importHistory) {
+ // wsprintf(str, "Skipping message type 0xF6 at offset %d.", dwOffset);
+ // AddMessage( LPGEN(str);
+ // }
+ // break;
+ case 0x50: // Extended message, ICQ 2000a+?
+ if (nImportOption != IMPORT_CONTACTS) {
+ return ImportExtendedMessage(dwOffset);
+ }
+ break;
+
+ case 0xA0: // URL message type 2
+ if (nImportOption != IMPORT_CONTACTS) {
+ if ((msg->filingStatus&FILING_RECEIVED) || (nCustomOptions&IOPT_URLRECV)) {
+ return ImportURLMessage(dwOffset);
+ }
+ }
+ break;
+
+ default:
+ if (nImportOption != IMPORT_CONTACTS) {
+ AddMessage( LPGEN("Skipping unknown event type %d at offset %d."), msg->hdr.subType, dwOffset );
+
+#ifdef _LOGGING
+ { // If this is a debug build, dump entry to disk
+ FILE *stream;
+ DWORD dwSize;
+ dwSize = *(PDWORD)(pDat + dwOffset);
+ wsprintfA(str, "import_unknown_eventtype_%u-%u.bin", msg->hdr.subType, dwOffset);
+ stream = fopen(str, "w");
+ fwrite(pDat + dwOffset, 1, dwSize, stream);
+ fclose(stream);
+ }
+#endif
+
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+
+static void MirabilisImport(HWND hdlgProgressWnd)
+{
+ HANDLE hIdx, hDat, hIdxMapping, hDatMapping;
+ DWORD i, ofs, highestIndexEntry;
+ TCHAR datFilename[MAX_PATH];
+ MSG msg;
+ DWORD dwTimer;
+
+
+ int status = 0;
+ hdlgProgress = hdlgProgressWnd;
+ nDupes = nContactsCount = nMessagesCount = 0;
+
+ SetProgress(0);
+ lstrcpy(datFilename, importFile);
+ {
+ TCHAR* str2;
+ str2 = _tcsrchr(datFilename,'.');
+ if ( str2 != NULL )
+ lstrcpy(str2, _T(".dat"));
+ }
+
+ hIdx = CreateFile(importFile, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (hIdx == INVALID_HANDLE_VALUE) {
+ AddMessage( LPGEN("Failed to open index file"));
+ AddMessage( LPGEN("Import aborted"));
+ SetProgress(100);
+ return;
+ }
+
+ hDat = CreateFile(datFilename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (hDat == INVALID_HANDLE_VALUE) {
+ AddMessage( LPGEN("Failed to open database file"));
+ AddMessage( LPGEN("Import aborted"));
+ SetProgress(100);
+ return;
+ }
+
+ // Creating file mappings
+ hIdxMapping = CreateFileMapping(hIdx, NULL, PAGE_READONLY, 0, 0, NULL);
+ hDatMapping = CreateFileMapping(hDat, NULL, PAGE_READONLY, 0, 0, NULL);
+
+ // Mapping views of files
+ pIdx = (PBYTE)MapViewOfFile(hIdxMapping, FILE_MAP_READ, 0, 0, 0);
+ pDat = (PBYTE)MapViewOfFile(hDatMapping, FILE_MAP_READ, 0, 0, 0);
+
+ // Is this a supported format?
+ if (GetDBVersion()) {
+ AddMessage( "" );
+
+ highestIndexEntry = GetHighestIndexEntry();
+
+ // Import groups
+ nGroupsCount = ImportGroups();
+ if (nGroupsCount < 0) {
+ AddMessage( LPGEN("Group import was not completed."));
+ nGroupsCount = 0;
+ }
+ AddMessage( "" );
+
+ // Start benchmark timer
+ dwTimer = time(NULL);
+
+ if ( !IsProtocolLoaded( szICQModuleName[iICQAccount] )) {
+ AddMessage( LPGEN("ICQ account is not installed."));
+ AddMessage( LPGEN("No ICQ contacts or history will be imported."));
+ AddMessage( "" );
+ }
+ else {
+ // Configure database for fast writing
+ CallService(MS_DB_SETSAFETYMODE, FALSE, 0);
+
+ // Import contacts
+ AddMessage( LPGEN("Importing contacts"));
+ for (i = 2001; i <= highestIndexEntry; i++) { //event ids start at 2001
+ if (!(i%10)) {
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+ if (!(i%100))
+ SetProgress(100 * (i - 2001) / (highestIndexEntry - 2001));
+
+ ofs = GetIdDatOfs(i);
+ if (ofs != 0) {
+ if (ImportContact(ofs) != INVALID_HANDLE_VALUE)
+ nContactsCount++;
+ }
+ }
+ AddMessage( "" );
+
+ // Import history
+ if (nImportOption != IMPORT_CONTACTS) {
+ AddMessage( LPGEN("Importing history (this may take a while)"));
+ for (i = 2001; i <= highestIndexEntry; i++) { //event ids start at 2001
+ if (!(i%10)) {
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ if (!(i%100))
+ SetProgress(100 * (i - 2001) / (highestIndexEntry - 2001));
+
+ ofs = GetIdDatOfs(i);
+ if (ofs != 0) ImportEvent(ofs);
+ }
+ AddMessage( "" );
+ }
+
+ // Restore database writing mode
+ CallService(MS_DB_SETSAFETYMODE, TRUE, 0);
+ }
+
+ dwTimer = time(NULL) - dwTimer;
+
+ AddMessage( LPGEN("Import completed in %d seconds."), dwTimer );
+ SetProgress(100);
+ AddMessage( LPGEN("Added %d contacts and %d groups."), nContactsCount, nGroupsCount );
+ if ( nImportOption != IMPORT_CONTACTS )
+ AddMessage( LPGEN("Added %d events and skipped %d duplicates."), nMessagesCount, nDupes );
+ }
+
+ UnmapViewOfFile(pDat);
+ UnmapViewOfFile(pIdx);
+ CloseHandle(hDatMapping);
+ CloseHandle(hIdxMapping);
+ CloseHandle(hDat);
+ CloseHandle(hIdx);
+}
diff --git a/plugins/Import/src/mirabilis.h b/plugins/Import/src/mirabilis.h new file mode 100644 index 0000000000..47f10141e1 --- /dev/null +++ b/plugins/Import/src/mirabilis.h @@ -0,0 +1,200 @@ +/*
+
+Import plugin for Miranda IM
+
+Copyright (C) 2001,2002,2003,2004 Martin Öberg, Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+
+
+#ifndef MIRABILIS_H
+#define MIRABILIS_H
+
+#include <windows.h>
+#include <newpluginapi.h>
+#include <m_database.h>
+
+// ======================
+// == GLOBAL FUNCTIONS ==
+// ======================
+
+HANDLE HistoryImportFindContact(HWND hdlgProgress, char* szModuleName, DWORD uin,int addUnknown);
+
+// =====================
+// == LOCAL FUNCTIONS ==
+// =====================
+
+
+// Main function
+static void MirabilisImport(HWND hdlgProgressWnd);
+
+// GUI callbacks
+INT_PTR CALLBACK ImportTypePageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+INT_PTR CALLBACK FinishedPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+INT_PTR CALLBACK ProgressPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+INT_PTR CALLBACK MirabilisPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+INT_PTR CALLBACK MirabilisOptionsPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+
+// Helper functions for entries
+static int GetHighestIndexEntry(void);
+static int GetIdDatOfs(DWORD id);
+static int GetDatEntryType(DWORD ofs);
+DWORD FindMyDetails(void);
+
+// Parsing functions
+DWORD GetDBVersion();
+int GetEntryVersion(WORD wSeparatorValue);
+DWORD ReadPropertyBlock(DWORD dwOffset, char* SearchWord, int* nSearchResult);
+DWORD ReadSubList(DWORD dwOffset);
+DWORD ReadPropertyBlock(DWORD dwOffset, char* SearchWord, int* nSearchResult);
+DWORD ReadPropertyBlockList(DWORD dwOffset, char* SearchWord, int* nSearchResult);
+DWORD ReadWavList(DWORD ofs);
+DWORD FindGroupList(DWORD dwOffset);
+char* GetGroupName(DWORD dwGroupID);
+int ImportGroups();
+static HANDLE ImportContact(DWORD dwOffset);
+
+BOOL ImportEvent(DWORD dwOffset);
+BOOL ImportMessage(DWORD dwOffset);
+BOOL ImportExtendedMessage(DWORD dwOffset);
+BOOL ImportURLMessage(DWORD dwOffset);
+
+
+
+
+// ======================
+// == GLOBAL VARIABLES ==
+// ======================
+
+extern TCHAR importFile[MAX_PATH];
+extern void (*DoImport)(HWND);
+extern int nImportOption;
+extern int nCustomOptions;
+
+
+extern int cICQAccounts;
+extern char ** szICQModuleName;
+extern TCHAR ** tszICQAccountName;
+extern int iICQAccount;
+
+// =====================
+// == LOCAL VARIABLES ==
+// =====================
+
+static DWORD dwDBVersion;
+static DWORD dwGroupListOfs;
+static PBYTE pIdx,pDat;
+
+// =============
+// == DEFINES ==
+// =============
+
+// Contact versions
+// These numbers are not 100% accurate
+#define ENTRYVUNKNOWN -1
+#define ENTRYV99A 200
+#define ENTRYV99B 300
+#define ENTRYV2000A 400
+#define ENTRYV2000B 455
+#define ENTRYV2001A 500
+#define ENTRYV2001B 515
+#define ENTRYV2002A 533
+
+// Database versions
+#define DBV99A 10
+#define DBV99B 14
+#define DBV2000A 17
+#define DBV2000B 18
+#define DBV2001A 19 // This is used by ICQ 2001a, 2001b & 2002a
+
+#define DATENTRY_UNFILED (DWORD)(-1)
+#define DATENTRY_MESSAGE 0
+#define DATENTRY_CONTACT 1
+#define DATENTRY_IGNORED 2
+#define DATENTRY_SYSTEM 9
+
+#define MAX_NON_ICQ_CONTACTS 100
+
+#define SUBTYPE_NEWMESSAGE 0x50
+#define SUBTYPE_NEWURL 0xA0
+
+#define SUBTYPE_MESSAGE 0xE0 //Message / URL Message / Request For Authorization / "Authorization" / System Request / "You Were Added" / Contacts List
+#define SUBTYPE_CHATREQUEST 0xE1
+#define SUBTYPE_FILEREQUEST 0xE2
+#define SUBTYPE_MYDETAILS 0xE4
+#define SUBTYPE_CONTACTINFO 0xE5
+#define SUBTYPE_REMINDER 0xE6
+#define SUBTYPE_ADDRESSBOOK 0xE7
+#define SUBTYPE_VOICEMSG 0xEC //???
+#define SUBTYPE_NOTE 0xEE
+#define SUBTYPE_EVENTFOLDER 0xEF
+#define SUBTYPE_SERVERLIST 0xF1 //and objectionable word list
+#define SUBTYPE_X1 0xF6 //(new to ICQ 99b???)
+
+#define FILING_RECEIVED 0x01
+#define FILING_DELETED 0x02
+#define FILING_MESSAGE 0x04
+#define MSGTYPE_MESSAGE 1
+#define MSGTYPE_URL 4
+#define MSGTYPE_CLIST 19
+#include <pshpack1.h>
+
+struct TIdxDatEntry {
+ DWORD status; //-2=valid, else is an index entry
+ DWORD entryId;
+ DWORD ofsNext,ofsPrev;
+ DWORD datOfs;
+};
+
+struct TIdxIndexEntry {
+ DWORD entryIdLow;
+ DWORD entryIdHigh;
+ DWORD ofsLower;
+ DWORD ofsInHere;
+ DWORD ofsHigher;
+};
+
+struct TDatEntryHeader {
+ DWORD entrySize; //in bytes
+ DWORD entryType; //DATENTRY_* constant
+ DWORD entryId; //same as in index
+ BYTE subType; //SUBTYPE_* constant
+ BYTE signature[15];
+};
+
+struct TDatEntryFooter {
+ DWORD unknown;
+ DWORD sent; //1 if sent, 0 if received
+ WORD separator;
+ DWORD timestamp; //unix time
+};
+
+struct TDatMessage {
+ struct TDatEntryHeader hdr; //hdr.entryType==DATENTRY_MESSAGE && hdr.subType==MSGTYPE_MESSAGE
+ WORD separator;
+ DWORD filingStatus; //FILING_* flags
+ WORD type; //MSGTYPE_* constant
+ DWORD uin;
+ WORD textLen;
+ char text[1]; //0xFE separates description & URL in URLs
+ //a struct TDatEntryFooter comes here
+};
+
+#include <poppack.h>
+
+#endif
diff --git a/plugins/Import/src/miranda.cpp b/plugins/Import/src/miranda.cpp new file mode 100644 index 0000000000..4f616fb714 --- /dev/null +++ b/plugins/Import/src/miranda.cpp @@ -0,0 +1,1445 @@ +/*
+
+Import plugin for Miranda IM
+
+Copyright (C) 2001-2005 Martin Öberg, Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+// ==============
+// == INCLUDES ==
+// ==============
+
+#include "import.h"
+
+#include "resource.h"
+#include "mirandadb0700.h"
+
+// ======================
+// == GLOBAL FUNCTIONS ==
+// ======================
+
+HANDLE HContactFromNumericID(char* pszProtoName, char* pszSetting, DWORD dwID);
+HANDLE HContactFromID(char* pszProtoName, char* pszSetting, char* pszID);
+
+HANDLE AddContact(HWND hdlgProgress, char* pszProtoName, char* pszUniqueSetting, DBVARIANT* id, DBVARIANT* nick, DBVARIANT* group);
+
+BOOL IsProtocolLoaded(char* pszProtocolName);
+BOOL IsDuplicateEvent(HANDLE hContact, DBEVENTINFO dbei);
+
+INT_PTR CALLBACK ImportTypePageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+INT_PTR CALLBACK FinishedPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+INT_PTR CALLBACK ProgressPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+INT_PTR CALLBACK MirandaOptionsPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+INT_PTR CALLBACK MirandaAdvOptionsPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+
+// =====================
+// == LOCAL FUNCTIONS ==
+// =====================
+
+void MirandaImport(HWND hdlgProgress);
+int CheckFileFormat(HANDLE hFile);
+static HANDLE ImportContact(HANDLE hDbFile, struct DBContact Contact);
+static void ImportHistory(HANDLE hDbFile, struct DBContact Contact, PROTOCOLDESCRIPTOR **protocol, int protoCount);
+static int ImportGroups(HANDLE hDbFile, struct DBHeader *pdbHeader);
+
+// Comment: The Find* functions only return a file offset.
+// The Get* functions actually reads the requested
+// data from the file and gives you a pointer to a structure
+// containing the data.
+
+DWORD FindFirstContact(struct DBHeader* pDbHeader);
+DWORD FindNextContact(struct DBContact* pDbContact);
+DWORD FindNextEvent(HANDLE hDbFile, DWORD dwOffset);
+DWORD FindOwnerContact(struct DBHeader* pDbHeader);
+
+int GetContactCount(struct DBHeader* pDbHeader);
+BOOL GetContact(HANDLE hDbFile, DWORD dwOffset, struct DBContact* pDbContact);
+BOOL GetSetting(HANDLE hDbFile, struct DBContact* pDbContact, char* pszModuleName, char* pszSettingName, DBVARIANT* pValue);
+char* GetNextSetting(char* pDbSetting);
+BOOL GetSettings(HANDLE hDbFile, DWORD dwOffset, struct DBContactSettings** pDbSettings);
+struct DBContactSettings* GetSettingsGroupByModuleName(HANDLE hdbFile, struct DBContact* pDbContact, char* pszName);
+DWORD GetBlobSize(struct DBContactSettings* pDbSettings);
+int GetSettingByName(struct DBContactSettings* pDbSettings, char* pszSettingName, DBVARIANT* pValue);
+int GetSettingValue(char* pBlob,DBVARIANT* pValue);
+
+BOOL GetEvent(HANDLE hDbFile, DWORD dwOffset, DBEVENTINFO* pDBEI);
+char* GetName(HANDLE hDbFile, DWORD dwOffset);
+
+
+// ======================
+// == GLOBAL VARIABLES ==
+// ======================
+
+extern void (*DoImport)(HWND);
+extern int nImportOption;
+extern int nCustomOptions;
+
+
+// =====================
+// == LOCAL VARIABLES ==
+// =====================
+
+TCHAR importFile[MAX_PATH];
+HWND hdlgProgress;
+DWORD dwFileSize;
+
+DWORD nDupes;
+DWORD nContactsCount;
+DWORD nMessagesCount;
+DWORD nGroupsCount;
+DWORD nSkippedEvents;
+DWORD nSkippedContacts;
+
+time_t dwSinceDate = 0;
+
+// =============
+// == DEFINES ==
+// =============
+
+#define EVENTTYPE_MESSAGE 0
+#define EVENTTYPE_URL 1
+#define EVENTTYPE_FILE 1002
+
+
+// Supported database versions
+#define DB_INVALID 0x00000000 // Unknown or corrupted DAT
+#define DB_000700 0x00000700 // Miranda 0.1.0.0 - 0.1.2.2+
+
+// DAT file signature
+struct DBSignature {
+ char name[15];
+ BYTE eof;
+};
+
+static struct DBSignature dbSignature={"Miranda ICQ DB",0x1A};
+
+// ====================
+// ====================
+// == IMPLEMENTATION ==
+// ====================
+// ====================
+
+static void SearchForLists(HWND hdlg, const TCHAR *mirandaPath, const TCHAR *mirandaProf, const TCHAR *pattern, const TCHAR *type)
+{
+ HANDLE hFind;
+ WIN32_FIND_DATA fd;
+ TCHAR szSearchPath[MAX_PATH];
+ TCHAR szRootName[MAX_PATH];
+ TCHAR* str2;
+ int i;
+
+ mir_sntprintf(szSearchPath, SIZEOF(szSearchPath), _T("%s\\%s"), mirandaPath, pattern);
+ hFind = FindFirstFile(szSearchPath, &fd);
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ _tcscpy(szRootName, fd.cFileName);
+ str2 = _tcsrchr(szRootName, '.');
+ if (str2 != NULL) *str2 = 0;
+ if (mirandaProf == NULL || _tcsicmp(mirandaProf, szRootName))
+ {
+ _tcscat(szRootName, type);
+ i = SendDlgItemMessage(hdlg, IDC_LIST, LB_ADDSTRING, 0, (LPARAM)szRootName);
+ str2 = (TCHAR*)mir_alloc((_tcslen(mirandaPath) + 2 + _tcslen(fd.cFileName)) * sizeof(TCHAR));
+ wsprintf(str2, _T("%s\\%s"), mirandaPath, fd.cFileName);
+ SendDlgItemMessage(hdlg, IDC_LIST, LB_SETITEMDATA, i, (LPARAM)str2);
+ }
+ }
+ while( FindNextFile( hFind, &fd ));
+
+ FindClose( hFind );
+ }
+}
+
+INT_PTR CALLBACK MirandaPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hdlg);
+ {
+ TCHAR *pfd, *pfd1, *pfd2, *pfn;
+
+ REPLACEVARSDATA dat = {0};
+ dat.cbSize = sizeof(dat);
+ dat.dwFlags = RVF_TCHAR;
+
+ pfd = (TCHAR*)CallService(MS_UTILS_REPLACEVARS, (WPARAM)_T("%miranda_path%\\Profiles"), (LPARAM)&dat);
+ pfd1 = (TCHAR*)CallService(MS_UTILS_REPLACEVARS, (WPARAM)_T("%miranda_path%"), (LPARAM)&dat);
+ pfd2 = (TCHAR*)CallService(MS_UTILS_REPLACEVARS, (WPARAM)_T("%miranda_profile%"), (LPARAM)&dat);
+ pfn = (TCHAR*)CallService(MS_UTILS_REPLACEVARS, (WPARAM)_T("%miranda_profilename%"), (LPARAM)&dat);
+
+ SearchForLists(hdlg, pfd2, pfn, _T("*.dat"), _T(" (Miranda IM v0.x)"));
+ SearchForLists(hdlg, pfd1, NULL, _T("*.dat"), _T(" (Miranda IM v0.x)"));
+ if (lstrcmpi(pfd, pfd2))
+ SearchForLists(hdlg, pfd, NULL, _T("*.dat"), _T(" (Miranda IM v0.x)"));
+
+ mir_free(pfn);
+ mir_free(pfd2);
+ mir_free(pfd1);
+ mir_free(pfd);
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDC_BACK:
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_IMPORTTYPE,(LPARAM)ImportTypePageProc);
+ break;
+
+ case IDOK:
+ {
+ TCHAR filename[MAX_PATH];
+
+ GetDlgItemText(hdlg, IDC_FILENAME, filename, SIZEOF(filename));
+ if (_taccess(filename, 4)) {
+ MessageBox(hdlg, TranslateT("The given file does not exist. Please check that you have entered the name correctly."), TranslateT("Miranda Import"), MB_OK);
+ break;
+ }
+ lstrcpy(importFile, filename);
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_OPTIONS,(LPARAM)MirandaOptionsPageProc);
+ }
+ break;
+
+ case IDCANCEL:
+ PostMessage(GetParent(hdlg),WM_CLOSE,0,0);
+ break;
+
+ case IDC_LIST:
+ if(HIWORD(wParam)==LBN_SELCHANGE) {
+ int sel = SendDlgItemMessage(hdlg, IDC_LIST, LB_GETCURSEL, 0, 0);
+ if (sel == LB_ERR) break;
+ SetDlgItemText(hdlg, IDC_FILENAME, (TCHAR*)SendDlgItemMessage(hdlg, IDC_LIST, LB_GETITEMDATA, sel, 0));
+ }
+ break;
+
+ case IDC_OTHER:
+ {
+ OPENFILENAME ofn;
+ TCHAR str[MAX_PATH], text[256];
+ TCHAR *pfd;
+ int index;
+
+ pfd = Utils_ReplaceVarsT(_T("%miranda_profile%"));
+
+ // TranslateTS doesnt translate \0 separated strings
+ index = mir_sntprintf(text, 64, _T("%s (*.dat)"), TranslateT("Miranda IM database")) + 1;
+ _tcscpy(text + index, _T("*.dat")); index += 6;
+ index += mir_sntprintf(text + index, 64, _T("%s (*.*)"), TranslateT("All Files")) + 1;
+ _tcscpy(text + index, _T("*.*")); index += 4;
+ text[index] = 0;
+
+ GetDlgItemText(hdlg, IDC_FILENAME, str, SIZEOF(str));
+ ZeroMemory(&ofn, sizeof(ofn));
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hdlg;
+ ofn.lpstrFilter = text;
+ ofn.lpstrDefExt = _T("dat");
+ ofn.lpstrFile = str;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_DONTADDTORECENT;
+ ofn.nMaxFile = SIZEOF(str);
+ ofn.lpstrInitialDir = pfd;
+ if (GetOpenFileName(&ofn))
+ SetDlgItemText(hdlg,IDC_FILENAME,str);
+
+ mir_free(pfd);
+ break;
+ }
+ }
+ break;
+ case WM_DESTROY:
+ {
+ int i;
+
+ for(i=SendDlgItemMessage(hdlg,IDC_LIST,LB_GETCOUNT,0,0)-1;i>=0;i--)
+ mir_free((char*)SendDlgItemMessage(hdlg,IDC_LIST,LB_GETITEMDATA,i,0));
+ break;
+ } }
+
+ return FALSE;
+}
+
+
+INT_PTR CALLBACK MirandaOptionsPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hdlg);
+ EnableWindow(GetDlgItem(hdlg,IDC_RADIO_ALL), TRUE);
+ EnableWindow(GetDlgItem(hdlg,IDC_STATIC_ALL), TRUE);
+ EnableWindow(GetDlgItem(hdlg,IDC_RADIO_CONTACTS), TRUE);
+ EnableWindow(GetDlgItem(hdlg,IDC_STATIC_CONTACTS), TRUE);
+ EnableWindow(GetDlgItem(hdlg,IDC_RADIO_CUSTOM), TRUE);
+ EnableWindow(GetDlgItem(hdlg,IDC_STATIC_CUSTOM), TRUE);
+ CheckDlgButton(hdlg,IDC_RADIO_ALL,BST_UNCHECKED);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDC_BACK:
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_MIRANDADB,(LPARAM)MirandaPageProc);
+ break;
+
+ case IDOK:
+ if(IsDlgButtonChecked(hdlg,IDC_RADIO_ALL)) {
+ nImportOption = IMPORT_ALL;
+ nCustomOptions = 0;//IOPT_MSGSENT|IOPT_MSGRECV|IOPT_URLSENT|IOPT_URLRECV;
+ DoImport = MirandaImport;
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_PROGRESS,(LPARAM)ProgressPageProc);
+ break;
+ }
+
+ if(IsDlgButtonChecked(hdlg,IDC_RADIO_CONTACTS)) {
+ nImportOption = IMPORT_CONTACTS;
+ nCustomOptions = 0;
+ DoImport = MirandaImport;
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_PROGRESS,(LPARAM)ProgressPageProc);
+ break;
+ }
+
+ if(IsDlgButtonChecked(hdlg,IDC_RADIO_CUSTOM)) {
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_ADVOPTIONS,(LPARAM)MirandaAdvOptionsPageProc);
+ break;
+ }
+ break;
+
+ case IDCANCEL:
+ PostMessage(GetParent(hdlg), WM_CLOSE, 0, 0);
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static const UINT InControls[]={IDC_IN_MSG,IDC_IN_URL,IDC_IN_FT,IDC_IN_OTHER};
+static const UINT OutControls[]={IDC_OUT_MSG,IDC_OUT_URL,IDC_OUT_FT,IDC_OUT_OTHER};
+static const UINT SysControls[]={IDC_CONTACTS, IDC_SYSTEM};
+
+INT_PTR CALLBACK MirandaAdvOptionsPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hdlg);
+ {
+ struct tm *TM = NULL;
+ struct _SYSTEMTIME ST = {0};
+
+ dwSinceDate = DBGetContactSettingDword(NULL,IMPORT_MODULE,"ImportSinceTS",time(NULL));
+
+ TM = localtime(&dwSinceDate);
+
+ ST.wYear = TM->tm_year + 1900;
+ ST.wMonth = TM->tm_mon + 1;
+ ST.wDay = TM->tm_mday;
+
+ DateTime_SetSystemtime(GetDlgItem(hdlg,IDC_DATETIMEPICKER),GDT_VALID,&ST);
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDC_BACK:
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_OPTIONS,(LPARAM)MirandaOptionsPageProc);
+ break;
+
+ case IDOK:
+ DoImport = MirandaImport;
+ nImportOption = IMPORT_CUSTOM;
+ nCustomOptions = 0;
+
+ if (IsDlgButtonChecked(hdlg,IDC_CONTACTS))
+ nCustomOptions |= IOPT_CONTACTS | IOPT_GROUPS;
+ if (IsDlgButtonChecked(hdlg,IDC_SYSTEM))
+ nCustomOptions |= IOPT_SYSTEM;
+
+ // incoming
+ if (IsDlgButtonChecked(hdlg,IDC_IN_MSG))
+ nCustomOptions |= IOPT_MSGRECV;
+ if (IsDlgButtonChecked(hdlg,IDC_IN_URL))
+ nCustomOptions |= IOPT_URLRECV;
+ if (IsDlgButtonChecked(hdlg,IDC_IN_FT))
+ nCustomOptions |= IOPT_FILERECV;
+ if (IsDlgButtonChecked(hdlg,IDC_IN_OTHER))
+ nCustomOptions |= IOPT_OTHERRECV;
+
+ // outgoing
+ if (IsDlgButtonChecked(hdlg,IDC_OUT_MSG))
+ nCustomOptions |= IOPT_MSGSENT;
+ if (IsDlgButtonChecked(hdlg,IDC_OUT_URL))
+ nCustomOptions |= IOPT_URLSENT;
+ if (IsDlgButtonChecked(hdlg,IDC_OUT_FT))
+ nCustomOptions |= IOPT_FILESENT;
+ if (IsDlgButtonChecked(hdlg,IDC_OUT_OTHER))
+ nCustomOptions |= IOPT_OTHERSENT;
+
+ // since date
+ dwSinceDate = 0;
+
+ if ( IsDlgButtonChecked( hdlg, IDC_SINCE )) {
+ struct _SYSTEMTIME ST = {0};
+
+ if (DateTime_GetSystemtime(GetDlgItem(hdlg,IDC_DATETIMEPICKER), &ST) == GDT_VALID) {
+ struct tm TM = {0};
+
+ TM.tm_mday = ST.wDay;
+ TM.tm_mon = ST.wMonth - 1;
+ TM.tm_year = ST.wYear - 1900;
+
+ dwSinceDate = mktime(&TM);
+
+ DBWriteContactSettingDword(NULL,IMPORT_MODULE,"ImportSinceTS",dwSinceDate);
+ } }
+
+ if (nCustomOptions)
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_PROGRESS,(LPARAM)ProgressPageProc);
+ break;
+
+ case IDCANCEL:
+ PostMessage(GetParent(hdlg), WM_CLOSE, 0, 0);
+ break;
+
+ case IDC_SINCE:
+ EnableWindow(GetDlgItem(hdlg, IDC_DATETIMEPICKER), IsDlgButtonChecked(hdlg, IDC_SINCE));
+ break;
+
+ if (HIWORD(wParam) != STN_CLICKED)
+ break;
+
+ case IDC_ALL:
+ case IDC_INCOMING:
+ case IDC_OUTGOING:
+ {
+ int i;
+
+ if (LOWORD(wParam) == IDC_ALL)
+ for (i = 0; i < sizeof(SysControls)/sizeof(SysControls[0]); i++)
+ CheckDlgButton(hdlg,SysControls[i], !IsDlgButtonChecked(hdlg,SysControls[i]));
+
+ if (LOWORD(wParam) != IDC_OUTGOING)
+ for (i = 0; i < sizeof(InControls)/sizeof(InControls[0]); i++)
+ CheckDlgButton(hdlg,InControls[i], !IsDlgButtonChecked(hdlg,InControls[i]));
+
+ if (LOWORD(wParam) != IDC_INCOMING)
+ for (i = 0; i < sizeof(OutControls)/sizeof(OutControls[0]); i++)
+ CheckDlgButton(hdlg,OutControls[i], !IsDlgButtonChecked(hdlg,OutControls[i]));
+ }
+ break;
+
+ case IDC_MSG:
+ CheckDlgButton(hdlg,IDC_IN_MSG, !IsDlgButtonChecked(hdlg,IDC_IN_MSG));
+ CheckDlgButton(hdlg,IDC_OUT_MSG, !IsDlgButtonChecked(hdlg,IDC_OUT_MSG));
+ break;
+
+ case IDC_URL:
+ CheckDlgButton(hdlg,IDC_IN_URL, !IsDlgButtonChecked(hdlg,IDC_IN_URL));
+ CheckDlgButton(hdlg,IDC_OUT_URL, !IsDlgButtonChecked(hdlg,IDC_OUT_URL));
+ break;
+
+ case IDC_FT:
+ CheckDlgButton(hdlg,IDC_IN_FT, !IsDlgButtonChecked(hdlg,IDC_IN_FT));
+ CheckDlgButton(hdlg,IDC_OUT_FT, !IsDlgButtonChecked(hdlg,IDC_OUT_FT));
+ break;
+
+ case IDC_OTHER:
+ CheckDlgButton(hdlg,IDC_IN_OTHER, !IsDlgButtonChecked(hdlg,IDC_IN_OTHER));
+ CheckDlgButton(hdlg,IDC_OUT_OTHER, !IsDlgButtonChecked(hdlg,IDC_OUT_OTHER));
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+#ifndef INVALID_SET_FILE_POINTER
+#define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
+
+// Read header from file, returns null on failure
+struct DBHeader* GetHeader(HANDLE hDbFile)
+{
+ struct DBHeader* pdbHeader;
+ DWORD dwBytesRead;
+
+ if (( pdbHeader = (DBHeader*)calloc(1, sizeof(struct DBHeader))) == NULL )
+ return NULL;
+
+ // Goto start of file
+ if (SetFilePointer(hDbFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ return FALSE;
+
+ // Read header
+ if ( !ReadFile(hDbFile, pdbHeader, sizeof(struct DBHeader), &dwBytesRead, NULL ) ||
+ dwBytesRead != sizeof(struct DBHeader))
+ return NULL;
+
+ // Return pointer to header
+ return pdbHeader;
+}
+
+int CheckFileFormat(HANDLE hDbFile)
+{
+ struct DBHeader* pdbHeader;
+
+ // Read header
+ if (( pdbHeader = GetHeader(hDbFile)) == NULL )
+ return DB_INVALID;
+
+ // Check header signature
+ if (memcmp(pdbHeader->signature, &dbSignature, sizeof(pdbHeader->signature))) {
+ AddMessage( LPGEN("Signature mismatch" ));
+ return DB_INVALID;
+ }
+
+ // Determine Miranda version
+ switch (pdbHeader->version) {
+ case DB_000700:
+ AddMessage( LPGEN("This looks like a Miranda database, version 0.1.0.0 or above." ));
+ free(pdbHeader);
+ return DB_000700;
+
+ default:
+ AddMessage( LPGEN("Version mismatch" ));
+ free(pdbHeader);
+ return DB_INVALID;
+} }
+
+// High level Miranda DB access functions
+// Returns true if pValue points to the requested value
+
+BOOL GetSetting(HANDLE hDbFile, struct DBContact* pDbContact, char* pszModuleName, char* pszSettingName, DBVARIANT* pValue)
+{
+ struct DBContactSettings* pDbSettings;
+ if ( pDbSettings = GetSettingsGroupByModuleName(hDbFile, pDbContact, pszModuleName)) {
+ if ( GetSettingByName( pDbSettings, pszSettingName, pValue )) {
+ free(pDbSettings);
+ return TRUE;
+ }
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Failed to find setting %s" ), pszSettingName );
+ #endif
+ free(pDbSettings);
+ }
+#ifdef _LOGGING
+ else AddMessage( LPGEN("Failed to find module %s" ), pszModuleName );
+#endif
+
+ // Search failed
+ pValue->type = 0;
+ return FALSE;
+}
+
+// **
+// ** CONTACT CHAIN
+// **
+
+// Return offset to first contact
+DWORD FindFirstContact(struct DBHeader* pDbHeader)
+{
+ if (!pDbHeader)
+ return 0;
+
+ return pDbHeader->ofsFirstContact;
+}
+
+DWORD FindOwnerContact(struct DBHeader* pDbHeader)
+{
+ if (!pDbHeader)
+ return 0;
+
+ return pDbHeader->ofsUser;
+}
+
+// Return offset to next contact
+DWORD FindNextContact(struct DBContact* pDbContact)
+{
+ if (!pDbContact)
+ return 0;
+
+ if (pDbContact->signature != DBCONTACT_SIGNATURE)
+ return 0;
+
+ return pDbContact->ofsNext;
+}
+
+
+// Read the contact at offset 'dwOffset'
+// Returns true if successful and pDbContact points to the contact struct
+// pDbContact must point to allocated struct
+BOOL GetContact(HANDLE hDbFile, DWORD dwOffset, struct DBContact* pDbContact)
+{
+ DWORD dwBytesRead;
+
+ // Early reject
+ if (dwOffset == 0 || dwOffset >= dwFileSize)
+ return FALSE;
+
+ // ** Read and verify the struct
+
+ if (SetFilePointer(hDbFile, (LONG)dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ return FALSE;
+
+ if ((!ReadFile(hDbFile, pDbContact, sizeof(struct DBContact), &dwBytesRead, NULL)) ||
+ (dwBytesRead != sizeof(struct DBContact)))
+ return FALSE;
+
+ if ((pDbContact->signature != DBCONTACT_SIGNATURE) ||
+ (pDbContact->ofsNext >= dwFileSize))
+ return FALSE; // Contact corrupted
+
+ return TRUE;
+}
+
+// Return ptr to next setting in settings struct
+char* GetNextSetting(char* pDbSetting)
+{
+ // Get next setting
+ pDbSetting = pDbSetting + *pDbSetting+1; // Skip name
+ switch( *(BYTE*)pDbSetting ) {
+ case DBVT_BYTE:
+ pDbSetting = pDbSetting+1+1;
+ break;
+
+ case DBVT_WORD:
+ pDbSetting = pDbSetting+1+2;
+ break;
+
+ case DBVT_DWORD:
+ pDbSetting = pDbSetting+1+4;
+ break;
+
+ case DBVT_ASCIIZ:
+ case DBVT_UTF8:
+ case DBVT_BLOB:
+ case DBVTF_VARIABLELENGTH:
+ pDbSetting = pDbSetting + 3 + *(WORD*)(pDbSetting+1);
+ break;
+
+ case DBVT_DELETED:
+ AddMessage( LPGEN("DEBUG: Deleted setting treated as 0-length setting"));
+ pDbSetting = pDbSetting+1;
+ break;
+
+ default:
+ // Unknown datatype assert
+ AddMessage( LPGEN("ERROR: Faulty settings chain"));
+ return NULL;
+ }
+
+ return pDbSetting;
+}
+
+
+// **
+// ** SETTINGS CHAIN
+// **
+
+// Return the settings at offset 'dwOffset'
+BOOL GetSettingsGroup(HANDLE hDbFile, DWORD dwOffset, struct DBContactSettings** pDbSettings)
+{
+ DWORD dwBytesRead, dwBlobSize, dwHead;
+ struct DBContactSettings pSettings;
+
+ // Early reject
+ if (dwOffset == 0 || dwOffset >= dwFileSize)
+ return FALSE;
+
+ // ** Read and verify the struct
+ if (SetFilePointer(hDbFile, dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ return FALSE;
+
+ dwHead = offsetof(struct DBContactSettings, blob);
+ if ((!ReadFile(hDbFile, &pSettings, dwHead, &dwBytesRead, NULL)) ||
+ (dwBytesRead != dwHead))
+ return FALSE;
+
+ if (pSettings.signature != DBCONTACTSETTINGS_SIGNATURE)
+ return FALSE; // Setttings corrupted
+
+ // ** Read the struct and the following blob
+ dwBlobSize = pSettings.cbBlob;
+ if (!(*pDbSettings = (DBContactSettings *)calloc(1, sizeof(struct DBContactSettings) + dwBlobSize)))
+ return FALSE;
+
+ memcpy(*pDbSettings, &pSettings, dwHead );
+
+ if ((!ReadFile(hDbFile, (*pDbSettings)->blob, sizeof(struct DBContactSettings) - dwHead + dwBlobSize, &dwBytesRead, NULL)) ||
+ (dwBytesRead != sizeof(struct DBContactSettings) - dwHead + dwBlobSize))
+ {
+ free(*pDbSettings);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// pDbContact is a ptr to a struct DBContact
+// Returns pointer to a struct DBContactSettings or NULL
+struct DBContactSettings* GetSettingsGroupByModuleName(HANDLE hDbFile, struct DBContact* pDbContact, char* pszName)
+{
+ char* pszGroupName;
+ struct DBContactSettings* pSettingsGroup;
+ DWORD dwGroupOfs;
+
+ // Get ptr to first settings group
+ if (!(dwGroupOfs = pDbContact->ofsFirstSettings))
+ return NULL; // No settings exists in this contact
+
+ // Loop over all settings groups
+ while (dwGroupOfs && dwGroupOfs < dwFileSize) {
+ pSettingsGroup = NULL;
+
+ // Read and verify the struct
+ if (!GetSettingsGroup(hDbFile, dwGroupOfs, &pSettingsGroup))
+ return NULL; // Bad struct
+
+ // Struct OK, now get the name
+ if ((pszGroupName = GetName(hDbFile, pSettingsGroup->ofsModuleName))) {
+
+ // Is it the right one?
+ if (strcmp(pszGroupName, pszName) == 0) {
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Found module: %s"), pszGroupName );
+ #endif
+ return pSettingsGroup;
+ }
+ #ifdef _LOGGING
+ else AddMessage( LPGEN("Ignoring module: %s"), pszGroupName );
+ #endif
+ }
+ else AddMessage( LPGEN("Warning: Found module with no name"));
+
+ dwGroupOfs = pSettingsGroup->ofsNext;
+
+ if (pSettingsGroup)
+ free(pSettingsGroup);
+ }
+
+ // Search failed
+ return NULL;
+}
+
+// pDbSettings must point to a complete DBContactSettings struct in memory
+int GetSettingByName(struct DBContactSettings* pDbSettings, char* pszSettingName, DBVARIANT* dbv)
+{
+ char pszName[256];
+ // We need at least one setting to start with
+ char* pDbSetting = (char*)pDbSettings->blob;
+ if ( !pDbSetting )
+ return FALSE;
+
+ // ** pDbSettings now points to the first setting in this module
+
+ // Loop over all settings
+ while (pDbSetting && *pDbSetting) {
+ memcpy(pszName, pDbSetting+1, *pDbSetting);
+ pszName[*pDbSetting] = 0;
+
+ // Is this the right one?
+ if (strcmp(pszSettingName, pszName) == 0) {
+ return GetSettingValue(pDbSetting, dbv);
+ }
+
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Ignoring setting: %s"), pszName );
+ #endif
+ pDbSetting = GetNextSetting(pDbSetting);
+ }
+
+ // Search failed
+ return FALSE;
+}
+
+// dwSettingpointer points to a valid DBSettings struct
+int GetSettingValue(char* pBlob, DBVARIANT* dbv)
+{
+ #ifdef _LOGGING
+ {
+ char* pszName = calloc((*pBlob)+1, 1);
+ memcpy(pszName, pBlob+1, *pBlob);
+ AddMessage( LPGEN("Getting type %u value for setting: %s"), (BYTE)*(pBlob+(*pBlob)+1), pszName );
+ free(pszName);
+ }
+ #endif
+
+ // Skip name
+ pBlob = pBlob + (*pBlob)+1;
+ dbv->type = ( BYTE )*pBlob++;
+
+ // Check what type it is
+ switch( dbv->type ) {
+ case DBVT_BYTE:
+ dbv->bVal = *pBlob;
+ return TRUE;
+
+ case DBVT_WORD:
+ dbv->wVal = *(WORD*)pBlob;
+ return TRUE;
+
+ case DBVT_DWORD:
+ dbv->dVal = *(DWORD*)pBlob;
+ return TRUE;
+
+ case DBVT_ASCIIZ:
+ case DBVT_UTF8:
+ dbv->cchVal = *(WORD*)pBlob;
+ dbv->pszVal = (char *)calloc( dbv->cchVal+1, sizeof( char ));
+ memcpy( dbv->pszVal, pBlob+2, dbv->cchVal );
+ dbv->pszVal[ dbv->cchVal ] = 0;
+ return TRUE;
+
+ case DBVTF_VARIABLELENGTH:
+ case DBVT_BLOB:
+ dbv->cpbVal = *(WORD*)pBlob;
+ dbv->pbVal = (BYTE *)calloc( dbv->cpbVal+1, sizeof( char ));
+ memcpy( dbv->pbVal, pBlob+2, dbv->cpbVal );
+ dbv->pbVal[ dbv->cpbVal ] = 0;
+ return TRUE;
+
+ case DBVT_DELETED:
+ AddMessage( LPGEN("DEBUG: Deleted setting treated as 0-length setting"));
+
+ default:
+ dbv->type = DBVT_DELETED;
+ }
+
+ return FALSE;
+}
+
+void FreeVariant( DBVARIANT* dbv )
+{
+ switch( dbv->type ) {
+ case DBVT_ASCIIZ:
+ case DBVT_UTF8:
+ if ( dbv->pszVal )
+ free( dbv->pszVal );
+ break;
+
+ case DBVTF_VARIABLELENGTH:
+ case DBVT_BLOB:
+ if ( dbv->pbVal )
+ free( dbv->pbVal );
+ break;
+ }
+
+ dbv->type = 0;
+}
+
+void WriteVariant( HANDLE hContact, const char* module, const char* var, DBVARIANT* dbv )
+{
+ DBCONTACTWRITESETTING dbw;
+ dbw.szModule = module;
+ dbw.szSetting = var;
+ dbw.value = *dbv;
+ CallService( MS_DB_CONTACT_WRITESETTING, (WPARAM)hContact, (LPARAM)&dbw );
+}
+
+// Returns true if pDBEI has been filled in with nice values
+// Don't forget to free those pointers!
+BOOL GetEvent(HANDLE hDbFile, DWORD dwOffset, DBEVENTINFO* pDBEI)
+{
+ DWORD dwBytesRead;
+ struct DBEvent pEvent;
+ static char pBlob[65536];
+
+ // Early reject
+ if (dwOffset == 0 || dwOffset >= dwFileSize)
+ return FALSE;
+
+ // ** Read and verify the struct
+ if (SetFilePointer(hDbFile, dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ return FALSE;
+
+ if (!ReadFile(hDbFile, &pEvent, offsetof(struct DBEvent, blob), &dwBytesRead, NULL) ||
+ (dwBytesRead != offsetof(struct DBEvent, blob)))
+ return FALSE;
+
+ if (pEvent.signature != DBEVENT_SIGNATURE)
+ return FALSE; // Event corrupted
+
+ // ** Read the blob
+ if ((!ReadFile(hDbFile, pBlob, pEvent.cbBlob, &dwBytesRead, NULL)) ||
+ (dwBytesRead != pEvent.cbBlob))
+ {
+ return FALSE;
+ }
+
+ // ** Copy the static part to the event info struct
+ pDBEI->timestamp = pEvent.timestamp;
+ pDBEI->eventType = pEvent.eventType;
+ pDBEI->cbSize = sizeof(DBEVENTINFO);
+ pDBEI->cbBlob = pEvent.cbBlob;
+ pDBEI->pBlob = (PBYTE)pBlob;
+ pDBEI->flags = (pEvent.flags & ~(DBEF_SENT+DBEF_READ)) +
+ ((pEvent.flags & DBEF_SENT) ? DBEF_SENT : DBEF_READ ); // Imported events are always marked READ
+
+ if (!(pDBEI->szModule = GetName(hDbFile, pEvent.ofsModuleName))) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// Returns a pointer to a string with the name
+// from a DBModuleName struct if given a file offset
+// Returns NULL on failure
+char* GetName(HANDLE hDbFile, DWORD dwOffset)
+{
+ static DWORD dwLastOffset = 0;
+ static HANDLE hLastDbFile = NULL;
+ static char szName[256] = {0};
+
+ DWORD dwBytesRead;
+ struct DBModuleName pModule;
+
+ // Early reject
+ if (dwOffset == 0 || dwOffset >= dwFileSize)
+ return FALSE;
+
+ // Quick lookup
+ if (dwOffset == dwLastOffset && hDbFile == hLastDbFile)
+ return szName;
+
+ // ** Read and verify the name struct
+ if (SetFilePointer(hDbFile, dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ return NULL;
+
+ if ((!ReadFile(hDbFile, &pModule, offsetof(struct DBModuleName, name), &dwBytesRead, NULL)) ||
+ (dwBytesRead != offsetof(struct DBModuleName, name)))
+ return NULL;
+
+ if (pModule.signature != DBMODULENAME_SIGNATURE) {
+ AddMessage( LPGEN("Modulename corrupted"));
+ return NULL; // ModuleName corrupted
+ }
+
+ // ** Name struct OK, now read name into string buffer
+ if ((!ReadFile(hDbFile, szName, pModule.cbName, &dwBytesRead, NULL)) || (dwBytesRead != pModule.cbName)) {
+ return NULL;
+ }
+
+ // terminate string
+ szName[pModule.cbName] = 0;
+
+ // update last offset
+ dwLastOffset = dwOffset;
+ hLastDbFile = hDbFile;
+
+ return szName;
+}
+
+DWORD FindNextEvent(HANDLE hDbFile, DWORD dwOffset)
+{
+ DWORD dwBytesRead;
+ struct DBEvent pEvent;
+
+ // Early reject
+ if (dwOffset == 0 || dwOffset >= dwFileSize)
+ return FALSE;
+
+ // ** Read and verify the struct
+ if (SetFilePointer(hDbFile, dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ return FALSE;
+
+ if ((!ReadFile(hDbFile, &pEvent, sizeof(struct DBEvent), &dwBytesRead, NULL)) ||
+ (dwBytesRead != sizeof(struct DBEvent)))
+ return FALSE;
+
+ if ( pEvent.signature != DBEVENT_SIGNATURE || pEvent.ofsNext > dwFileSize )
+ return FALSE; // Event corrupted
+
+ return pEvent.ofsNext;
+}
+
+int ImportGroups(HANDLE hDbFile, struct DBHeader* pdbHeader)
+{
+ struct DBContactSettings* pDbSettings;
+ struct DBContact DbContact;
+ char* pSetting;
+ DWORD dwOffset;
+ int nGroups = 0;
+
+ // Find owner data
+ dwOffset = pdbHeader->ofsUser;
+ if (!GetContact(hDbFile, dwOffset, &DbContact)) {
+ AddMessage( LPGEN("No owner found."));
+ return -1;
+ }
+
+ // Find the module with the groups, and import them all
+ if ( pDbSettings = GetSettingsGroupByModuleName( hDbFile, &DbContact, "CListGroups" )) {
+ pSetting = (char *)pDbSettings->blob;
+ while ( pSetting && *pSetting ) {
+ DBVARIANT dbv;
+ if ( GetSettingValue( pSetting, &dbv )) {
+ if ( CreateGroup( dbv.type, dbv.pszVal+1, NULL ))
+ nGroups++;
+ FreeVariant( &dbv );
+ }
+ pSetting = GetNextSetting(pSetting);
+ }
+ free(pDbSettings);
+ }
+
+ return nGroups;
+}
+
+HANDLE ImportContact(HANDLE hDbFile, struct DBContact Contact)
+{
+ HANDLE hContact;
+ DBVARIANT group, nick, dbv;
+ char* pszProtoName;
+ char* pszUniqueSetting;
+ char* pszUserName;
+ char id[ 40 ];
+
+ // Check what protocol this contact belongs to
+ if ( !GetSetting( hDbFile, &Contact, "Protocol", "p", &dbv )) {
+ AddMessage( LPGEN("Skipping contact with no protocol"));
+ return INVALID_HANDLE_VALUE;
+ }
+
+ pszProtoName = NEWSTR_ALLOCA( dbv.pszVal );
+ FreeVariant( &dbv );
+
+ if ( !IsProtocolLoaded( pszProtoName )) {
+ AddMessage( LPGEN("Skipping contact, %s not installed."), pszProtoName );
+ return INVALID_HANDLE_VALUE;
+ }
+
+ // Skip protocols with no unique id setting (some non IM protocols return NULL)
+ pszUniqueSetting = (char*)CallProtoService(pszProtoName, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
+ if ( !pszUniqueSetting || (INT_PTR)pszUniqueSetting == CALLSERVICE_NOTFOUND ) {
+ AddMessage( LPGEN("Skipping non-IM contact (%s)"), pszProtoName );
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if ( !GetSetting(hDbFile, &Contact, pszProtoName, pszUniqueSetting, &dbv )) {
+ AddMessage( LPGEN("Skipping %s contact, ID not found"), pszProtoName );
+ return INVALID_HANDLE_VALUE;
+ }
+
+ // Does the contact already exist?
+ if ( dbv.type == DBVT_DWORD ) {
+ pszUserName = _ltoa( dbv.dVal, id, 10 );
+ hContact = HContactFromNumericID( pszProtoName, pszUniqueSetting, dbv.dVal );
+ }
+ else {
+ pszUserName = NEWSTR_ALLOCA( dbv.pszVal );
+ hContact = HContactFromID( pszProtoName, pszUniqueSetting, dbv.pszVal );
+ }
+
+ if ( hContact != INVALID_HANDLE_VALUE ) {
+ AddMessage( LPGEN("Skipping duplicate %s contact %s"), pszProtoName, pszUserName );
+ FreeVariant( &dbv );
+ return INVALID_HANDLE_VALUE;
+ }
+ // No, add contact and copy some important settings
+ GetSetting(hDbFile, &Contact, "CList", "Group", &group);
+
+ if ( !GetSetting( hDbFile, &Contact, "CList", "MyHandle", &nick ))
+ GetSetting(hDbFile, &Contact, pszProtoName, "Nick", &nick );
+
+ hContact = AddContact( hdlgProgress, pszProtoName, pszUniqueSetting, &dbv, &nick, &group );
+
+ if ( hContact != INVALID_HANDLE_VALUE) {
+
+ // Hidden?
+ if ( GetSetting( hDbFile, &Contact, "CList", "Hidden", &dbv )) {
+ WriteVariant( hContact, "CList", "Hidden", &dbv );
+ FreeVariant( &dbv );
+ }
+ // Ignore settings
+ if ( GetSetting( hDbFile, &Contact, "Ignore", "Mask1", &dbv )) {
+ WriteVariant( hContact, "Ignore", "Mask1", &dbv );
+ FreeVariant( &dbv );
+ }
+
+ // Apparent mode
+ if ( GetSetting( hDbFile, &Contact, pszProtoName, "ApparentMode", &dbv )) {
+ WriteVariant( hContact, pszProtoName, "ApparentMode", &dbv );
+ FreeVariant( &dbv );
+ }
+
+ // Nick
+ if ( GetSetting( hDbFile, &Contact, pszProtoName, "Nick", &dbv )) {
+ WriteVariant( hContact, pszProtoName, "Nick", &dbv );
+ FreeVariant( &dbv );
+ }
+
+ // Myhandle
+ if ( GetSetting( hDbFile, &Contact, pszProtoName, "MyHandle", &dbv )) {
+ WriteVariant( hContact, pszProtoName, "MyHandle", &dbv );
+ FreeVariant( &dbv );
+ }
+
+ // First name
+ if ( GetSetting( hDbFile, &Contact, pszProtoName, "FirstName", &dbv )) {
+ WriteVariant( hContact, pszProtoName, "FirstName", &dbv );
+ FreeVariant( &dbv );
+ }
+
+ // Last name
+ if ( GetSetting( hDbFile, &Contact, pszProtoName, "LastName", &dbv )) {
+ WriteVariant( hContact, pszProtoName, "LastName", &dbv );
+ FreeVariant( &dbv );
+ }
+
+ // About
+ if ( GetSetting( hDbFile, &Contact, pszProtoName, "About", &dbv )) {
+ WriteVariant( hContact, pszProtoName, "About", &dbv );
+ FreeVariant( &dbv );
+ }
+ }
+ else AddMessage( LPGEN("Unknown error while adding %s contact %s"), pszProtoName, pszUserName );
+
+ return hContact;
+}
+
+// This function should always be called after contact import. That is
+// why there are no messages for errors related to contacts. Those
+// would only be a repetition of the messages printed during contact
+// import.
+
+static void ImportHistory(HANDLE hDbFile, struct DBContact Contact, PROTOCOLDESCRIPTOR **protocol, int protoCount)
+{
+ HANDLE hContact = INVALID_HANDLE_VALUE;
+ DWORD dwOffset;
+ MSG msg;
+ DBVARIANT proto;
+ int i, skipAll, bIsVoidContact;
+
+ // Is it contats history import?
+ if ( protoCount == 0 ) {
+
+ // Check what protocol this contact belongs to
+ if ( GetSetting( hDbFile, &Contact, "Protocol", "p", &proto )) {
+
+ // Protocol installed?
+ if ( IsProtocolLoaded( proto.pszVal )) {
+ // Is contact in database?
+ char* pszUniqueSetting = (char*)CallProtoService( proto.pszVal, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0);
+
+ // Skip protocols with no unique id setting (some non IM protocols return NULL)
+ if ( pszUniqueSetting && ( INT_PTR )pszUniqueSetting != CALLSERVICE_NOTFOUND ) {
+ DBVARIANT dbv;
+ if ( GetSetting( hDbFile, &Contact, proto.pszVal, pszUniqueSetting, &dbv )) {
+ if ( dbv.type == DBVT_DWORD )
+ hContact = HContactFromNumericID( proto.pszVal, pszUniqueSetting, dbv.dVal );
+ else
+ hContact = HContactFromID( proto.pszVal, pszUniqueSetting, dbv.pszVal );
+ FreeVariant( &dbv );
+ } } }
+ FreeVariant( &proto );
+ }
+ }
+ else hContact = NULL; //system history import
+
+ // OK to import this chain?
+ if (hContact == INVALID_HANDLE_VALUE) {
+ nSkippedContacts++;
+ return;
+ }
+
+ i = skipAll = 0;
+ bIsVoidContact = CallService( MS_DB_EVENT_GETCOUNT, ( WPARAM )hContact, 0 ) == 0;
+
+ // Get the start of the event chain
+ dwOffset = Contact.ofsFirstEvent;
+ while (dwOffset) {
+ int skip = 0;
+
+ // Copy the event and import it
+ DBEVENTINFO dbei = { 0 };
+ if (GetEvent(hDbFile, dwOffset, &dbei)) {
+ // check protocols during system history import
+ if (hContact == NULL) {
+ int i;
+ skipAll = 1;
+
+ for(i = 0; i < protoCount; i++)
+ if (!strcmp(dbei.szModule, protocol[i]->szName)) { //&& protocol[i]->type == PROTOTYPE_PROTOCOL)
+ skipAll = 0;
+ break;
+ }
+
+ skip = skipAll;
+ }
+
+ // custom filtering
+ if (!skip && nImportOption == IMPORT_CUSTOM) {
+ BOOL sent = (dbei.flags&DBEF_SENT);
+
+ if (dbei.timestamp < (DWORD)dwSinceDate)
+ skip = 1;
+
+ if (!skip) {
+ if (hContact) {
+ skip = 1;
+ switch(dbei.eventType) {
+ case EVENTTYPE_MESSAGE:
+ if ((sent?IOPT_MSGSENT:IOPT_MSGRECV)&nCustomOptions)
+ skip = 0;
+ break;
+ case EVENTTYPE_FILE:
+ if ((sent?IOPT_FILESENT:IOPT_FILERECV)&nCustomOptions)
+ skip = 0;
+ break;
+ case EVENTTYPE_URL:
+ if ((sent?IOPT_URLSENT:IOPT_URLRECV)&nCustomOptions)
+ skip = 0;
+ break;
+ default:
+ if ((sent?IOPT_OTHERSENT:IOPT_OTHERRECV)&nCustomOptions)
+ skip = 0;
+ break;
+ }
+ }
+ else if ( !( nCustomOptions & IOPT_SYSTEM ))
+ skip = 1;
+ }
+
+ if (skip)
+ nSkippedEvents++;
+ }
+
+ if (!skip) {
+ // Check for duplicate entries
+ if ( !IsDuplicateEvent( hContact, dbei )) {
+ // Add dbevent
+ if (!bIsVoidContact)
+ dbei.flags &= ~DBEF_FIRST;
+ if (CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei))
+ nMessagesCount++;
+ else
+ AddMessage( LPGEN("Failed to add message"));
+ }
+ else
+ nDupes++;
+ }
+ }
+
+ if ( !( i%10 )) {
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ } }
+
+ // skip this chain if needed
+ if ( skipAll )
+ break;
+
+ // Get next event
+ dwOffset = FindNextEvent(hDbFile, dwOffset);
+ i++;
+ }
+}
+
+static void MirandaImport(HWND hdlg)
+{
+ int nDBVersion;
+ int i;
+ int nNumberOfContacts = 0;
+ MSG msg;
+ DWORD dwTimer;
+ DWORD dwOffset;
+ HANDLE hFile;
+ char* pszModuleName = NULL;
+ struct DBHeader* pdbHeader = NULL;
+ struct DBContact Contact;
+
+ // Just to keep the macros happy
+ hdlgProgress = hdlg;
+
+ // Reset statistics
+ nSkippedEvents = 0;
+ nDupes = 0;
+ nContactsCount = 0;
+ nMessagesCount = 0;
+ nGroupsCount = 0;
+ nSkippedContacts = 0;
+ SetProgress(0);
+
+ // Open database
+ hFile = CreateFile(importFile,
+ GENERIC_READ, // open for reading
+ 0, // do not share
+ NULL, // no security
+ OPEN_EXISTING, // existing file only
+ FILE_ATTRIBUTE_NORMAL, // normal file
+ NULL); // no attr. template
+
+ // Read error
+ if (hFile == INVALID_HANDLE_VALUE) {
+ AddMessage( LPGEN("Could not open file."));
+ SetProgress(100);
+ return;
+ }
+
+ // Check filesize
+ dwFileSize = GetFileSize(hFile, NULL) ;
+ if ((dwFileSize == INVALID_FILE_SIZE) || (dwFileSize < sizeof(struct DBHeader))) {
+ AddMessage( LPGEN("This is not a valid Miranda IM database."));
+ SetProgress(100);
+ CloseHandle(hFile);
+ return;
+ }
+
+ // Check header and database version
+ nDBVersion = CheckFileFormat(hFile);
+ if (nDBVersion == DB_INVALID) {
+ AddMessage( LPGEN("This is not a valid Miranda IM database."));
+ SetProgress(100);
+ CloseHandle(hFile);
+ return;
+ }
+
+ // Load database header
+ if (!(pdbHeader = GetHeader(hFile))) {
+ AddMessage( LPGEN("Read failure."));
+ SetProgress(100);
+ CloseHandle(hFile);
+ return;
+ }
+
+ // Get number of contacts
+ nNumberOfContacts = pdbHeader->contactCount;
+ AddMessage( LPGEN("Number of contacts in database: %d"), nNumberOfContacts );
+ AddMessage( "" );
+
+ // Configure database for fast writing
+ CallService(MS_DB_SETSAFETYMODE, FALSE, 0);
+
+ // Start benchmark timer
+ dwTimer = time(NULL);
+
+ // Import Groups
+ if (nImportOption == IMPORT_ALL || (nCustomOptions & IOPT_GROUPS)) {
+ AddMessage( LPGEN("Importing groups."));
+ nGroupsCount = ImportGroups(hFile, pdbHeader);
+ if (nGroupsCount == -1)
+ AddMessage( LPGEN("Group import failed."));
+
+ AddMessage( "" );
+ }
+ // End of Import Groups
+
+ // Import Contacts
+ if (nImportOption != IMPORT_CUSTOM || (nCustomOptions & IOPT_CONTACTS)) {
+ AddMessage( LPGEN("Importing contacts."));
+ i = 1;
+ dwOffset = FindFirstContact(pdbHeader);
+ while (dwOffset && (dwOffset < dwFileSize)) {
+ if (!GetContact(hFile, dwOffset, &Contact)) {
+ AddMessage( LPGEN("ERROR: Chain broken, no valid contact at %d"), dwOffset );
+ SetProgress(100);
+ break;
+ }
+
+ if (ImportContact(hFile, Contact) != INVALID_HANDLE_VALUE)
+ nContactsCount++;
+
+ // Update progress bar
+ SetProgress(100 * i / nNumberOfContacts);
+ i++;
+
+ // Process queued messages
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ // Get next contact in chain
+ dwOffset = FindNextContact(&Contact);
+ }
+ }
+ else AddMessage( LPGEN("Skipping new contacts import."));
+ AddMessage( "" );
+ // End of Import Contacts
+
+ // Import history
+ if (nImportOption != IMPORT_CONTACTS) {
+ // Import NULL contact message chain
+ if (nImportOption == IMPORT_ALL || (nCustomOptions & IOPT_SYSTEM)) {
+ AddMessage( LPGEN("Importing system history."));
+ dwOffset = FindOwnerContact(pdbHeader);
+ if (!GetContact(hFile, dwOffset, &Contact)) {
+ AddMessage( LPGEN("ERROR: Chain broken, no valid contact at %d"), dwOffset );
+ SetProgress(100);
+ }
+ else {
+ PROTOCOLDESCRIPTOR **protocol;
+ int protoCount;
+
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&protoCount, (LPARAM)&protocol);
+
+ if (protoCount > 0)
+ ImportHistory(hFile, Contact, protocol, protoCount);
+ }
+ }
+ else AddMessage( LPGEN("Skipping system history import."));
+
+ AddMessage( "" );
+
+ // Import other contact messages
+ if (nImportOption == IMPORT_ALL || (nCustomOptions & 2046)) { // 2 - 1024 types
+ AddMessage( LPGEN("Importing history."));
+ dwOffset = FindFirstContact(pdbHeader);
+ for(i=1; i <= nNumberOfContacts; i++) {
+ if (!GetContact(hFile, dwOffset, &Contact)) {
+ AddMessage( LPGEN("ERROR: Chain broken, no valid contact at %d"), dwOffset );
+ SetProgress(100);
+ break;
+ }
+
+ ImportHistory(hFile, Contact, NULL, 0);
+
+ SetProgress(100 * i / nNumberOfContacts);
+ dwOffset = FindNextContact(&Contact);
+ }
+ }
+ else AddMessage( LPGEN("Skipping history import."));
+
+ AddMessage( "" );
+ }
+ // End of Import History
+
+ // Restore database writing mode
+ CallService(MS_DB_SETSAFETYMODE, TRUE, 0);
+
+ // Clean up before exit
+ CloseHandle(hFile);
+ free(pdbHeader);
+
+ // Stop timer
+ dwTimer = time(NULL) - dwTimer;
+
+ // Print statistics
+ AddMessage( LPGEN("Import completed in %d seconds."), dwTimer );
+ SetProgress(100);
+ AddMessage((nImportOption == IMPORT_CONTACTS) ?
+ LPGEN("Added %d contacts and %d groups.") : LPGEN("Added %d contacts, %d groups and %d events."),
+ nContactsCount, nGroupsCount, nMessagesCount);
+
+ if ( nImportOption != IMPORT_CONTACTS ) {
+ if (nSkippedContacts)
+ AddMessage( LPGEN("Skipped %d contacts."), nSkippedContacts );
+
+ AddMessage((nImportOption == IMPORT_CUSTOM) ?
+ LPGEN("Skipped %d duplicates and %d filtered events.") : LPGEN("Skipped %d duplicates."),
+ nDupes, nSkippedEvents);
+} }
diff --git a/plugins/Import/src/mirandadb0700.h b/plugins/Import/src/mirandadb0700.h new file mode 100644 index 0000000000..2c77588951 --- /dev/null +++ b/plugins/Import/src/mirandadb0700.h @@ -0,0 +1,142 @@ +/*
+
+Import plugin for Miranda IM
+
+Copyright (C) 2001,2002,2003,2004 Martin Öberg, Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+
+
+//all offsets are relative to the start of the file
+//offsets are 0 if there is nothing in the chain or this is the last in the
+//chain
+
+
+
+
+/* tree diagram
+
+DBHeader
+ |-->end of file (plain offset)
+ |-->first contact (DBContact)
+ | |-->next contact (DBContact)
+ | | \--> ...
+ | |-->first settings (DBContactSettings)
+ | | |-->next settings (DBContactSettings)
+ | | | \--> ...
+ | | \-->module name (DBModuleName)
+ | \-->first/last/firstunread event
+ |-->user contact (DBContact)
+ | |-->next contact=NULL
+ | |-->first settings as above
+ | \-->first/last/firstunread event as above
+ \-->first module name (DBModuleName)
+ \-->next module name (DBModuleName)
+ \--> ...
+*/
+
+#define DB_THIS_VERSION 0x00000700u
+
+#include <pshpack1.h>
+struct DBHeader {
+ BYTE signature[16]; // 'Miranda ICQ DB',0,26
+ DWORD version; //as 4 bytes, ie 1.2.3.10=0x0102030a
+ //this version is 0x00000700
+ DWORD ofsFileEnd; //offset of the end of the database - place to write
+ //new structures
+ DWORD slackSpace; //a counter of the number of bytes that have been
+ //wasted so far due to deleting structures and/or
+ //re-making them at the end. We should compact when
+ //this gets above a threshold
+ DWORD contactCount; //number of contacts in the chain,excluding the user
+ DWORD ofsFirstContact; //offset to first struct DBContact in the chain
+ DWORD ofsUser; //offset to struct DBContact representing the user
+ DWORD ofsFirstModuleName; //offset to first struct DBModuleName in the chain
+};
+
+#define DBCONTACT_SIGNATURE 0x43DECADEu
+struct DBContact {
+ DWORD signature;
+ DWORD ofsNext; //offset to the next contact in the chain. zero if
+ //this is the 'user' contact or the last contact
+ //in the chain
+ DWORD ofsFirstSettings; //offset to the first DBContactSettings in the
+ //chain for this contact.
+ DWORD eventCount; //number of events in the chain for this contact
+ DWORD ofsFirstEvent,ofsLastEvent; //offsets to the first and last DBEvent in
+ //the chain for this contact
+ DWORD ofsFirstUnreadEvent; //offset to the first (chronological) unread event
+ //in the chain, 0 if all are read
+ DWORD timestampFirstUnread; //timestamp of the event at ofsFirstUnreadEvent
+};
+
+#define DBMODULENAME_SIGNATURE 0x4DDECADEu
+struct DBModuleName {
+ DWORD signature;
+ DWORD ofsNext; //offset to the next module name in the chain
+ BYTE cbName; //number of characters in this module name
+ char name[1]; //name, no nul terminator
+};
+
+#define DBCONTACTSETTINGS_SIGNATURE 0x53DECADEu
+struct DBContactSettings {
+ DWORD signature;
+ DWORD ofsNext; //offset to the next contactsettings in the chain
+ DWORD ofsModuleName; //offset to the DBModuleName of the owner of these
+ //settings
+ DWORD cbBlob; //size of the blob in bytes. May be larger than the
+ //actual size for reducing the number of moves
+ //required using granularity in resizing
+ BYTE blob[1]; //the blob. a back-to-back sequence of DBSetting
+ //structs, the last has cbName=0
+};
+
+/* not a valid structure, content is figured out on the fly
+struct DBSetting {
+ BYTE cbName; //number of bytes in the name of this setting
+ //this =0 marks the end
+ char szName[...]; //setting name, excluding nul
+ BYTE dataType; //type of data. see m_database.h, db/contact/getsetting
+ union { //a load of types of data, length is defined by dataType
+ BYTE bVal; WORD wVal; DWORD dVal;
+ struct {
+ WORD cbString;
+ char szVal[...]; //excludes nul terminator
+ };
+ struct {
+ WORD cbBlob;
+ BYTE blobVal[...];
+ };
+ };
+};
+*/
+
+#define DBEVENT_SIGNATURE 0x45DECADEu
+struct DBEvent {
+ DWORD signature;
+ DWORD ofsPrev,ofsNext; //offset to the previous and next events in the
+ //chain. Chain is sorted chronologically
+ DWORD ofsModuleName; //offset to a DBModuleName struct of the name of
+ //the owner of this event
+ DWORD timestamp; //seconds since 00:00:00 01/01/1970
+ DWORD flags; //see m_database.h, db/event/add
+ WORD eventType; //module-defined event type
+ DWORD cbBlob; //number of bytes in the blob
+ BYTE blob[1]; //the blob. module-defined formatting
+};
+#include <poppack.h>
diff --git a/plugins/Import/src/mirandahistory.cpp b/plugins/Import/src/mirandahistory.cpp new file mode 100644 index 0000000000..ba7a923df7 --- /dev/null +++ b/plugins/Import/src/mirandahistory.cpp @@ -0,0 +1,208 @@ +/*
+Miranda ICQ: the free icq client for MS Windows
+Copyright (C) 2000-2 Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include <windows.h>
+#include <stdio.h>
+#include <time.h>
+#include "resource.h"
+#include "import.h"
+
+BOOL CALLBACK MirandaPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+BOOL CALLBACK FinishedPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+BOOL CALLBACK ProgressPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+extern char importFile[MAX_PATH];
+extern void (*DoImport)(HWND);
+static void OldMirandaHistoryImport(HWND hdlgProgress);
+HANDLE HContactFromUIN(DWORD uin);
+HANDLE HistoryImportFindContact(HWND hdlgProgress,DWORD uin,int addUnknown);
+static DWORD importOptions;
+
+BOOL CALLBACK MirandaHistoryPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ CheckDlgButton(hdlg,IDC_ADDUNKNOWN,BST_CHECKED);
+ CheckDlgButton(hdlg,IDC_MSGRECV,BST_CHECKED);
+ CheckDlgButton(hdlg,IDC_MSGSENT,BST_CHECKED);
+ CheckDlgButton(hdlg,IDC_URLRECV,BST_CHECKED);
+ CheckDlgButton(hdlg,IDC_URLSENT,BST_CHECKED);
+ CheckDlgButton(hdlg,IDC_AUTHREQ,BST_CHECKED);
+ CheckDlgButton(hdlg,IDC_ADDED,BST_CHECKED);
+ return TRUE;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDC_BACK:
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_MIRANDADB,(LPARAM)MirandaPageProc);
+ break;
+ case IDOK:
+ importOptions=0;
+ if(IsDlgButtonChecked(hdlg,IDC_ADDUNKNOWN)) importOptions|=IOPT_ADDUNKNOWN;
+ if(IsDlgButtonChecked(hdlg,IDC_MSGSENT)) importOptions|=IOPT_MSGSENT;
+ if(IsDlgButtonChecked(hdlg,IDC_MSGRECV)) importOptions|=IOPT_MSGRECV;
+ if(IsDlgButtonChecked(hdlg,IDC_URLSENT)) importOptions|=IOPT_URLSENT;
+ if(IsDlgButtonChecked(hdlg,IDC_URLRECV)) importOptions|=IOPT_URLRECV;
+ if(IsDlgButtonChecked(hdlg,IDC_AUTHREQ)) importOptions|=IOPT_AUTHREQ;
+ if(IsDlgButtonChecked(hdlg,IDC_ADDED)) importOptions|=IOPT_ADDED;
+ DoImport=OldMirandaHistoryImport;
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_PROGRESS,(LPARAM)ProgressPageProc);
+ break;
+ case IDCANCEL:
+ PostMessage(GetParent(hdlg),WM_CLOSE,0,0);
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+#define HISTORY_MSGRECV 1
+#define HISTORY_MSGSEND 2
+#define HISTORY_URLRECV 3
+#define HISTORY_URLSEND 4
+#define HISTORY_AUTHREQ 5
+#define HISTORY_ADDED 6
+#define issent(t) ((t)==HISTORY_MSGSEND || (t)==HISTORY_URLSEND)
+static int historyTypeToOption[]={0,IOPT_MSGRECV,IOPT_MSGSENT,IOPT_URLRECV,IOPT_URLSENT,IOPT_AUTHREQ,IOPT_ADDED};
+
+static PBYTE ReadHistoryLines(FILE *fp,int *cbBlob)
+{
+ char str[2048];
+ char *blob;
+ int ofs;
+ *cbBlob=0;
+ blob=NULL;
+ while(fgets(str,sizeof(str),fp) && lstrcmp(str,"\xEE\xEE\xEE\xEE\r\n")) {
+ ofs=*cbBlob;
+ *cbBlob+=lstrlen(str);
+ blob=(char*)realloc(blob,*cbBlob+1);
+ lstrcpy(blob+ofs,str);
+ }
+ if (*cbBlob) {
+ (*cbBlob)--;
+ blob[*cbBlob-1]=0;
+ }
+ else {
+ *cbBlob=1;
+ blob=(char*)malloc(1);
+ blob[0]=0;
+ }
+ return (PBYTE)blob;
+}
+
+static void OldMirandaHistoryImport(HWND hdlgProgress)
+{
+ int fileSize;
+ FILE *fp;
+ char str[2048],*eol,*timeofs;
+ HANDLE hContact;
+ int type;
+ DWORD uin;
+ struct tm tmEventTime;
+ DBEVENTINFO dbei;
+
+ AddMessage("Old Miranda history import routines initialised");
+ fp=fopen(importFile,"rb");
+ AddMessage("Calibrating status support functions");
+ fseek(fp,0,SEEK_END);
+ fileSize=ftell(fp);
+ fseek(fp,0,SEEK_SET);
+ SetProgress(0);
+
+ ZeroMemory(&dbei,sizeof(dbei));
+ dbei.cbSize=sizeof(dbei);
+ dbei.szModule=ICQOSCPROTONAME;
+
+ while(!feof(fp)) {
+ SetProgress(100*ftell(fp)/fileSize);
+ if(fgets(str,sizeof(str),fp)==NULL) break;
+ eol=str+lstrlen(str)-1;
+
+ while(*eol=='\r' || *eol=='\n' && eol!=str-1)
+ *(eol--)=0;
+
+ if(lstrlen(str)<20) continue;
+ type=*eol;
+ uin=strtoul(str,NULL,10);
+ if(uin==0) continue; //skip MSN
+ timeofs=str+lstrlen(str)-20;
+ tmEventTime.tm_hour=atoi(timeofs);
+ timeofs=strchr(timeofs,':');
+ if(timeofs==NULL) continue;
+ tmEventTime.tm_min=atoi(timeofs+1);
+ tmEventTime.tm_sec=0;
+ tmEventTime.tm_isdst=-1;
+ tmEventTime.tm_mday=atoi(timeofs+7);
+ tmEventTime.tm_mon=atoi(timeofs+10)-1;
+ tmEventTime.tm_year=atoi(timeofs+13)-1900;
+ dbei.timestamp=mktime(&tmEventTime)+_timezone;
+ if (!(importOptions&historyTypeToOption[type])) continue;
+ hContact=HistoryImportFindContact(hdlgProgress,uin,importOptions&IOPT_ADDUNKNOWN);
+ if(hContact==INVALID_HANDLE_VALUE) break;
+ dbei.flags=issent(type)?DBEF_SENT:DBEF_READ;
+ switch(type) {
+ case HISTORY_MSGRECV:
+ case HISTORY_MSGSEND:
+ dbei.eventType=EVENTTYPE_MESSAGE;
+ dbei.pBlob=ReadHistoryLines(fp,&dbei.cbBlob);
+ CallService(MS_DB_EVENT_ADD,(WPARAM)hContact,(LPARAM)&dbei);
+ break;
+ case HISTORY_URLRECV:
+ case HISTORY_URLSEND:
+ dbei.eventType=EVENTTYPE_URL;
+ dbei.pBlob=ReadHistoryLines(fp,&dbei.cbBlob);
+ { char *endOfUrl;
+ endOfUrl=strchr(dbei.pBlob,'\r');
+ if(endOfUrl!=NULL) {
+ *endOfUrl=0;
+ dbei.cbBlob--;
+ MoveMemory(endOfUrl+1,endOfUrl+2,dbei.cbBlob-(endOfUrl-dbei.pBlob));
+ }
+ }
+ CallService(MS_DB_EVENT_ADD,(WPARAM)hContact,(LPARAM)&dbei);
+ break;
+ case HISTORY_AUTHREQ:
+ dbei.eventType=EVENTTYPE_AUTHREQUEST;
+ dbei.pBlob=ReadHistoryLines(fp,&dbei.cbBlob);
+ dbei.pBlob=(PBYTE)realloc(dbei.pBlob,dbei.cbBlob+8);
+ MoveMemory(dbei.pBlob+8,dbei.pBlob,dbei.cbBlob);
+ *(PDWORD)dbei.pBlob=uin;
+ *(char*)(dbei.pBlob+4)=0; //leave nick, first, last, email blank
+ *(char*)(dbei.pBlob+5)=0;
+ *(char*)(dbei.pBlob+6)=0;
+ *(char*)(dbei.pBlob+7)=0;
+ dbei.cbBlob+=8;
+ CallService(MS_DB_EVENT_ADD,(WPARAM)(HANDLE)NULL,(LPARAM)&dbei);
+ break;
+ case HISTORY_ADDED:
+ dbei.eventType=EVENTTYPE_ADDED;
+ dbei.pBlob=(PBYTE)malloc(8);
+ dbei.cbBlob=8;
+ *(PDWORD)dbei.pBlob=uin;
+ *(char*)(dbei.pBlob+4)=0; //leave nick, first, last, email blank
+ *(char*)(dbei.pBlob+5)=0;
+ *(char*)(dbei.pBlob+6)=0;
+ *(char*)(dbei.pBlob+7)=0;
+ CallService(MS_DB_EVENT_ADD,(WPARAM)(HANDLE)NULL,(LPARAM)&dbei);
+ break;
+ }
+ }
+ AddMessage("Terminating cached I/O access");
+ fclose(fp);
+ AddMessage("Import completed successfully");
+ SetProgress(100);
+}
\ No newline at end of file diff --git a/plugins/Import/src/progress.cpp b/plugins/Import/src/progress.cpp new file mode 100644 index 0000000000..99412f647a --- /dev/null +++ b/plugins/Import/src/progress.cpp @@ -0,0 +1,100 @@ +/*
+
+Import plugin for Miranda IM
+
+Copyright (C) 2001-2005 Martin Öberg, Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include <windows.h>
+#include <stdio.h>
+#include <commctrl.h>
+
+#include "import.h"
+#include "resource.h"
+
+#define PROGM_START (WM_USER+100)
+
+INT_PTR CALLBACK FinishedPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam);
+
+void (*DoImport)(HWND);
+
+INT_PTR CALLBACK ProgressPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hdlg);
+ SendMessage(GetParent(hdlg),WIZM_DISABLEBUTTON,0,0);
+ SendMessage(GetParent(hdlg),WIZM_DISABLEBUTTON,1,0);
+ SendMessage(GetParent(hdlg),WIZM_DISABLEBUTTON,2,0);
+ SendDlgItemMessage(hdlg,IDC_PROGRESS,PBM_SETRANGE,0,MAKELPARAM(0,100));
+ PostMessage(hdlg,PROGM_START,0,0);
+ return TRUE;
+
+ case PROGM_SETPROGRESS:
+ SendDlgItemMessage(hdlg,IDC_PROGRESS,PBM_SETPOS,wParam,0);
+ break;
+
+ case PROGM_ADDMESSAGE:
+ {
+ int i=SendDlgItemMessage(hdlg,IDC_STATUS,LB_ADDSTRING,0,lParam);
+ SendDlgItemMessage(hdlg,IDC_STATUS,LB_SETTOPINDEX,i,0);
+ }
+ break;
+
+ case PROGM_START:
+ DoImport(hdlg);
+ SendMessage(GetParent(hdlg),WIZM_ENABLEBUTTON,1,0);
+ SendMessage(GetParent(hdlg),WIZM_ENABLEBUTTON,2,0);
+ break;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_FINISHED,(LPARAM)FinishedPageProc);
+ break;
+ case IDCANCEL:
+ PostMessage(GetParent(hdlg),WM_CLOSE,0,0);
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+void AddMessage( const char* fmt, ... )
+{
+ va_list args;
+ char msgBuf[ 4096 ];
+ va_start( args, fmt );
+
+ mir_vsnprintf( msgBuf, sizeof(msgBuf), Translate(fmt), args );
+
+ #ifdef _LOGGING
+ {
+ FILE *stream;
+ stream = fopen("Import Debug.log", "a");
+ fprintf(stream, "%s\n", msgBuf);
+ fclose(stream);
+ }
+ #endif
+
+ { TCHAR* str = mir_a2t( msgBuf );
+ SendMessage( hdlgProgress, PROGM_ADDMESSAGE, 0, ( LPARAM )str );
+ mir_free( str );
+ }
+}
diff --git a/plugins/Import/src/resource.h b/plugins/Import/src/resource.h new file mode 100644 index 0000000000..7bf769f14d --- /dev/null +++ b/plugins/Import/src/resource.h @@ -0,0 +1,64 @@ +//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by resource.rc
+//
+#define IDC_BACK 3
+#define IDD_WIZARD 101
+#define IDD_OPTIONS 102
+#define IDD_ICQSERVER 104
+#define IDD_IMPORTTYPE 106
+#define IDD_WIZARDINTRO 107
+#define IDD_FINISHED 108
+#define IDD_MIRABILISDB 109
+#define IDD_MIRANDADB 110
+#define IDD_PROGRESS 111
+#define IDD_ADVOPTIONS 112
+#define IDI_IMPORT 177
+#define IDC_MIRABILIS 1000
+#define IDC_DONTLOADPLUGIN 1001
+#define IDC_MIRANDA 1001
+#define IDC_USEFINDADD 1004
+#define IDC_OTHER 1005
+#define IDC_LIST 1006
+#define IDC_FILENAME 1007
+#define IDC_PROGRESS 1008
+#define IDC_STATUS 1009
+#define IDC_MIRABILISRUNNING 1010
+#define IDC_MIRABILISACCOUNT 1011
+#define IDC_RADIO_ALL 1016
+#define IDC_RADIO_CONTACTS 1017
+#define IDC_RADIO_CUSTOM 1018
+#define IDC_STATIC_ALL 1019
+#define IDC_STATIC_CONTACTS 1020
+#define IDC_STATIC_CUSTOM 1021
+#define IDC_DATETIMEPICKER 1023
+#define IDC_IN_FT 1024
+#define IDC_CONTACTS 1025
+#define IDC_SYSTEM 1026
+#define IDC_SINCE 1027
+#define IDC_IN_MSG 1028
+#define IDC_IN_URL 1029
+#define IDC_OUT_FT 1030
+#define IDC_OUT_MSG 1031
+#define IDC_OUT_URL 1032
+#define IDC_IN_OTHER 1033
+#define IDC_OUT_OTHER 1034
+#define IDC_INCOMING 1035
+#define IDC_OUTGOING 1036
+#define IDC_ALL 1037
+#define IDC_MSG 1038
+#define IDC_URL 1039
+#define IDC_FILE 1040
+#define IDC_FT 1040
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 105
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1041
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/plugins/Import/src/version.h b/plugins/Import/src/version.h new file mode 100644 index 0000000000..58f9cf348c --- /dev/null +++ b/plugins/Import/src/version.h @@ -0,0 +1,5 @@ +#include <m_version.h>
+
+#define __FILEVERSION_STRING MIRANDA_VERSION_FILEVERSION
+#define __VERSION_STRING MIRANDA_VERSION_STRING
+#define __VERSION_DWORD MIRANDA_VERSION_DWORD
diff --git a/plugins/Import/src/wizard.cpp b/plugins/Import/src/wizard.cpp new file mode 100644 index 0000000000..780501a10a --- /dev/null +++ b/plugins/Import/src/wizard.cpp @@ -0,0 +1,216 @@ +/*
+
+Import plugin for Miranda IM
+
+Copyright (C) 2001-2005 Martin Öberg, Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "import.h"
+#include "resource.h"
+
+INT_PTR CALLBACK WizardIntroPageProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK FinishedPageProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK MirabilisPageProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK MirandaPageProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK ICQserverPageProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam);
+
+extern HINSTANCE hInst;
+BOOL IsProtocolLoaded(char* pszProtocolName);
+BOOL EnumICQAccounts();
+void FreeICQAccountsList();
+
+INT_PTR CALLBACK ImportTypePageProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch( message ) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hdlg);
+ CheckDlgButton(hdlg, IDC_MIRANDA, BST_CHECKED);
+
+ // Disable Mirabilis import if ICQ isn't loaded.
+ if (!EnumICQAccounts())
+ EnableWindow(GetDlgItem(hdlg, IDC_MIRABILIS), FALSE);
+
+ return TRUE;
+
+ case WM_COMMAND:
+ switch( LOWORD( wParam )) {
+ case IDC_BACK:
+ PostMessage(GetParent(hdlg), WIZM_GOTOPAGE, IDD_WIZARDINTRO, (LPARAM)WizardIntroPageProc);
+ break;
+
+ case IDOK:
+ if (IsDlgButtonChecked(hdlg, IDC_MIRANDA))
+ PostMessage(GetParent(hdlg), WIZM_GOTOPAGE, IDD_MIRANDADB, (LPARAM)MirandaPageProc);
+ else if (IsDlgButtonChecked(hdlg, IDC_MIRABILIS))
+ PostMessage(GetParent(hdlg), WIZM_GOTOPAGE, IDD_MIRABILISDB, (LPARAM)MirabilisPageProc);
+ else if (IsDlgButtonChecked(hdlg, IDC_USEFINDADD)) {
+ CallService(MS_FINDADD_FINDADD, 0, 0);
+ PostMessage(GetParent(hdlg), WIZM_GOTOPAGE, IDD_FINISHED, (LPARAM)FinishedPageProc);
+ }
+ break;
+
+ case IDCANCEL:
+ PostMessage(GetParent(hdlg), WM_CLOSE, 0, 0);
+ break;
+ } }
+
+ return FALSE;
+}
+
+INT_PTR CALLBACK WizardIntroPageProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch( message ) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hdlg);
+ SendMessage(GetParent(hdlg), WIZM_DISABLEBUTTON, 0, 0);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch( LOWORD( wParam )) {
+ case IDOK:
+ PostMessage(GetParent(hdlg), WIZM_GOTOPAGE, IDD_IMPORTTYPE, (LPARAM)ImportTypePageProc);
+ break;
+
+ case IDCANCEL:
+ PostMessage(GetParent(hdlg), WM_CLOSE, 0, 0);
+ break;
+ } }
+
+ return FALSE;
+}
+
+INT_PTR CALLBACK FinishedPageProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch( message ) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hdlg);
+ SendMessage(GetParent(hdlg), WIZM_DISABLEBUTTON, 0, 0);
+ SendMessage(GetParent(hdlg), WIZM_SETCANCELTEXT, 0, (LPARAM)TranslateT("Finish"));
+ CheckDlgButton(hdlg, IDC_DONTLOADPLUGIN, BST_UNCHECKED);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch( LOWORD( wParam )) {
+ case IDOK:
+ PostMessage(GetParent(hdlg), WIZM_GOTOPAGE, IDD_IMPORTTYPE, (LPARAM)ImportTypePageProc);
+ break;
+
+ case IDCANCEL:
+ if ( IsDlgButtonChecked( hdlg, IDC_DONTLOADPLUGIN )) {
+ char sModuleFileName[MAX_PATH];
+ char *pszFileName;
+
+ GetModuleFileNameA(hInst, sModuleFileName, sizeof(sModuleFileName));
+ pszFileName = strrchr(sModuleFileName, '\\' );
+ if (pszFileName == NULL)
+ pszFileName = sModuleFileName;
+ else
+ pszFileName++;
+
+ // We must lower case here because if a DLL is loaded in two
+ // processes, its file name from GetModuleFileName in one process may
+ // differ in case from its file name in the other process. This will
+ // prevent the plugin from disabling/enabling correctly (this fix relies
+ // on the plugin loader to ignore case)
+ CharLowerA(pszFileName);
+ DBWriteContactSettingByte(NULL, "PluginDisable", pszFileName, 1);
+ }
+ PostMessage(GetParent(hdlg), WM_CLOSE, 0, 0);
+ break;
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+INT_PTR CALLBACK WizardDlgProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ static HWND hwndPage;
+
+ switch ( message ) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hdlg);
+ hwndPage = CreateDialog(hInst, MAKEINTRESOURCE(IDD_WIZARDINTRO), hdlg, WizardIntroPageProc);
+ SetWindowPos(hwndPage, 0, 0, 0, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
+ ShowWindow(hwndPage, SW_SHOW);
+ ShowWindow(hdlg, SW_SHOW);
+ SendMessage(hdlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(hInst,MAKEINTRESOURCE(IDI_IMPORT)));
+ return TRUE;
+
+ case WIZM_GOTOPAGE:
+ DestroyWindow(hwndPage);
+ EnableWindow(GetDlgItem(hdlg, IDC_BACK), TRUE);
+ EnableWindow(GetDlgItem(hdlg, IDOK), TRUE);
+ EnableWindow(GetDlgItem(hdlg, IDCANCEL), TRUE);
+ SetDlgItemText(hdlg, IDCANCEL, TranslateT("Cancel"));
+ hwndPage = CreateDialog(hInst, MAKEINTRESOURCE(wParam), hdlg, (DLGPROC)lParam);
+ SetWindowPos(hwndPage, 0, 0, 0, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
+ ShowWindow(hwndPage, SW_SHOW);
+ break;
+
+ case WIZM_DISABLEBUTTON:
+ switch ( wParam ) {
+ case 0:
+ EnableWindow(GetDlgItem(hdlg, IDC_BACK), FALSE);
+ break;
+
+ case 1:
+ EnableWindow(GetDlgItem(hdlg, IDOK), FALSE);
+ break;
+
+ case 2:
+ EnableWindow(GetDlgItem(hdlg, IDCANCEL), FALSE);
+ break;
+ }
+ break;
+
+ case WIZM_ENABLEBUTTON:
+ switch ( wParam ) {
+ case 0:
+ EnableWindow(GetDlgItem(hdlg, IDC_BACK), TRUE);
+ break;
+
+ case 1:
+ EnableWindow(GetDlgItem(hdlg, IDOK), TRUE);
+ break;
+
+ case 2:
+ EnableWindow(GetDlgItem(hdlg, IDCANCEL), TRUE);
+ break;
+ }
+ break;
+
+ case WIZM_SETCANCELTEXT:
+ SetDlgItemText(hdlg, IDCANCEL, (TCHAR*)lParam);
+ break;
+
+ case WM_COMMAND:
+ SendMessage(hwndPage, WM_COMMAND, wParam, lParam);
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndPage);
+ DestroyWindow(hdlg);
+
+ FreeICQAccountsList();
+ break;
+ }
+
+ return FALSE;
+}
|