summaryrefslogtreecommitdiff
path: root/plugins/Import/src/mirabilis.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/Import/src/mirabilis.cpp')
-rw-r--r--plugins/Import/src/mirabilis.cpp1493
1 files changed, 1493 insertions, 0 deletions
diff --git a/plugins/Import/src/mirabilis.cpp b/plugins/Import/src/mirabilis.cpp
new file mode 100644
index 0000000000..1f032827af
--- /dev/null
+++ b/plugins/Import/src/mirabilis.cpp
@@ -0,0 +1,1493 @@
+/*
+
+Import plugin for Miranda IM
+
+Copyright (C) 2001-2005 Martin Öberg, Richard Hughes, Roland Rabien & Tristan Van de Vreede
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+// ==============
+// == INCLUDES ==
+// ==============
+
+#include "import.h"
+#include "mirabilis.h"
+#include "resource.h"
+
+BOOL IsDuplicateEvent(HANDLE hContact, DBEVENTINFO dbei);
+BOOL IsProtocolLoaded(char* pszProtocolName);
+HANDLE HContactFromNumericID(char* pszProtoName, char* pszSetting, DWORD dwID);
+HANDLE AddContact(HWND hdlgProgress, char* pszProtoName, char* pszUniqueSetting, DBVARIANT* id, DBVARIANT* nick, DBVARIANT* group);
+
+// ====================
+// ====================
+// == IMPLEMENTATION ==
+// ====================
+// ====================
+
+static void SearchForDatabases(HWND hdlg, const TCHAR *dbPath, const TCHAR *type)
+{
+ HANDLE hFind;
+ WIN32_FIND_DATA fd;
+ TCHAR szSearchPath[MAX_PATH];
+ TCHAR szRootName[MAX_PATH],*str2;
+
+ int i;
+
+ wsprintf(szSearchPath, _T("%s\\*.idx"), dbPath);
+ hFind=FindFirstFile(szSearchPath,&fd);
+ if(hFind!=INVALID_HANDLE_VALUE) {
+ do {
+ lstrcpy(szRootName,fd.cFileName);
+ str2=_tcsrchr(szRootName,'.');
+ if(str2!=NULL) *str2=0;
+ if(lstrlen(szRootName)>3 && !lstrcmpi(szRootName+lstrlen(szRootName)-3,_T("tmp")))
+ continue;
+ lstrcat(szRootName,type);
+ i=SendDlgItemMessage(hdlg,IDC_LIST,LB_ADDSTRING,0,(LPARAM)szRootName);
+ str2 = (TCHAR*)mir_alloc((lstrlen(dbPath) + 2+lstrlen(fd.cFileName))*sizeof(TCHAR));
+ wsprintf(str2, _T("%s\\%s"), dbPath, fd.cFileName);
+ SendDlgItemMessage(hdlg,IDC_LIST,LB_SETITEMDATA,i,(LPARAM)str2);
+ }
+ while( FindNextFile( hFind, &fd ));
+
+ FindClose(hFind);
+ }
+}
+
+INT_PTR CALLBACK MirabilisPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ {
+ HKEY hKey;
+ LONG lResult;
+ int i;
+ TranslateDialogDefault(hdlg);
+ if (ERROR_SUCCESS != (lResult = RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\Mirabilis\\ICQ\\DefaultPrefs"), 0, KEY_QUERY_VALUE, &hKey)))
+ lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Mirabilis\\ICQ\\DefaultPrefs"), 0, KEY_QUERY_VALUE, &hKey);
+
+ if (lResult == ERROR_SUCCESS) {
+ TCHAR dbPath[MAX_PATH];
+ DWORD cch;
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("New Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (99a)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("99b Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (99b)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("2000a Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (2000a)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("2000b Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (2000b)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("2001a Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (2001a)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("2001b Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (2001b)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("2002a Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (2002a)"));
+ cch=sizeof(dbPath);
+ if(ERROR_SUCCESS==RegQueryValueEx(hKey,_T("2003a Database"),NULL,NULL,(LPBYTE)dbPath,&cch))
+ SearchForDatabases(hdlg,dbPath,_T(" (2003a)"));
+ }
+
+ for (i = 0; i < cICQAccounts; i++)
+ {
+ SendDlgItemMessage(hdlg, IDC_MIRABILISACCOUNT, CB_ADDSTRING, 0, (LPARAM)tszICQAccountName[i]);
+ }
+ SendDlgItemMessage(hdlg, IDC_MIRABILISACCOUNT, CB_SETCURSEL, 0, 0);
+
+ SetTimer(hdlg,1,2000,NULL);
+ SendMessage(hdlg,WM_TIMER,0,0);
+ return TRUE;
+ }
+ case WM_TIMER:
+ { HANDLE hMirabilisMutex;
+ hMirabilisMutex=OpenMutexA(MUTEX_ALL_ACCESS,FALSE,"Mirabilis ICQ Mutex");
+ if(hMirabilisMutex!=NULL) {
+ CloseHandle(hMirabilisMutex);
+ ShowWindow(GetDlgItem(hdlg,IDC_MIRABILISRUNNING),SW_SHOW);
+ }
+ else ShowWindow(GetDlgItem(hdlg,IDC_MIRABILISRUNNING),SW_HIDE);
+ }
+ break;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDC_BACK:
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_IMPORTTYPE,(LPARAM)ImportTypePageProc);
+ break;
+ case IDOK:
+ { TCHAR filename[MAX_PATH];
+ GetDlgItemText(hdlg,IDC_FILENAME,filename,SIZEOF(filename));
+ if(_taccess(filename,4)) {
+ MessageBox(hdlg,TranslateT("The given file does not exist. Please check that you have entered the name correctly."),TranslateT("Mirabilis Import"),MB_OK);
+ break;
+ }
+ lstrcpy(importFile,filename);
+ iICQAccount = SendDlgItemMessage(hdlg, IDC_MIRABILISACCOUNT, CB_GETCURSEL, 0, 0);
+ PostMessage(GetParent(hdlg),WIZM_GOTOPAGE,IDD_OPTIONS,(LPARAM)MirabilisOptionsPageProc);
+ break;
+ }
+ case IDCANCEL:
+ PostMessage(GetParent(hdlg),WM_CLOSE,0,0);
+ break;
+ case IDC_LIST:
+ if(HIWORD(wParam)==LBN_SELCHANGE) {
+ int sel=SendDlgItemMessage(hdlg,IDC_LIST,LB_GETCURSEL,0,0);
+ if(sel==LB_ERR) break;
+ SetDlgItemText(hdlg,IDC_FILENAME,(TCHAR*)SendDlgItemMessage(hdlg,IDC_LIST,LB_GETITEMDATA,sel,0));
+ }
+ break;
+ case IDC_OTHER:
+ { OPENFILENAME ofn;
+ TCHAR str[MAX_PATH], text[256];
+ int index;
+
+ // TranslateTS doesnt translate \0 separated strings
+ index = mir_sntprintf(text, 64, _T("%s (*.idx)"), TranslateT("Mirabilis ICQ database indexes")) + 1;
+ _tcscpy(text + index, _T("*.idx")); index += 6;
+ index += mir_sntprintf(text + index, 64, _T("%s (*.*)"), TranslateT("All Files")) + 1;
+ _tcscpy(text + index, _T("*.*")); index += 4;
+ text[index] = 0;
+
+ GetDlgItemText(hdlg,IDC_FILENAME,str,SIZEOF(str));
+ ZeroMemory(&ofn, sizeof(ofn));
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hdlg;
+ ofn.lpstrFilter = text;
+ ofn.lpstrFile = str;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_DONTADDTORECENT;
+ ofn.nMaxFile = SIZEOF(str);
+ ofn.lpstrDefExt = _T("idx");
+ if(GetOpenFileName(&ofn))
+ SetDlgItemText(hdlg,IDC_FILENAME,str);
+ break;
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ { int i;
+ for(i=SendDlgItemMessage(hdlg,IDC_LIST,LB_GETCOUNT,0,0)-1;i>=0;i--)
+ mir_free((char*)SendDlgItemMessage(hdlg,IDC_LIST,LB_GETITEMDATA,i,0));
+ break;
+ }
+ }
+ return FALSE;
+}
+
+
+INT_PTR CALLBACK MirabilisOptionsPageProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hdlg);
+ EnableWindow(GetDlgItem(hdlg, IDC_RADIO_ALL), TRUE);
+ EnableWindow(GetDlgItem(hdlg, IDC_STATIC_ALL), TRUE);
+ EnableWindow(GetDlgItem(hdlg, IDC_RADIO_CONTACTS), TRUE);
+ EnableWindow(GetDlgItem(hdlg, IDC_STATIC_CONTACTS), TRUE);
+ CheckDlgButton(hdlg, IDC_RADIO_ALL, BST_CHECKED);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDC_BACK:
+ PostMessage(GetParent(hdlg), WIZM_GOTOPAGE, IDD_MIRABILISDB, (LPARAM)MirabilisPageProc);
+ break;
+ case IDOK:
+ if (IsDlgButtonChecked(hdlg, IDC_RADIO_ALL)) {
+ DoImport = MirabilisImport;
+ nImportOption = IMPORT_ALL;
+ nCustomOptions = IOPT_MSGSENT|IOPT_MSGRECV|IOPT_URLSENT|IOPT_URLRECV;
+ PostMessage(GetParent(hdlg), WIZM_GOTOPAGE, IDD_PROGRESS, (LPARAM)ProgressPageProc);
+ break;
+ }
+ if (IsDlgButtonChecked(hdlg, IDC_RADIO_CONTACTS)) {
+ DoImport = MirabilisImport;
+ nImportOption = IMPORT_CONTACTS;
+ nCustomOptions = 0;
+ PostMessage(GetParent(hdlg), WIZM_GOTOPAGE, IDD_PROGRESS, (LPARAM)ProgressPageProc);
+ break;
+ }
+ break;
+ case IDCANCEL:
+ PostMessage(GetParent(hdlg), WM_CLOSE, 0, 0);
+ break;
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+static int GetHighestIndexEntry(void)
+{
+ struct TIdxIndexEntry *entry;
+ DWORD ofs;
+
+ ofs=*(PDWORD)(pIdx+12);
+ for (;;) {
+ entry=(struct TIdxIndexEntry*)(pIdx+ofs);
+ if(entry->entryIdLow==(DWORD)-2) return ((struct TIdxDatEntry*)entry)->entryId;
+ if(entry->ofsHigher>=0xF0000000) ofs=entry->ofsInHere;
+ else ofs=entry->ofsHigher;
+ }
+}
+
+static int GetIdDatOfs(DWORD id)
+{
+ struct TIdxIndexEntry *entry;
+ DWORD ofs = *(PDWORD)(pIdx+12);
+ for (;;) {
+ entry=(struct TIdxIndexEntry*)(pIdx+ofs);
+ if(entry->entryIdLow==(DWORD)-2) {
+ if(entry->entryIdHigh==id) return ((struct TIdxDatEntry*)entry)->datOfs;
+ return 0;
+ }
+ if(id<entry->entryIdLow) ofs=entry->ofsLower;
+ else if(entry->ofsHigher<0xF0000000 && id>=entry->entryIdHigh) ofs=entry->ofsHigher;
+ else ofs=entry->ofsInHere;
+ }
+ return 0;
+}
+
+static int GetDatEntryType(DWORD ofs)
+{
+ return *(int*)(pDat+ofs+4);
+}
+
+DWORD GetDBVersion()
+{
+ dwDBVersion = *(PDWORD)(pIdx+16);
+
+ switch (dwDBVersion) {
+ case DBV99A:
+ AddMessage( LPGEN("This looks like a ICQ 99a database."));
+ break;
+ case DBV99B:
+ AddMessage( LPGEN("This looks like a ICQ 99b database."));
+ break;
+ case DBV2000A:
+ AddMessage( LPGEN("This looks like a ICQ 2000a database."));
+ break;
+ case DBV2000B:
+ AddMessage( LPGEN("This looks like a ICQ 2000b database."));
+ break;
+ case DBV2001A:
+ AddMessage( LPGEN("This looks like a ICQ 2001, 2002 or 2003a database."));
+ break;
+ default:
+ AddMessage( LPGEN("This database is an unknown version."));
+ return 0;
+ }
+
+ return dwDBVersion;
+}
+
+int GetEntryVersion(WORD wSeparatorValue)
+{
+ int nVersion;
+
+ if (wSeparatorValue < ENTRYV99A)
+ nVersion = 0; // Cannot handle ICQ98 contacts
+ else if ((wSeparatorValue >= ENTRYV99A) && (wSeparatorValue < ENTRYV99B))
+ nVersion = ENTRYV99A;
+ else if ((wSeparatorValue >= ENTRYV99B) && (wSeparatorValue < ENTRYV2000A))
+ nVersion = ENTRYV99B;
+ else if ((wSeparatorValue >= ENTRYV2000A) && (wSeparatorValue < ENTRYV2000B))
+ nVersion = ENTRYV2000A;
+ else if ((wSeparatorValue >= ENTRYV2000B) && (wSeparatorValue < ENTRYV2001A))
+ nVersion = ENTRYV2000B;
+ else if ((wSeparatorValue >= ENTRYV2001A) && (wSeparatorValue < ENTRYV2001B))
+ nVersion = ENTRYV2001A;
+ else if ((wSeparatorValue >= ENTRYV2001B) && (wSeparatorValue < ENTRYV2002A))
+ nVersion = ENTRYV2001B;
+ else if (wSeparatorValue >= ENTRYV2002A)
+ nVersion = ENTRYV2002A;
+ else
+ nVersion = ENTRYVUNKNOWN; // Just in case... Skip undocumented contact versions
+
+ return nVersion;
+}
+
+DWORD ReadSubList(DWORD dwOffset)
+{
+ DWORD dwSubType, dwProperties, n;
+
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Attempting to parse sub list at offset %u."), dwOffset);
+ #endif
+
+ // Check number of properties in sub list
+ dwProperties = *(PDWORD)(pDat+dwOffset);
+ dwOffset+=4;
+
+ // Check sub list type
+ dwSubType = *(PBYTE)(pDat+dwOffset);
+ dwOffset+=1;
+
+ switch (dwSubType){
+ case 0x6B:
+ for(n=0;n<dwProperties;n++) dwOffset+=*(PWORD)(pDat+dwOffset)+2;
+ break;
+ case 0x6E:
+ for(n=0;n<dwProperties;n++) {
+ if (!(dwOffset = ReadPropertyBlock(dwOffset, NULL, NULL))) return 0;
+ }
+ break;
+ default:
+ // Unknown sub list type
+ AddMessage( LPGEN("Error: Unknown sub list type (%u) at offset %u."), dwSubType, dwOffset);
+ return 0;
+ }
+
+ return dwOffset;
+}
+
+DWORD ReadPropertyBlock(DWORD dwOffset, char* SearchWord, int* nSearchResult)
+{
+ DWORD n, dwProperties, nameOfs;
+ WORD nameLen;
+
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Attempting to parse property block at offset %u."), dwOffset );
+ #endif
+
+ // Reset search result
+ if (SearchWord) *nSearchResult = 0;
+
+ // Check number of properties in block
+ dwOffset+=2;
+ dwProperties = *(PDWORD)(pDat+dwOffset);
+
+ // Scan all properties and search for
+ // 'SearchWord' (if it has been specified).
+ dwOffset+=4;
+ for(n=0;n<dwProperties;n++) {
+ nameLen=*(PWORD)(pDat+dwOffset); // Length of current property name
+ dwOffset+=2;
+ nameOfs=dwOffset; // Save pointer to start of name
+ dwOffset+=nameLen; // dwOffset now points to property value type
+
+ if ( SearchWord ) {
+ // Is this the property we are searching for?
+ if (!lstrcmpA((char*)(pDat+nameOfs),SearchWord)) {
+ *nSearchResult = 1;
+ return dwOffset;
+ } }
+
+ // Increase 'dwOffset' to point to length of next property
+ switch(*(pDat+dwOffset)) {
+ case 0x64:
+ case 0x65: dwOffset+=2; break;
+ case 0x66:
+ case 0x67: dwOffset+=3; break;
+ case 0x68:
+ case 0x69: dwOffset+=5; break;
+ case 0x6b: dwOffset+=*(PWORD)(pDat+dwOffset+1)+3; break;
+ case 0x6d:
+ dwOffset = ReadSubList(dwOffset+1);
+ if (!dwOffset) return 0;
+ break;
+ case 0x6f: dwOffset+=*(PDWORD)(pDat+dwOffset+1)+5; break;
+ default:
+ // Unknown property value type
+ AddMessage( LPGEN("Error: Unknown datatype (%u) at offset %u."), *(pDat+dwOffset), dwOffset);
+ return 0;
+ } }
+
+ // We have reached the end without finding
+ // the property we searched for
+ if (SearchWord) *nSearchResult = 0;
+
+ // Return offset to the byte right after the
+ // property list
+ return dwOffset;
+}
+
+DWORD ReadPropertyBlockList(DWORD dwOffset, char* SearchWord, int* nSearchResult)
+{
+ DWORD dwBlocks, n;
+
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Attempting to parse property block list at offset %u."), dwOffset );
+ #endif
+
+ // Check number of blocks in list
+ dwBlocks = *(PDWORD)(pDat+dwOffset);
+
+ // Scan all blocks and search for 'SearchWord' (if
+ // it has been specified).
+ dwOffset += 4;
+ for(n = 0;n<dwBlocks;n++) {
+ if (!(dwOffset = ReadPropertyBlock(dwOffset, SearchWord, nSearchResult))) {
+ AddMessage( LPGEN("Failed to read Property block."));
+ return 0;
+ }
+ if (SearchWord)
+ // Was the property found in the block?
+ if (*nSearchResult) return dwOffset;
+ }
+
+ // We have reached the end without finding
+ // the property we searched for
+ if (SearchWord) *nSearchResult = 0;
+
+ // Return offset to the byte right after the
+ // property list
+ return dwOffset;
+}
+
+DWORD ReadWavList(DWORD dwOffset)
+{
+ DWORD dwWavEntries, n;
+ WORD wNameLen;
+
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Attempting to parse wav file list at offset %u."), dwOffset);
+ #endif
+
+ // Check number of wav entries
+ dwWavEntries = *(PDWORD)(pDat+dwOffset);
+
+ // Read entire list
+ dwOffset+=4;
+ for(n = 0;n<dwWavEntries;n++) {
+ wNameLen = *(PWORD)(pDat+dwOffset+0x0A);
+ dwOffset += wNameLen + 0x0C;
+ }
+
+ // Return the offset to the byte right
+ // after the list
+ return dwOffset;
+}
+
+DWORD FindMyDetails(void)
+{
+ DWORD dwOffset = GetIdDatOfs(1005);
+
+ if (!dwOffset) return 0;
+ if (*(PDWORD)(pDat+dwOffset+0x08) != 1005) return 0;
+ if (*(PBYTE)(pDat+dwOffset+0x0C) != 0xE4) return 0;
+ if (*(int*)(pDat+dwOffset+0x1e) != 'USER') return 0;
+ if (*(PDWORD)(pDat+dwOffset+0x22) != 6) return 0;
+
+ return dwOffset;
+}
+
+// dwOffset must point to MyDetails
+DWORD FindGroupList(DWORD dwOffset)
+{
+ DWORD n, dwPhoneEntries;
+ WORD wSeparatorValue;
+ int nFormat;
+
+ wSeparatorValue = *(PWORD)(pDat+dwOffset+0x1c);
+ nFormat = GetEntryVersion(wSeparatorValue);
+
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Attempting to parse group list, type %d."), nFormat );
+ #endif
+
+ switch (nFormat) {
+ case ENTRYV99A:
+ if (!(dwOffset = ReadWavList(dwOffset+0x54))) return 0;
+ if (!(dwOffset = ReadPropertyBlock(dwOffset+38, NULL, NULL))) return 0;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // User name
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Nick name
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // First name
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Last name
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Primary e-mail
+ dwOffset += 0x13; // Various fixed length data
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Home city
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Home state
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Additional details
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // User homepage
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Home phone number
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Notes
+ dwOffset += 0x08; // Various fixed length data
+ dwOffset += 0x04; // ++ UNKNOWN ++
+ dwPhoneEntries = *(PDWORD)(pDat+dwOffset); // Phonebook starts here
+ dwOffset += 0x04;
+ for(n = 0;n<dwPhoneEntries;n++) {
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += 2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ }
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Picture file name
+ dwOffset += 0x06; // ++ UNKNOWN ++ (8 enligt spec)
+ dwOffset += 0x06; // Various fixed length data
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Secondary e-mail
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Old e-mail
+ dwOffset += 0x04; // ++ UNKNOWN ++
+ dwOffset += 0x03; // Various fixed length data
+ dwOffset += 0x08; // ++ UNKNOWN ++
+ dwOffset += 0x03; // Various fixed length data
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Home street address
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Home fax number
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Home cell phone number
+ dwOffset += 0x04; // ++ UNKNOWN ++
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Company Div/Dept
+ dwOffset += 0x01; // Occupation
+ dwOffset += 0x04; // ++ UNKNOWN ++
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Company position
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Company name
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Work street address
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Work state
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Work city
+ dwOffset += 0x08; // Various fixed length data
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Work phone number
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Work fax number
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Work homepage
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Past background #1
+ dwOffset += 0x02; // Past background #1 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Past background #2
+ dwOffset += 0x02; // Past background #2 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Past background #3
+ dwOffset += 0x02; // Past background #3 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Affiliation #1
+ dwOffset += 0x02; // Affiliation #1 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Affiliation #2
+ dwOffset += 0x02; // Affiliation #2 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Affiliation #3
+ dwOffset += 0x02; // Affiliation #3 category
+ dwOffset += 0x14; // ++ UNKNOWN ++
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Interest #1
+ dwOffset += 0x02; // Interest #1 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Interest #2
+ dwOffset += 0x02; // Interest #2 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Interest #3
+ dwOffset += 0x02; // Interest #3 category
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Interest #4
+ dwOffset += 0x02; // Interest #4 category
+ dwOffset += 0x28; // ++ UNKNOWN ++
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // Password
+ dwOffset += 0x04; // ++ UNKNOWN ++
+ dwOffset += 0x0E; // ++ UNKNOWN ++
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // POP3 account name
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // POP3 account password
+ dwOffset += *(PWORD)(pDat+dwOffset)+2; // POP server name
+ dwOffset += 0x15; // ++ UNKNOWN ++
+ return dwOffset;
+
+ case ENTRYV99B:
+ if (!(dwOffset = ReadWavList(dwOffset+0x2C))) return 0;
+ if (!(dwOffset = ReadPropertyBlockList(dwOffset+0x02, NULL, NULL))) return 0;
+ dwOffset += 0x08;
+ dwPhoneEntries = *(PDWORD)(pDat+dwOffset); // Phonebook
+ dwOffset += 0x04;
+ for(n = 0;n<dwPhoneEntries;n++) {
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ dwOffset += 2;
+ dwOffset += *(PWORD)(pDat+dwOffset)+2;
+ }
+ return dwOffset + 0x06;
+
+ case ENTRYV2000A:
+ case ENTRYV2000B:
+ case ENTRYV2002A:
+ if (!(dwOffset = ReadWavList(dwOffset+0x2C))) return 0;
+ if (!(dwOffset = ReadPropertyBlockList(dwOffset+0x02, NULL, NULL))) return 0;
+ return dwOffset + 0x06;
+
+ case ENTRYV2001A:
+ case ENTRYV2001B:
+ if (!(dwOffset = ReadPropertyBlockList(dwOffset+0x2C, NULL, NULL))) return 0;
+ return dwOffset + 0x06;
+
+ default:
+ AddMessage( LPGEN("default"));
+ return 0;
+ }
+}
+
+// ------------------------------------------------
+// Finds the name of a group with a specific GroupID.
+// ------------------------------------------------
+// dwGroupID is the GroupID of the group.
+// Returns a pointer to the name string or NULL if
+// it was not found.
+char* GetGroupName(DWORD dwGroupID)
+{
+ DWORD dwGroups, n, tmpOfs, dwOffset;
+ char* strGroupName = 0;
+ int nSearchResult;
+
+ // Check for the existence of any group
+ if (!dwGroupListOfs) return 0;
+ dwOffset = dwGroupListOfs;
+ dwGroups = *(PDWORD)(pDat + dwOffset);
+ if (dwGroups == 0) return 0;
+
+ dwOffset += 4;
+
+ // Examine all groups
+ switch (dwDBVersion) {
+ case DBV99A:
+ case DBV99B:
+ for (n = 0; n < dwGroups; n++) {
+ if (dwGroupID == *(PDWORD)(pDat + dwOffset)) {
+ if (*(PWORD)(pDat + dwOffset + 4) > 1)
+ return 6 + (char*)(pDat + dwOffset);
+
+ break;
+ }
+ else
+ // Skip to next group
+ dwOffset += *(PWORD)(pDat + dwOffset + 4) + 12;
+ }
+ break;
+
+ case DBV2000A:
+ case DBV2000B:
+ case DBV2001A:
+ for (n = 0; n < dwGroups; n++) {
+ if (tmpOfs = ReadPropertyBlock(dwOffset, "GroupID", &nSearchResult)) {
+ if (nSearchResult) {
+ if (dwGroupID == *(PDWORD)(pDat + tmpOfs + 1)) {
+ strGroupName = 3 + (char*)(pDat + ReadPropertyBlock(dwOffset, "GroupName", &nSearchResult));
+ if (nSearchResult) {
+ if ((DWORD)*(strGroupName - 2) > 1)
+ return strGroupName;
+ break;
+ } } } }
+
+ // Skip to next group
+ if ( dwOffset != ReadPropertyBlock(dwOffset, NULL, NULL))
+ break;
+ }
+ break;
+ }
+
+ // The GroupID was not found, or it was found
+ // but the group did not have a name, or there
+ // was an error during parsing.
+ return 0;
+}
+
+// ------------------------------------------------
+// Scans a group list and adds all found groups to
+// the Miranda contact list
+// ------------------------------------------------
+// dwOffset must point to the number of entries in
+// the following group list.
+// Returns the number of added groups, or -1 if an error
+// occurred
+
+int ImportGroups()
+{
+ DWORD dwGroups, n, tmpOfs, dwOffset;
+ int nImported = 0;
+ int nSearchResult, nFormat;
+ WORD wSeparatorValue;
+
+ if (!(dwOffset = FindMyDetails())) {
+ AddMessage( LPGEN("ERROR: Failed to find owner information."));
+ return -1;
+ }
+
+ wSeparatorValue = *(PWORD)(pDat + dwOffset + 0x1c);
+ nFormat = GetEntryVersion(wSeparatorValue);
+
+ dwGroupListOfs = dwOffset = FindGroupList(dwOffset);
+ if (!dwOffset) {
+ AddMessage( LPGEN("ERROR: Failed to find contact list groups."));
+ #ifdef _LOGGING
+ { // If this is a debug build, dump MyDetails block to disk
+ FILE *stream;
+ DWORD dwSize;
+ dwOffset = FindMyDetails();
+ dwSize = *(PDWORD)(pDat + dwOffset);
+ stream = fopen("import_grouplist_dump.bin", "w");
+ fwrite(pDat + dwOffset, 1, dwSize, stream);
+ fclose(stream);
+ }
+ #endif
+ return -1;
+ }
+
+ // Check number of groups
+ dwGroups = *(PDWORD)(pDat + dwOffset);
+ if (dwGroups > 0)
+ AddMessage( LPGEN("Importing groups."));
+ else {
+ AddMessage( LPGEN("This database does not contain any contact groups."));
+ return 0;
+ }
+
+ dwOffset += 4;
+
+ // Import all groups with a name
+ switch (nFormat) {
+ case ENTRYV99A:
+ case ENTRYV99B:
+ for (n = 0; n < dwGroups; n++) {
+ if (*(PWORD)(pDat+dwOffset+4) > 1) {
+ if ( CreateGroup(DBVT_ASCIIZ, (char*)(pDat + dwOffset) + 6, NULL ))
+ nImported++;
+ dwOffset += *(PWORD)(pDat + dwOffset + 4) + 12;
+ } }
+ break;
+
+ case ENTRYV2000A:
+ case ENTRYV2000B:
+ case ENTRYV2001A:
+ case ENTRYV2001B:
+ case ENTRYV2002A:
+ for (n = 0; n < dwGroups; n++) {
+ if (tmpOfs = ReadPropertyBlock(dwOffset, "GroupName", &nSearchResult)) {
+ if (nSearchResult) {
+ if (CreateGroup( DBVT_ASCIIZ, (char*)(pDat + tmpOfs + 3), NULL ))
+ nImported++;
+ } }
+
+ dwOffset = ReadPropertyBlock(dwOffset, NULL, NULL);
+ if (!dwOffset) {
+ AddMessage( LPGEN("ERROR: An error occurred while importing groups."));
+ AddMessage( LPGEN("All groups may not have not been imported."));
+ #ifdef _LOGGING
+ { // If this is a debug build, dump MyDetails block to disk
+ FILE *stream;
+ DWORD dwSize;
+ dwOffset = FindMyDetails();
+ dwSize = *(PDWORD)(pDat + dwOffset);
+ stream = fopen("import_grouplist_dump.bin", "w");
+ fwrite(pDat + dwOffset, 1, dwSize, stream);
+ fclose(stream);
+ }
+ #endif
+ return -1;
+ } }
+ break;
+
+ default:
+ return -1;
+ }
+
+ return nImported;
+}
+
+// Imports the contact at offset dwOffset
+// Returns the HANDLE of the Miranda contact
+// or INVALID_HANDLE_VALUE on failure
+
+HANDLE ImportContact(DWORD dwOffset)
+{
+ int nContactVersion, nSearchResult;
+ BYTE Status;
+ WORD wSeparatorValue;
+ DWORD dwGroup, dwUIN = 0, tmpOfs = 0;
+ char *strNickname = 0, *strGroupName = 0;
+
+ if (*(int*)(pDat + dwOffset + 4) != DATENTRY_CONTACT)
+ return INVALID_HANDLE_VALUE;
+
+ if (*(int*)(pDat + dwOffset + 0x1e) != 'USER')
+ return INVALID_HANDLE_VALUE;
+
+ #ifdef _LOGGING
+ { // If this is a debug build, dump contact to disk
+ FILE *stream;
+ DWORD dwSize;
+ dwSize = *(PDWORD)(pDat + dwOffset);
+ stream = fopen("import_last_contact.bin", "w");
+ fwrite(pDat + dwOffset, 1, dwSize, stream);
+ fclose(stream);
+ }
+ #endif
+
+ Status = *(pDat + dwOffset + 0x22);
+ wSeparatorValue = *(PWORD)(pDat + dwOffset + 0x1c);
+ nContactVersion = GetEntryVersion(wSeparatorValue);
+
+ dwGroup = *(PDWORD)(pDat + dwOffset + 0x26);
+ if (dwGroup >= 1000)
+ strGroupName = GetGroupName(dwGroup);
+
+ if (Status == 5)
+ return INVALID_HANDLE_VALUE; // Skip deleted contacts
+
+ if ((Status != 2) && (Status != 3)) {
+ AddMessage( LPGEN("Skipping inactive contact."));
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if ((nContactVersion < ENTRYV99A) || (nContactVersion == 0)) {
+ AddMessage( LPGEN("Skipping contact with unsupported version."));
+ return INVALID_HANDLE_VALUE;
+ }
+
+ switch(nContactVersion){
+ case ENTRYV99A:
+ if (!(dwOffset = ReadWavList(dwOffset + 0x54))) return INVALID_HANDLE_VALUE;
+ if (!(dwOffset = ReadPropertyBlock(dwOffset + 0x26, NULL, NULL))) return INVALID_HANDLE_VALUE;
+ // Check for custom nickname
+ if (*(PWORD)(pDat + dwOffset) > 1) strNickname = (char*)(dwOffset + pDat + 2);
+ // Find UIN
+ dwOffset += *(PWORD)(pDat + dwOffset) + 2; // Custom nick name
+ dwOffset += *(PWORD)(pDat + dwOffset) + 2; // Nick name
+ dwOffset += *(PWORD)(pDat + dwOffset) + 2; // First name
+ dwOffset += *(PWORD)(pDat + dwOffset) + 2; // Last name
+ dwOffset += *(PWORD)(pDat + dwOffset) + 2; // E-mail
+ dwUIN = *(PDWORD)(pDat + dwOffset); // UIN
+ break;
+
+ case ENTRYV99B:
+ case ENTRYV2000A:
+ case ENTRYV2000B:
+ if (!(dwOffset = ReadWavList(dwOffset + 0x2C))) return INVALID_HANDLE_VALUE;
+ tmpOfs = ReadPropertyBlockList(dwOffset + 0x02, "UIN", &nSearchResult);
+ if (nSearchResult) dwUIN = *(PDWORD)(pDat + tmpOfs + 1);
+ tmpOfs = ReadPropertyBlockList(dwOffset + 0x02, "MyDefinedHandle", &nSearchResult);
+ if (nSearchResult) strNickname = (char*)(tmpOfs + pDat + 3);
+ break;
+
+ case ENTRYV2001A:
+ case ENTRYV2001B:
+ tmpOfs = ReadPropertyBlockList(dwOffset + 0x2C, "MyDefinedHandle", &nSearchResult);
+ if (nSearchResult) strNickname = (char*)(tmpOfs + pDat + 3);
+ tmpOfs = ReadPropertyBlockList(dwOffset + 0x2C, "UIN", &nSearchResult);
+ if (nSearchResult) dwUIN = *(PDWORD)(pDat + tmpOfs + 1);
+ break;
+
+ case ENTRYV2002A:
+ tmpOfs = ReadPropertyBlockList(dwOffset + 0x32, "MyDefinedHandle", &nSearchResult);
+ if (nSearchResult) strNickname = (char*)(tmpOfs + pDat + 3);
+ tmpOfs = ReadPropertyBlockList(dwOffset + 0x32, "UIN", &nSearchResult);
+ if (nSearchResult) dwUIN = *(PDWORD)(pDat + tmpOfs + 1);
+ break;
+ }
+
+ if (!dwUIN) {
+ AddMessage( LPGEN("Skipping unrecognizable contact."));
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (dwUIN < 10000) {
+ AddMessage( LPGEN("Skipping non-ICQ contact %u."), dwUIN );
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (HContactFromNumericID( szICQModuleName[ iICQAccount ], "UIN", dwUIN) == INVALID_HANDLE_VALUE) {
+ DBVARIANT id, nick, group;
+ id.type = DBVT_DWORD; id.dVal = dwUIN;
+ if ( strNickname != NULL && strlen(strNickname) > 0 )
+ nick.type = DBVT_ASCIIZ, nick.pszVal = strNickname;
+ else
+ nick.type = DBVT_DELETED;
+ group.type = DBVT_ASCIIZ, group.pszVal = strGroupName;
+ return AddContact(hdlgProgress, szICQModuleName[ iICQAccount ], "UIN", &id, &nick, &group);
+ }
+ else {
+ if ((strNickname != NULL) && (strlen(strNickname) > 0))
+ AddMessage( LPGEN("Skipping duplicate ICQ contact %u, %s"), dwUIN, strNickname);
+ else
+ AddMessage( LPGEN("Skipping duplicate ICQ contact %u"), dwUIN);
+ }
+
+ // Failure
+ return INVALID_HANDLE_VALUE;
+}
+
+BOOL ImportMessage(DWORD dwOffset)
+{
+ struct TDatMessage *msg = (struct TDatMessage*)(pDat + dwOffset);
+ struct TDatEntryFooter *footer;
+ DBEVENTINFO dbei;
+ HANDLE hContact;
+ int nUCTOffset;
+ TIME_ZONE_INFORMATION TimeZoneInformation;
+ int nHistoryCount = 0;
+
+ // Get timestamp offset. In ICQ, event timestamps are stored
+ // as UTC + (0-TZ offset). YES! That's the negation of the
+ // timezone offset, only God and Mirabilis knows why.
+ GetTimeZoneInformation(&TimeZoneInformation);
+ nUCTOffset = -TimeZoneInformation.Bias * 60;
+
+ // Ignore messages in 'Deleted' folder
+ if (msg->filingStatus&FILING_DELETED)
+ return FALSE;
+
+ // Skip messages from non-icq contacts
+ if (msg->uin < 10000) {
+ AddMessage( LPGEN("Ignoring msg from user %d at ofs %d."), msg->uin, dwOffset );
+ return FALSE;
+ }
+
+ // Ignore received messages?
+ if (( msg->filingStatus & FILING_RECEIVED ) && !( nCustomOptions & IOPT_MSGRECV ))
+ return FALSE;
+
+ // Ignores sent messages?
+ if ( !(msg->filingStatus & FILING_RECEIVED) && !( nCustomOptions & IOPT_MSGSENT ))
+ return FALSE;
+
+ // Check if contact exists in Miranda database
+ hContact = HistoryImportFindContact(hdlgProgress, szICQModuleName[ iICQAccount ], msg->uin, nCustomOptions&IOPT_ADDUNKNOWN);
+ if (hContact == INVALID_HANDLE_VALUE)
+ return FALSE; // Contact couldn't be found/added
+
+ // Convert the event to a Miranda dbevent
+ footer = (struct TDatEntryFooter*)(pDat + dwOffset + msg->textLen + offsetof(struct TDatMessage, text));
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.flags = footer->sent == 1 ? DBEF_SENT : DBEF_READ;
+ dbei.szModule = szICQModuleName[ iICQAccount ];
+ // Convert timestamp
+ dbei.timestamp = footer->timestamp + nUCTOffset;
+ dbei.cbBlob = msg->textLen;
+ dbei.pBlob = (PBYTE)alloca(msg->textLen);
+ CopyMemory(dbei.pBlob, msg->text, dbei.cbBlob);
+ dbei.pBlob[dbei.cbBlob - 1] = 0;
+
+ // Check for duplicate entries
+ if (IsDuplicateEvent(hContact, dbei)) {
+ nDupes++;
+ }
+ else {
+ if (CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei))
+ nMessagesCount++;
+ }
+
+ return TRUE;
+}
+
+BOOL ImportExtendedMessage(DWORD dwOffset)
+{
+ struct TDatMessage *msg = (struct TDatMessage*)(pDat + dwOffset);
+ struct TDatEntryFooter *footer;
+ DBEVENTINFO dbei;
+ HANDLE hContact;
+ int nUCTOffset;
+ TIME_ZONE_INFORMATION TimeZoneInformation;
+ int nHistoryCount = 0;
+ char* pszText = 0;
+ DWORD dwRichTextOffset = 0;
+ DWORD wRichTextLength = 0;
+ DWORD wLength = 0;
+ BOOL bFreeMe = FALSE;
+
+ // Get timestamp offset. In ICQ, event timestamps are stored
+ // as UTC + (0-TZ offset). YES! That's the negation of the
+ // timezone offset, only God and Mirabilis knows why.
+ GetTimeZoneInformation(&TimeZoneInformation);
+ nUCTOffset = -TimeZoneInformation.Bias * 60;
+
+ // Ignore messages in 'Deleted' folder
+ if (msg->filingStatus&FILING_DELETED)
+ return FALSE;
+
+ // Skip messages from non-icq contacts
+ if (msg->uin < 10000) {
+ AddMessage( LPGEN("Ignoring msg from user %d at ofs %d."), msg->uin, dwOffset );
+ return FALSE;
+ }
+
+ // Ignore received messages?
+ if (( msg->filingStatus & FILING_RECEIVED) && !( nCustomOptions & IOPT_MSGRECV ))
+ return FALSE;
+
+ // Ignore sent messages?
+ if ( !( msg->filingStatus & FILING_RECEIVED ) && !( nCustomOptions & IOPT_MSGSENT ))
+ return FALSE;
+
+ // Check if contact exists in Miranda database
+ hContact = HistoryImportFindContact(hdlgProgress, szICQModuleName[ iICQAccount ], msg->uin, nCustomOptions&IOPT_ADDUNKNOWN);
+ if (hContact == INVALID_HANDLE_VALUE)
+ return FALSE; // Contact couldn't be found/added
+
+ // Find a piece of usable text content
+ if (msg->textLen <= 1) {
+ // Skip past the RTF segment
+ wRichTextLength = *(PWORD)(pDat + dwOffset + 0x2A + msg->textLen + 0x21);
+ dwRichTextOffset = dwOffset + 0x2A + msg->textLen + 0x23;
+
+ // Use the UTF-8 text segment
+ wLength = *(PWORD)(pDat + dwRichTextOffset + wRichTextLength);
+ if (wLength <= 1) {
+ AddMessage( LPGEN("Ignoring msg with no text from %d ofs %d."), msg->uin, dwOffset );
+ return FALSE;
+ }
+ pszText = _strdup((char*)pDat + dwRichTextOffset + wRichTextLength + 2);
+ bFreeMe = TRUE;
+ mir_utf8decode(pszText, NULL);
+ wLength = (DWORD)strlen(pszText)+1;
+ }
+ else {
+ // Use the ANSI text segment
+ wLength = msg->textLen;
+ pszText = (char*)(pDat + dwOffset + 0x2A);
+ }
+
+ // Convert the event to a Miranda dbevent
+ footer = (struct TDatEntryFooter*)(pDat + dwOffset + msg->textLen + offsetof(struct TDatMessage, text));
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.flags = footer->sent == 1 ? DBEF_SENT : DBEF_READ;
+ dbei.szModule = szICQModuleName[ iICQAccount ];
+ // Convert timestamp
+ dbei.timestamp = footer->timestamp + nUCTOffset;
+ dbei.cbBlob = wLength;
+ dbei.pBlob = (PBYTE)calloc(wLength,1);
+ CopyMemory(dbei.pBlob, pszText, dbei.cbBlob);
+ dbei.pBlob[dbei.cbBlob - 1] = 0;
+
+ // Check for duplicate entries
+ if (IsDuplicateEvent(hContact, dbei)) {
+ nDupes++;
+ }
+ else {
+ if (CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei))
+ nMessagesCount++;
+ }
+
+ free(dbei.pBlob);
+ if (bFreeMe)
+ free(pszText);
+
+ return TRUE;
+}
+
+BOOL ImportURLMessage(DWORD dwOffset)
+{
+ struct TDatMessage *msg = (struct TDatMessage*)(pDat + dwOffset);
+ struct TDatEntryFooter *footer;
+ DBEVENTINFO dbei;
+ HANDLE hContact;
+ int nUCTOffset;
+ TIME_ZONE_INFORMATION TimeZoneInformation;
+ int nHistoryCount = 0;
+ char *pSeparator;
+
+ // Get timestamp offset. In ICQ, event timestamps are stored
+ // as UTC + (0-TZ offset). YES! That's the negation of the
+ // timezone offset, only God and Mirabilis knows why.
+ GetTimeZoneInformation(&TimeZoneInformation);
+ nUCTOffset = -TimeZoneInformation.Bias * 60;
+
+ // Ignore URLs in 'Deleted' folder
+ if (msg->filingStatus&FILING_DELETED)
+ return FALSE;
+
+ // Skip URLs from non-icq contacts
+ if (msg->uin < 10000) {
+ AddMessage( LPGEN("Ignoring msg from user %d at ofs %d."), msg->uin, dwOffset );
+ return FALSE;
+ }
+
+ // Ignore received URLs?
+ if (( msg->filingStatus & FILING_RECEIVED ) && !( nCustomOptions & IOPT_URLRECV ))
+ return FALSE;
+
+ // Ignores sent URLs?
+ if ( !( msg->filingStatus & FILING_RECEIVED ) && !( nCustomOptions & IOPT_URLSENT ))
+ return FALSE;
+
+ // Check if contact exists in Miranda database
+ hContact = HistoryImportFindContact(hdlgProgress, szICQModuleName[ iICQAccount ], msg->uin, nCustomOptions&IOPT_ADDUNKNOWN);
+ if (hContact == INVALID_HANDLE_VALUE)
+ return FALSE; // Contact couldn't be found/added
+
+ // Convert the event to a Miranda dbevent
+ footer = (struct TDatEntryFooter*)(pDat + dwOffset + msg->textLen + offsetof(struct TDatMessage, text));
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.eventType = EVENTTYPE_URL;
+ dbei.flags = footer->sent == 1 ? DBEF_SENT : DBEF_READ;
+ dbei.szModule = szICQModuleName[ iICQAccount ];
+ // Convert timestamp
+ dbei.timestamp = footer->timestamp + nUCTOffset;
+ dbei.cbBlob = msg->textLen;
+ dbei.pBlob = (PBYTE)alloca(msg->textLen);
+ CopyMemory(dbei.pBlob, msg->text, dbei.cbBlob);
+ dbei.pBlob[dbei.cbBlob - 1] = 0;
+ // Separate URL and description
+ pSeparator = strchr((char*)dbei.pBlob, 0xFE);
+ if (pSeparator != NULL)
+ *pSeparator = 0;
+
+ // Check for duplicate entries
+ if (IsDuplicateEvent(hContact, dbei))
+ nDupes++;
+ else if (CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei))
+ nMessagesCount++;
+
+ return TRUE;
+}
+
+BOOL ImportEvent(DWORD dwOffset)
+{
+ struct TDatMessage *msg = (struct TDatMessage*)(pDat + dwOffset);
+
+ // Events have IDs > 2000
+ if (msg->hdr.entryId < 2001) {
+ AddMessage( LPGEN("Skipping event with ID < 2001."));
+ return FALSE;
+ }
+
+ // Separate code paths based on the event signature
+ switch (msg->hdr.subType) {
+
+ case SUBTYPE_MESSAGE: // All kinds of messages
+ switch (msg->type) {
+ case 1: // Normal message
+ if ((nCustomOptions&IOPT_MSGRECV) || (nCustomOptions&IOPT_MSGSENT)) {
+ return ImportMessage(dwOffset);
+ }
+ break;
+
+ case 4: // URL
+ if ((nCustomOptions&IOPT_URLSENT) || (nCustomOptions&IOPT_URLRECV)) {
+ return ImportURLMessage(dwOffset);
+ }
+ break;
+
+ case 6: // Request for authorization
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Request for auth.' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 7: // Authorization request denied
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Auth. denied' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 8: // Authorization request accepted
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Auth. accepted' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 9: // System message
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'System message', ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 12: // You were added
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'You were added' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 13: // WWWPager ?
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'WWW Pager' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 14: // Email Express ?
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Email Express' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 19: // Contact list
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Contact' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 21: // Phonecall request?
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Phonecall' msg (?), ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 26: // SMS request?
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'SMS' msg (?), ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 29: // Active list invitation ??
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 29 msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 30: // Birthday reminder
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 'Birthday' msg (?), ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case 32: // Unknown (Tomer)
+ #ifdef _LOGGING
+ AddMessage( LPGEN("Skipping 32 msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ default:
+ AddMessage( LPGEN("Skipping unknown 0xE0 subtype (%d), ofs %d."), msg->type, dwOffset );
+
+ #ifdef _LOGGING
+ { // If this is a debug build, dump entry to disk
+ FILE *stream;
+ DWORD dwSize = *(PDWORD)(pDat + dwOffset);
+ wsprintfA(str, "import_unknown_E0subtype_%u-%u.bin", msg->type, dwOffset);
+ stream = fopen(str, "w");
+ fwrite(pDat + dwOffset, 1, dwSize, stream);
+ fclose(stream);
+ }
+ #endif
+
+ return FALSE;
+ }
+ break;
+
+ case SUBTYPE_CHATREQUEST: // 0xE1
+ #ifdef _LOGGING
+ if (nImportOption != IMPORT_CONTACTS)
+ AddMessage( LPGEN("Skipping 'Chat request' msg, ofs %d."), dwOffset );
+ #endif
+ break;
+
+ case SUBTYPE_FILEREQUEST: // 0xE2
+ #ifdef _LOGGING
+ if (nImportOption != IMPORT_CONTACTS)
+ AddMessage( LPGEN("Skipping file message offset %d."), dwOffset );
+ #endif
+ break;
+
+ case 0xE3: // External (IPhone, Battlecom) Maybe general voice calls?
+ #ifdef _LOGGING
+ if (nImportOption != IMPORT_CONTACTS)
+ AddMessage( LPGEN("Skipping message type 0xE3 at offset %d."), dwOffset );
+ #endif
+ break;
+
+ case 0xE4: // My details
+ break;
+ case 0xE5: // Contact
+ break;
+ case 0xE6: // Reminder
+ break;
+ case 0xE7: // Addressbook
+ break;
+ case 0xEC: // Voice message
+ break;
+ case 0xED: // Unknown, something to do with chatting and .CHT files
+ // if (importHistory) {
+ // wsprintf(str, "Skipping message type 0xED at offset %d.", dwOffset);
+ // AddMessage( LPGEN(str);
+ // }
+ break;
+ case 0xEE: // Note
+ break;
+ case 0xEF: // Event folder
+ break;
+ // case 0xF0: // Unknown
+ // if (importHistory) {
+ // wsprintf(str, "Skipping message type 0xF0 at offset %d.", dwOffset);
+ // AddMessage( LPGEN(str);
+ // }
+ // break;
+ case 0xF1: // Server list
+ break;
+ // case 0xF6: // Unknown
+ // if (importHistory) {
+ // wsprintf(str, "Skipping message type 0xF6 at offset %d.", dwOffset);
+ // AddMessage( LPGEN(str);
+ // }
+ // break;
+ case 0x50: // Extended message, ICQ 2000a+?
+ if (nImportOption != IMPORT_CONTACTS) {
+ return ImportExtendedMessage(dwOffset);
+ }
+ break;
+
+ case 0xA0: // URL message type 2
+ if (nImportOption != IMPORT_CONTACTS) {
+ if ((msg->filingStatus&FILING_RECEIVED) || (nCustomOptions&IOPT_URLRECV)) {
+ return ImportURLMessage(dwOffset);
+ }
+ }
+ break;
+
+ default:
+ if (nImportOption != IMPORT_CONTACTS) {
+ AddMessage( LPGEN("Skipping unknown event type %d at offset %d."), msg->hdr.subType, dwOffset );
+
+#ifdef _LOGGING
+ { // If this is a debug build, dump entry to disk
+ FILE *stream;
+ DWORD dwSize;
+ dwSize = *(PDWORD)(pDat + dwOffset);
+ wsprintfA(str, "import_unknown_eventtype_%u-%u.bin", msg->hdr.subType, dwOffset);
+ stream = fopen(str, "w");
+ fwrite(pDat + dwOffset, 1, dwSize, stream);
+ fclose(stream);
+ }
+#endif
+
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+
+static void MirabilisImport(HWND hdlgProgressWnd)
+{
+ HANDLE hIdx, hDat, hIdxMapping, hDatMapping;
+ DWORD i, ofs, highestIndexEntry;
+ TCHAR datFilename[MAX_PATH];
+ MSG msg;
+ DWORD dwTimer;
+
+
+ int status = 0;
+ hdlgProgress = hdlgProgressWnd;
+ nDupes = nContactsCount = nMessagesCount = 0;
+
+ SetProgress(0);
+ lstrcpy(datFilename, importFile);
+ {
+ TCHAR* str2;
+ str2 = _tcsrchr(datFilename,'.');
+ if ( str2 != NULL )
+ lstrcpy(str2, _T(".dat"));
+ }
+
+ hIdx = CreateFile(importFile, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (hIdx == INVALID_HANDLE_VALUE) {
+ AddMessage( LPGEN("Failed to open index file"));
+ AddMessage( LPGEN("Import aborted"));
+ SetProgress(100);
+ return;
+ }
+
+ hDat = CreateFile(datFilename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (hDat == INVALID_HANDLE_VALUE) {
+ AddMessage( LPGEN("Failed to open database file"));
+ AddMessage( LPGEN("Import aborted"));
+ SetProgress(100);
+ return;
+ }
+
+ // Creating file mappings
+ hIdxMapping = CreateFileMapping(hIdx, NULL, PAGE_READONLY, 0, 0, NULL);
+ hDatMapping = CreateFileMapping(hDat, NULL, PAGE_READONLY, 0, 0, NULL);
+
+ // Mapping views of files
+ pIdx = (PBYTE)MapViewOfFile(hIdxMapping, FILE_MAP_READ, 0, 0, 0);
+ pDat = (PBYTE)MapViewOfFile(hDatMapping, FILE_MAP_READ, 0, 0, 0);
+
+ // Is this a supported format?
+ if (GetDBVersion()) {
+ AddMessage( "" );
+
+ highestIndexEntry = GetHighestIndexEntry();
+
+ // Import groups
+ nGroupsCount = ImportGroups();
+ if (nGroupsCount < 0) {
+ AddMessage( LPGEN("Group import was not completed."));
+ nGroupsCount = 0;
+ }
+ AddMessage( "" );
+
+ // Start benchmark timer
+ dwTimer = time(NULL);
+
+ if ( !IsProtocolLoaded( szICQModuleName[iICQAccount] )) {
+ AddMessage( LPGEN("ICQ account is not installed."));
+ AddMessage( LPGEN("No ICQ contacts or history will be imported."));
+ AddMessage( "" );
+ }
+ else {
+ // Configure database for fast writing
+ CallService(MS_DB_SETSAFETYMODE, FALSE, 0);
+
+ // Import contacts
+ AddMessage( LPGEN("Importing contacts"));
+ for (i = 2001; i <= highestIndexEntry; i++) { //event ids start at 2001
+ if (!(i%10)) {
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+ if (!(i%100))
+ SetProgress(100 * (i - 2001) / (highestIndexEntry - 2001));
+
+ ofs = GetIdDatOfs(i);
+ if (ofs != 0) {
+ if (ImportContact(ofs) != INVALID_HANDLE_VALUE)
+ nContactsCount++;
+ }
+ }
+ AddMessage( "" );
+
+ // Import history
+ if (nImportOption != IMPORT_CONTACTS) {
+ AddMessage( LPGEN("Importing history (this may take a while)"));
+ for (i = 2001; i <= highestIndexEntry; i++) { //event ids start at 2001
+ if (!(i%10)) {
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ if (!(i%100))
+ SetProgress(100 * (i - 2001) / (highestIndexEntry - 2001));
+
+ ofs = GetIdDatOfs(i);
+ if (ofs != 0) ImportEvent(ofs);
+ }
+ AddMessage( "" );
+ }
+
+ // Restore database writing mode
+ CallService(MS_DB_SETSAFETYMODE, TRUE, 0);
+ }
+
+ dwTimer = time(NULL) - dwTimer;
+
+ AddMessage( LPGEN("Import completed in %d seconds."), dwTimer );
+ SetProgress(100);
+ AddMessage( LPGEN("Added %d contacts and %d groups."), nContactsCount, nGroupsCount );
+ if ( nImportOption != IMPORT_CONTACTS )
+ AddMessage( LPGEN("Added %d events and skipped %d duplicates."), nMessagesCount, nDupes );
+ }
+
+ UnmapViewOfFile(pDat);
+ UnmapViewOfFile(pIdx);
+ CloseHandle(hDatMapping);
+ CloseHandle(hIdxMapping);
+ CloseHandle(hDat);
+ CloseHandle(hIdx);
+}