From 6e7980b0ad162bbb526b3d52acbc1639c2b11760 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Fri, 20 Jul 2012 10:43:41 +0000 Subject: Import, KeyboardNotify: changed folder structure git-svn-id: http://svn.miranda-ng.org/main/trunk@1071 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/Import/src/ICQserver.cpp | 74 ++ plugins/Import/src/ICQserver.h | 62 ++ plugins/Import/src/import.h | 108 +++ plugins/Import/src/main.cpp | 543 ++++++++++++ plugins/Import/src/mirabilis.cpp | 1493 +++++++++++++++++++++++++++++++++ plugins/Import/src/mirabilis.h | 200 +++++ plugins/Import/src/miranda.cpp | 1445 +++++++++++++++++++++++++++++++ plugins/Import/src/mirandadb0700.h | 142 ++++ plugins/Import/src/mirandahistory.cpp | 208 +++++ plugins/Import/src/progress.cpp | 100 +++ plugins/Import/src/resource.h | 64 ++ plugins/Import/src/version.h | 5 + plugins/Import/src/wizard.cpp | 216 +++++ 13 files changed, 4660 insertions(+) create mode 100644 plugins/Import/src/ICQserver.cpp create mode 100644 plugins/Import/src/ICQserver.h create mode 100644 plugins/Import/src/import.h create mode 100644 plugins/Import/src/main.cpp create mode 100644 plugins/Import/src/mirabilis.cpp create mode 100644 plugins/Import/src/mirabilis.h create mode 100644 plugins/Import/src/miranda.cpp create mode 100644 plugins/Import/src/mirandadb0700.h create mode 100644 plugins/Import/src/mirandahistory.cpp create mode 100644 plugins/Import/src/progress.cpp create mode 100644 plugins/Import/src/resource.h create mode 100644 plugins/Import/src/version.h create mode 100644 plugins/Import/src/wizard.cpp (limited to 'plugins/Import/src') 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 + +// ====================== +// == 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 + +#include +#include // datetimepicker + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ** 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(identryIdLow) 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 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 +#include +#include + +// ====================== +// == 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 + +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 + +#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 +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 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 +#include +#include +#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 +#include +#include + +#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 + +#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; +} -- cgit v1.2.3