From f920ef497f3299ae24fe783ce03bdd93b419f764 Mon Sep 17 00:00:00 2001 From: Kirill Volinsky Date: Fri, 18 May 2012 22:02:50 +0000 Subject: plugins folders renaming git-svn-id: http://svn.miranda-ng.org/main/trunk@60 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/Import/mirabilis.c | 1493 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1493 insertions(+) create mode 100644 plugins/Import/mirabilis.c (limited to 'plugins/Import/mirabilis.c') diff --git a/plugins/Import/mirabilis.c b/plugins/Import/mirabilis.c new file mode 100644 index 0000000000..79a07f1546 --- /dev/null +++ b/plugins/Import/mirabilis.c @@ -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(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 = 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); +} -- cgit v1.2.3