// ---------------------------------------------------------------------------80 // ICQ plugin for Miranda Instant Messenger // ________________________________________ // // Copyright © 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede // Copyright © 2001-2002 Jon Keating, Richard Hughes // Copyright © 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater // Copyright © 2004-2008 Joe Kucera // // 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. // // ----------------------------------------------------------------------------- // DESCRIPTION: // // Implements Manage Server List dialog // // ----------------------------------------------------------------------------- #include "icqoscar.h" static int bListInit = 0; static HANDLE hItemAll; static int dwUploadDelay = 1000; // initial setting, it is too low for icq server but good for short updates static HWND hwndUploadContacts=NULL; static const UINT settingsControls[]={IDOK}; static WORD* pwGroupIds = NULL; static int cbGroupIds = 0; // Init default clist options static void ResetCListOptions(HWND hwndList) { int i; SendMessage(hwndList, CLM_SETBKBITMAP, 0, (LPARAM)(HBITMAP)NULL); SendMessage(hwndList, CLM_SETBKCOLOR, GetSysColor(COLOR_WINDOW), 0); SendMessage(hwndList, CLM_SETGREYOUTFLAGS, 0, 0); SendMessage(hwndList, CLM_SETLEFTMARGIN, 2, 0); SendMessage(hwndList, CLM_SETINDENT, 10, 0); for(i=0; i<=FONTID_MAX; i++) SendMessage(hwndList, CLM_SETTEXTCOLOR, i, GetSysColor(COLOR_WINDOWTEXT)); SetWindowLongPtr(hwndList, GWL_STYLE, GetWindowLongPtr(hwndList, GWL_STYLE)|CLS_SHOWHIDDEN); if (CallService(MS_CLUI_GETCAPS, 0, 0) & CLUIF_HIDEEMPTYGROUPS) // hide empty groups SendMessage(hwndList, CLM_SETHIDEEMPTYGROUPS, (WPARAM) TRUE, 0); } // Selects the "All contacts" checkbox if all other list entries // are selected, deselects it if not. static void UpdateAllContactsCheckmark(HWND hwndList, CIcqProto* ppro, HANDLE phItemAll) { int check = 1; HANDLE hContact = ppro->FindFirstContact(); while (hContact) { HANDLE hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0); if (hItem) { if (!SendMessage(hwndList, CLM_GETCHECKMARK, (WPARAM)hItem, 0)) { // if any of our contacts is unchecked, uncheck all contacts as well check = 0; break; } } hContact = ppro->FindNextContact(hContact); } SendMessage(hwndList, CLM_SETCHECKMARK, (WPARAM)phItemAll, check); } // Loop over all contacts and update the checkmark // that indicates wether or not they are already uploaded static int UpdateCheckmarks(HWND hwndList, CIcqProto* ppro, HANDLE phItemAll) { int bAll = 1; bListInit = 1; // lock CLC events HANDLE hContact = ppro->FindFirstContact(); while (hContact) { HANDLE hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0); if (hItem) { if (ppro->getWord(hContact, DBSETTING_SERVLIST_ID, 0)) SendMessage(hwndList, CLM_SETCHECKMARK, (WPARAM)hItem, 1); else bAll = 0; } hContact = ppro->FindNextContact(hContact); } // Update the "All contacts" checkmark if (phItemAll) SendMessage(hwndList, CLM_SETCHECKMARK, (WPARAM)phItemAll, bAll); bListInit = 0; return bAll; } static void DeleteOtherContactsFromControl(HWND hCtrl, CIcqProto* ppro) { for (HANDLE hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { HANDLE hItem = (HANDLE)SendMessage(hCtrl, CLM_FINDCONTACT, (WPARAM)hContact, 0); if (hItem) if (!ppro->IsICQContact(hContact)) SendMessage(hCtrl, CLM_DELETEITEM, (WPARAM)hItem, 0); } } static void AppendToUploadLog(HWND hwndDlg, const char *fmt, ...) { va_list va; char szText[1024]; int iItem; va_start(va, fmt); mir_vsnprintf(szText, sizeof(szText), fmt, va); va_end(va); iItem = ListBoxAddStringUtf(GetDlgItem(hwndDlg, IDC_LOG), szText); SendDlgItemMessage(hwndDlg, IDC_LOG, LB_SETTOPINDEX, iItem, 0); } static void DeleteLastUploadLogLine(HWND hwndDlg) { SendDlgItemMessage(hwndDlg, IDC_LOG, LB_DELETESTRING, SendDlgItemMessage(hwndDlg, IDC_LOG, LB_GETCOUNT, 0, 0)-1, 0); } static void GetLastUploadLogLine(HWND hwndDlg, char *szBuf, size_t cbBuf) { WCHAR str[MAX_PATH]; SendDlgItemMessageW(hwndDlg, IDC_LOG, LB_GETTEXT, SendDlgItemMessage(hwndDlg, IDC_LOG, LB_GETCOUNT, 0, 0)-1, (LPARAM)str); make_utf8_string_static(str, szBuf, cbBuf); } static int GroupEnumIdsEnumProc(const char *szSetting,LPARAM lParam) { if (szSetting && strlennull(szSetting)<5) { // it is probably server group char val[MAX_PATH+2]; // dummy DBVARIANT dbv; DBCONTACTGETSETTING cgs; dbv.type = DBVT_ASCIIZ; dbv.pszVal = val; dbv.cchVal = MAX_PATH; cgs.szModule=(char*)lParam; cgs.szSetting=szSetting; cgs.pValue=&dbv; if(CallService(MS_DB_CONTACT_GETSETTINGSTATIC,0,(LPARAM)&cgs)) return 0; // this converts all string types to DBVT_ASCIIZ if(dbv.type!=DBVT_ASCIIZ) { // it is not a cached server-group name return 0; } pwGroupIds = (WORD*)SAFE_REALLOC(pwGroupIds, (cbGroupIds+1)*sizeof(WORD)); pwGroupIds[cbGroupIds] = (WORD)strtoul(szSetting, NULL, 0x10); cbGroupIds++; } return 0; } static void enumServerGroups(CIcqProto* ppro) { char szModule[MAX_PATH+9]; mir_snprintf(szModule, SIZEOF(szModule), "%s%s", ppro->m_szModuleName, "SrvGroups"); DBCONTACTENUMSETTINGS dbces = { 0 }; dbces.pfnEnumProc = &GroupEnumIdsEnumProc; dbces.szModule = szModule; dbces.lParam = (LPARAM)szModule; CallService(MS_DB_CONTACT_ENUMSETTINGS, 0, (LPARAM)&dbces); } static DWORD sendUploadGroup(CIcqProto* ppro, WORD wAction, WORD wGroupId, char* szItemName) { DWORD dwCookie; cookie_servlist_action* ack; if (ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action))) { // we have cookie good, go on ack->wGroupId = wGroupId; ack->dwAction = SSA_SERVLIST_ACK; dwCookie = ppro->AllocateCookie(CKT_SERVERLIST, wAction, 0, ack); ack->lParam = dwCookie; ppro->icq_sendServerGroup(dwCookie, wAction, ack->wGroupId, szItemName, NULL, 0, 0); return dwCookie; } return 0; } static DWORD sendUploadBuddy(CIcqProto* ppro, HANDLE hContact, WORD wAction, DWORD dwUin, char *szUID, WORD wContactId, WORD wGroupId, WORD wItemType) { DWORD dwCookie; cookie_servlist_action* ack; if (ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action))) { // we have cookie good, go on ack->hContact = hContact; ack->wContactId = wContactId; ack->wGroupId = wGroupId; ack->dwAction = SSA_SERVLIST_ACK; dwCookie = ppro->AllocateCookie(CKT_SERVERLIST, wAction, hContact, ack); ack->lParam = dwCookie; if (wItemType == SSI_ITEM_BUDDY) ppro->icq_sendServerContact(hContact, dwCookie, wAction, ack->wGroupId, ack->wContactId, SSOP_ITEM_ACTION | SSOF_CONTACT, 500, NULL); else ppro->icq_sendSimpleItem(dwCookie, wAction, dwUin, szUID, ack->wGroupId, ack->wContactId, wItemType, SSOP_ITEM_ACTION, 500); return dwCookie; } return 0; } static char* getServerResultDesc(int wCode) { switch (wCode) { case 0: return LPGEN("OK"); case 2: return LPGEN("NOT FOUND"); case 3: return LPGEN("ALREADY EXISTS"); case 0xA: return LPGEN("INVALID DATA"); case 0xC: return LPGEN("LIST FULL"); default: return LPGEN("FAILED"); } } #define ACTION_NONE 0 #define ACTION_ADDBUDDY 1 #define ACTION_ADDBUDDYAUTH 2 #define ACTION_REMOVEBUDDY 3 #define ACTION_ADDGROUP 4 #define ACTION_REMOVEGROUP 5 #define ACTION_UPDATESTATE 6 #define ACTION_MOVECONTACT 7 #define ACTION_ADDVISIBLE 8 #define ACTION_REMOVEVISIBLE 9 #define ACTION_ADDINVISIBLE 10 #define ACTION_REMOVEINVISIBLE 11 #define STATE_READY 1 #define STATE_REGROUP 2 #define STATE_ITEMS 3 #define STATE_VISIBILITY 5 #define STATE_CONSOLIDATE 4 #define M_PROTOACK (WM_USER+100) #define M_UPLOADMORE (WM_USER+101) #define M_INITCLIST (WM_USER+102) static INT_PTR CALLBACK DlgProcUploadList(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam) { CIcqProto* ppro = (CIcqProto*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); static int working; static HANDLE hProtoAckHook; static int currentSequence; static int currentAction; static int currentState; static HANDLE hCurrentContact; static int lastAckResult = 0; static WORD wNewContactId; static WORD wNewGroupId; static char *szNewGroupName; static WORD wNewVisibilityId; switch(message) { case WM_INITDIALOG: TranslateDialogDefault(hwndDlg); SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); { char str[MAX_PATH]; working = 0; hProtoAckHook = NULL; currentState = STATE_READY; ResetCListOptions(GetDlgItem(hwndDlg, IDC_CLIST)); AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Select contacts you want to store on server."), str, MAX_PATH)); AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Ready..."), str, MAX_PATH)); } return TRUE; // The M_PROTOACK message is received when the // server has responded to our last update packet case M_PROTOACK: { int bMulti = 0; ACKDATA *ack = (ACKDATA*)lParam; char szLastLogLine[MAX_PATH]; char str[MAX_PATH]; // Is this an ack we are waiting for? if (strcmpnull(ack->szModule, ppro->m_szModuleName)) break; if (ack->type == ICQACKTYPE_RATEWARNING) { // we are sending tooo fast, slow down the process if (ack->hProcess != (HANDLE)1) break; // check class if (ack->lParam == 2 || ack->lParam == 3) // check status { GetLastUploadLogLine(hwndDlg, szLastLogLine, MAX_PATH); DeleteLastUploadLogLine(hwndDlg); AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Server rate warning -> slowing down the process."), str, MAX_PATH)); AppendToUploadLog(hwndDlg, szLastLogLine); dwUploadDelay *= 2; break; } if (ack->lParam == 4) dwUploadDelay /= 2; // the rate is ok, turn up } if (ack->type != ICQACKTYPE_SERVERCLIST) break; if ((int)ack->hProcess != currentSequence) break; lastAckResult = ack->result == ACKRESULT_SUCCESS ? 0 : 1; switch (currentAction) { case ACTION_ADDBUDDY: if (ack->result == ACKRESULT_SUCCESS) { ppro->setByte(hCurrentContact, "Auth", 0); ppro->setWord(hCurrentContact, DBSETTING_SERVLIST_ID, wNewContactId); ppro->setWord(hCurrentContact, DBSETTING_SERVLIST_GROUP, wNewGroupId); break; } else { // If the server refused to add the contact without authorization, // we try again _with_ authorization TLV DWORD dwUIN; uid_str szUID; ppro->setByte(hCurrentContact, "Auth", 1); if (!ppro->getContactUid(hCurrentContact, &dwUIN, &szUID)) { currentAction = ACTION_ADDBUDDYAUTH; currentSequence = sendUploadBuddy(ppro, hCurrentContact, ICQ_LISTS_ADDTOLIST, dwUIN, szUID, wNewContactId, wNewGroupId, SSI_ITEM_BUDDY); } return FALSE; } case ACTION_ADDBUDDYAUTH: if (ack->result == ACKRESULT_SUCCESS) { ppro->setWord(hCurrentContact, DBSETTING_SERVLIST_ID, wNewContactId); ppro->setWord(hCurrentContact, DBSETTING_SERVLIST_GROUP, wNewGroupId); } else { db_unset(hCurrentContact, ppro->m_szModuleName, "Auth"); ppro->FreeServerID(wNewContactId, SSIT_ITEM); } break; case ACTION_REMOVEBUDDY: if (ack->result == ACKRESULT_SUCCESS) { // clear obsolete settings ppro->FreeServerID(wNewContactId, SSIT_ITEM); db_unset(hCurrentContact, ppro->m_szModuleName, DBSETTING_SERVLIST_ID); db_unset(hCurrentContact, ppro->m_szModuleName, DBSETTING_SERVLIST_GROUP); db_unset(hCurrentContact, ppro->m_szModuleName, "Auth"); } break; case ACTION_ADDGROUP: if (ack->result == ACKRESULT_SUCCESS) { void* groupData; int groupSize; cookie_servlist_action* ack; ppro->setServListGroupName(wNewGroupId, szNewGroupName); // add group to list ppro->setServListGroupLinkID(szNewGroupName, wNewGroupId); // grouppath is known groupData = ppro->collectGroups(&groupSize); groupData = SAFE_REALLOC(groupData, groupSize+2); *(((WORD*)groupData)+(groupSize>>1)) = wNewGroupId; // add this new group id groupSize += 2; ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action)); if (ack) { DWORD dwCookie; // we do not use this ack->dwAction = SSA_SERVLIST_ACK; dwCookie = ppro->AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_UPDATEGROUP, 0, ack); ppro->icq_sendServerGroup(dwCookie, ICQ_LISTS_UPDATEGROUP, 0, ack->szGroupName, groupData, groupSize, 0); } SAFE_FREE((void**)&groupData); } else ppro->FreeServerID(wNewGroupId, SSIT_GROUP); SAFE_FREE((void**)&szNewGroupName); break; case ACTION_REMOVEGROUP: if (ack->result == ACKRESULT_SUCCESS) { void* groupData; int groupSize; cookie_servlist_action* ack; ppro->FreeServerID(wNewGroupId, SSIT_GROUP); ppro->setServListGroupName(wNewGroupId, NULL); // remove group from list ppro->removeGroupPathLinks(wNewGroupId); // grouppath is known groupData = ppro->collectGroups(&groupSize); ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action)); if (ack) { DWORD dwCookie; // we do not use this ack->dwAction = SSA_SERVLIST_ACK; dwCookie = ppro->AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_UPDATEGROUP, 0, ack); ppro->icq_sendServerGroup(dwCookie, ICQ_LISTS_UPDATEGROUP, 0, ack->szGroupName, groupData, groupSize, 0); } SAFE_FREE((void**)&groupData); } break; case ACTION_UPDATESTATE: // do nothing break; case ACTION_MOVECONTACT: if (ack->result == ACKRESULT_SUCCESS) { ppro->FreeServerID(ppro->getWord(hCurrentContact, DBSETTING_SERVLIST_ID, 0), SSIT_ITEM); ppro->setWord(hCurrentContact, DBSETTING_SERVLIST_ID, wNewContactId); ppro->setWord(hCurrentContact, DBSETTING_SERVLIST_GROUP, wNewGroupId); dwUploadDelay *= 2; // we double the delay here (2 packets) } break; case ACTION_ADDVISIBLE: if (ack->result == ACKRESULT_SUCCESS) ppro->setWord(hCurrentContact, DBSETTING_SERVLIST_PERMIT, wNewContactId); else ppro->FreeServerID(wNewContactId, SSIT_ITEM); break; case ACTION_ADDINVISIBLE: if (ack->result == ACKRESULT_SUCCESS) ppro->setWord(hCurrentContact, DBSETTING_SERVLIST_DENY, wNewContactId); else ppro->FreeServerID(wNewContactId, SSIT_ITEM); break; case ACTION_REMOVEVISIBLE: if (ack->result == ACKRESULT_SUCCESS) { ppro->FreeServerID(wNewContactId, SSIT_ITEM); ppro->setWord(hCurrentContact, DBSETTING_SERVLIST_PERMIT, 0); } break; case ACTION_REMOVEINVISIBLE: if (ack->result == ACKRESULT_SUCCESS) { ppro->FreeServerID(wNewContactId, SSIT_ITEM); ppro->setWord(hCurrentContact, DBSETTING_SERVLIST_DENY, 0); } break; } // Update the log window GetLastUploadLogLine(hwndDlg, szLastLogLine, MAX_PATH); DeleteLastUploadLogLine(hwndDlg); AppendToUploadLog(hwndDlg, "%s%s", szLastLogLine, ICQTranslateUtfStatic(getServerResultDesc(ack->lParam), str, MAX_PATH)); if (!bMulti) { SetTimer(hwndDlg, M_UPLOADMORE, dwUploadDelay, 0); // delay } } break; case WM_TIMER: { switch (wParam) { case M_UPLOADMORE: KillTimer(hwndDlg, M_UPLOADMORE); if (currentAction == ACTION_MOVECONTACT) dwUploadDelay /= 2; // turn it back PostMessage(hwndDlg, M_UPLOADMORE, 0, 0); return 0; } } // The M_UPLOADMORE window message is received when the user presses 'Update' // and every time an ack from the server has been taken care of. case M_UPLOADMORE: { HANDLE hContact; HANDLE hItem; DWORD dwUin; uid_str szUid; char *pszNick; char *pszGroup; int isChecked; int isOnServer; BOOL bUidOk; char str[MAX_PATH]; HWND hwndList = GetDlgItem(hwndDlg, IDC_CLIST); switch (currentState) { case STATE_REGROUP: // TODO: iterate over all checked groups and create if needed // if creation requires reallocation of groups do it here currentState = STATE_ITEMS; hCurrentContact = NULL; PostMessage(hwndDlg, M_UPLOADMORE, 0, 0); break; case STATE_ITEMS: // Iterate over all contacts until one is found that // needs to be updated on the server if (hCurrentContact == NULL) hContact = ppro->FindFirstContact(); else // we do not want to go thru all contacts over and over again { hContact = hCurrentContact; if (lastAckResult) // if the last operation on this contact fail, do not do it again, go to next hContact = ppro->FindNextContact(hContact); } while (hContact) { hCurrentContact = hContact; hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0); if (hItem) { isChecked = SendMessage(hwndList, CLM_GETCHECKMARK, (WPARAM)hItem, 0) != 0; isOnServer = ppro->getWord(hContact, DBSETTING_SERVLIST_ID, 0) != 0; bUidOk = !ppro->getContactUid(hContact, &dwUin, &szUid); // Is this one out of sync? if (bUidOk && (isChecked != isOnServer)) { // Only upload custom nicks pszNick = ppro->getSettingStringUtf(hContact, "CList", "MyHandle", NULL); if (isChecked) { // Queue for uploading pszGroup = ppro->getContactCListGroup(hContact); if (!strlennull(pszGroup)) pszGroup = null_strdup(DEFAULT_SS_GROUP); // Get group ID from cache, if not ready use parent group, if still not ready create one wNewGroupId = ppro->getServListGroupLinkID(pszGroup); if (!wNewGroupId && strstrnull(pszGroup, "\\") != NULL) { // if it is sub-group, take master parent strstrnull(pszGroup, "\\")[0] = '\0'; wNewGroupId = ppro->getServListGroupLinkID(pszGroup); } if (!wNewGroupId && currentAction != ACTION_ADDGROUP) { // if the group still does not exist and there was no try before, try to add group AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Adding group \"%s\"..."), str, MAX_PATH), pszGroup); wNewGroupId = ppro->GenerateServerID(SSIT_GROUP, 0); // ??? szNewGroupName = pszGroup; currentAction = ACTION_ADDGROUP; currentSequence = sendUploadGroup(ppro, ICQ_LISTS_ADDTOLIST, wNewGroupId, pszGroup); SAFE_FREE(&pszNick); return FALSE; } SAFE_FREE(&pszGroup); AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Uploading %s..."), str, MAX_PATH), pszNick ? pszNick : strUID(dwUin, szUid)); currentAction = ACTION_ADDBUDDY; if (wNewGroupId) { wNewContactId = ppro->GenerateServerID(SSIT_ITEM, 0); currentSequence = sendUploadBuddy(ppro, hCurrentContact, ICQ_LISTS_ADDTOLIST, dwUin, szUid, wNewContactId, wNewGroupId, SSI_ITEM_BUDDY); SAFE_FREE(&pszNick); return FALSE; } else { char szLastLogLine[MAX_PATH]; // Update the log window with the failure and continue with next contact GetLastUploadLogLine(hwndDlg, szLastLogLine, MAX_PATH); DeleteLastUploadLogLine(hwndDlg); AppendToUploadLog(hwndDlg, "%s%s", szLastLogLine, ICQTranslateUtfStatic(LPGEN("FAILED"), str, MAX_PATH)); AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("No upload group available"), str, MAX_PATH)); ppro->debugLogA("Upload failed, no group"); currentState = STATE_READY; } } else { // Queue for deletion if (pszNick) AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Deleting %s..."), str, MAX_PATH), pszNick); else AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Deleting %s..."), str, MAX_PATH), strUID(dwUin, szUid)); wNewGroupId = ppro->getWord(hContact, DBSETTING_SERVLIST_GROUP, 0); wNewContactId = ppro->getWord(hContact, DBSETTING_SERVLIST_ID, 0); currentAction = ACTION_REMOVEBUDDY; currentSequence = sendUploadBuddy(ppro, hContact, ICQ_LISTS_REMOVEFROMLIST, dwUin, szUid, wNewContactId, wNewGroupId, SSI_ITEM_BUDDY); } SAFE_FREE((void**)&pszNick); break; } else if (bUidOk && isChecked) { // the contact is and should be on server, check if it is in correct group, move otherwise WORD wCurrentGroupId = ppro->getWord(hContact, DBSETTING_SERVLIST_GROUP, 0); pszGroup = ppro->getContactCListGroup(hContact); if (!strlennull(pszGroup)) pszGroup = null_strdup(DEFAULT_SS_GROUP); wNewGroupId = ppro->getServListGroupLinkID(pszGroup); if (!wNewGroupId && strstrnull(pszGroup, "\\") != NULL) { // if it is sub-group, take master parent strstrnull(pszGroup, "\\")[0] = '\0'; wNewGroupId = ppro->getServListGroupLinkID(pszGroup); } if (!wNewGroupId && currentAction != ACTION_ADDGROUP) { // if the group still does not exist and there was no try before, try to add group AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Adding group \"%s\"..."), str, MAX_PATH), pszGroup); wNewGroupId = ppro->GenerateServerID(SSIT_GROUP, 0); szNewGroupName = pszGroup; currentAction = ACTION_ADDGROUP; currentSequence = sendUploadGroup(ppro, ICQ_LISTS_ADDTOLIST, wNewGroupId, pszGroup); return FALSE; } if (wNewGroupId && (wNewGroupId != wCurrentGroupId)) { // we have a group the contact should be in, move it WORD wCurrentContactId = ppro->getWord(hContact, DBSETTING_SERVLIST_ID, 0); BYTE bAuth = ppro->getByte(hContact, "Auth", 0); pszNick = ppro->getSettingStringUtf(hContact, "CList", "MyHandle", NULL); if (pszNick) AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Moving %s to group \"%s\"..."), str, MAX_PATH), pszNick, pszGroup); else AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Moving %s to group \"%s\"..."), str, MAX_PATH), strUID(dwUin, szUid), pszGroup); currentAction = ACTION_MOVECONTACT; wNewContactId = ppro->GenerateServerID(SSIT_ITEM, 0); sendUploadBuddy(ppro, hContact, ICQ_LISTS_REMOVEFROMLIST, dwUin, szUid, wCurrentContactId, wCurrentGroupId, SSI_ITEM_BUDDY); currentSequence = sendUploadBuddy(ppro, hContact, ICQ_LISTS_ADDTOLIST, dwUin, szUid, wNewContactId, wNewGroupId, SSI_ITEM_BUDDY); SAFE_FREE((void**)&pszNick); SAFE_FREE((void**)&pszGroup); break; } SAFE_FREE((void**)&pszGroup); } } hContact = db_find_next(hContact); } if (!hContact) { currentState = STATE_VISIBILITY; hCurrentContact = NULL; PostMessage(hwndDlg, M_UPLOADMORE, 0, 0); } break; case STATE_VISIBILITY: // Iterate over all contacts until one is found that // needs to be updated on the server if (hCurrentContact == NULL) hContact = ppro->FindFirstContact(); else // we do not want to go thru all contacts over and over again { hContact = hCurrentContact; if (lastAckResult) // if the last operation on this contact fail, do not do it again, go to next hContact = ppro->FindNextContact(hContact); } while (hContact) { WORD wApparentMode = ppro->getWord(hContact, "ApparentMode", 0); WORD wDenyId = ppro->getWord(hContact, DBSETTING_SERVLIST_DENY, 0); WORD wPermitId = ppro->getWord(hContact, DBSETTING_SERVLIST_PERMIT, 0); WORD wIgnoreId = ppro->getWord(hContact, DBSETTING_SERVLIST_IGNORE, 0); hCurrentContact = hContact; ppro->getContactUid(hContact, &dwUin, &szUid); if (wApparentMode == ID_STATUS_ONLINE) { // contact is on the visible list if (wPermitId == 0) { currentAction = ACTION_ADDVISIBLE; wNewContactId = ppro->GenerateServerID(SSIT_ITEM, 0); AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Adding %s to visible list..."), str, MAX_PATH), strUID(dwUin, szUid)); currentSequence = sendUploadBuddy(ppro, hContact, ICQ_LISTS_ADDTOLIST, dwUin, szUid, wNewContactId, 0, SSI_ITEM_PERMIT); break; } } if (wApparentMode == ID_STATUS_OFFLINE) { // contact is on the invisible list if (wDenyId == 0 && wIgnoreId == 0) { currentAction = ACTION_ADDINVISIBLE; wNewContactId = ppro->GenerateServerID(SSIT_ITEM, 0); AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Adding %s to invisible list..."), str, MAX_PATH), strUID(dwUin, szUid)); currentSequence = sendUploadBuddy(ppro, hContact, ICQ_LISTS_ADDTOLIST, dwUin, szUid, wNewContactId, 0, SSI_ITEM_DENY); break; } } if (wApparentMode != ID_STATUS_ONLINE) { // contact is not on visible list if (wPermitId != 0) { currentAction = ACTION_REMOVEVISIBLE; wNewContactId = wPermitId; AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Deleting %s from visible list..."), str, MAX_PATH), strUID(dwUin, szUid)); currentSequence = sendUploadBuddy(ppro, hContact, ICQ_LISTS_REMOVEFROMLIST, dwUin, szUid, wNewContactId, 0, SSI_ITEM_PERMIT); break; } } if (wApparentMode != ID_STATUS_OFFLINE) { // contact is not on invisible list if (wDenyId != 0) { currentAction = ACTION_REMOVEINVISIBLE; wNewContactId = wDenyId; AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Deleting %s from invisible list..."), str, MAX_PATH), strUID(dwUin, szUid)); currentSequence = sendUploadBuddy(ppro, hContact, ICQ_LISTS_REMOVEFROMLIST, dwUin, szUid, wNewContactId, 0, SSI_ITEM_DENY); break; } } hContact = db_find_next(hContact); } if (!hContact) { currentState = STATE_CONSOLIDATE; AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Cleaning groups"), str, MAX_PATH)); EnableDlgItem(hwndDlg, IDCANCEL, FALSE); enumServerGroups(ppro); PostMessage(hwndDlg, M_UPLOADMORE, 0, 0); } break; case STATE_CONSOLIDATE: // updage group data, remove redundant groups if (currentAction == ACTION_UPDATESTATE) DeleteLastUploadLogLine(hwndDlg); if (cbGroupIds) // some groups in the list { void* groupData; int groupSize; cbGroupIds--; wNewGroupId = pwGroupIds[cbGroupIds]; if (groupData = ppro->collectBuddyGroup(wNewGroupId, &groupSize)) { // the group is still not empty, just update it char* pszGroup = ppro->getServListGroupName(wNewGroupId); cookie_servlist_action* ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action)); ack->dwAction = SSA_SERVLIST_ACK; ack->wGroupId = wNewGroupId; currentSequence = ppro->AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_UPDATEGROUP, 0, ack); ack->lParam = currentSequence; currentAction = ACTION_UPDATESTATE; AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Updating group \"%s\"..."), str, MAX_PATH), pszGroup); ppro->icq_sendServerGroup(currentSequence, ICQ_LISTS_UPDATEGROUP, wNewGroupId, pszGroup, groupData, groupSize, 0); SAFE_FREE((void**)&pszGroup); } else // the group is empty, delete it if it does not have sub-groups { if (!ppro->CheckServerID((WORD)(wNewGroupId+1), 0) || ppro->getServListGroupLevel((WORD)(wNewGroupId+1)) == 0) { // is next id an sub-group, if yes, we cannot delete this group char *pszGroup = ppro->getServListGroupName(wNewGroupId); currentAction = ACTION_REMOVEGROUP; AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Deleting group \"%s\"..."), str, MAX_PATH), pszGroup); currentSequence = sendUploadGroup(ppro, ICQ_LISTS_REMOVEFROMLIST, wNewGroupId, pszGroup); SAFE_FREE((void**)&pszGroup); } else // update empty group { char *pszGroup = ppro->getServListGroupName(wNewGroupId); cookie_servlist_action *ack = (cookie_servlist_action*)SAFE_MALLOC(sizeof(cookie_servlist_action)); ack->dwAction = SSA_SERVLIST_ACK; ack->wGroupId = wNewGroupId; currentSequence = ppro->AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_UPDATEGROUP, 0, ack); ack->lParam = currentSequence; currentAction = ACTION_UPDATESTATE; AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("Updating group \"%s\"..."), str, MAX_PATH), pszGroup); ppro->icq_sendServerGroup(currentSequence, ICQ_LISTS_UPDATEGROUP, wNewGroupId, pszGroup, 0, 0, 0); SAFE_FREE((void**)&pszGroup); } } SAFE_FREE((void**)&groupData); // free the memory } else { // all groups processed SAFE_FREE((void**)&pwGroupIds); currentState = STATE_READY; } break; } if (currentState == STATE_READY) { // All contacts are in sync AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("All operations complete"), str, MAX_PATH)); EnableDlgItem(hwndDlg, IDCANCEL, TRUE); SetDlgItemTextUtf(hwndDlg, IDCANCEL, ICQTranslateUtfStatic(LPGEN("Close"), str, MAX_PATH)); // end server modifications here ppro->servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100); working = 0; // SendMessage(hwndList, CLM_SETGREYOUTFLAGS,0,0); UpdateCheckmarks(hwndList, ppro, hItemAll); // EnableWindow(hwndList, FALSE); if (hProtoAckHook) UnhookEvent(hProtoAckHook); } break; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: SendDlgItemMessage(hwndDlg, IDC_LOG, LB_RESETCONTENT, 0, 0); if (!ppro->icqOnline()) { char str[MAX_PATH]; AppendToUploadLog(hwndDlg, ICQTranslateUtfStatic(LPGEN("You have to be online to sychronize the server-list!"), str, MAX_PATH)); break; } working = 1; hCurrentContact = NULL; currentState = STATE_REGROUP; currentAction = ACTION_NONE; icq_ShowMultipleControls(hwndDlg, settingsControls, SIZEOF(settingsControls), SW_HIDE); // SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETGREYOUTFLAGS, 0xFFFFFFFF, 0); // InvalidateRect(GetDlgItem(hwndDlg, IDC_CLIST), NULL, FALSE); EnableDlgItem(hwndDlg, IDC_CLIST, FALSE); hProtoAckHook = HookEventMessage(ME_PROTO_ACK, hwndDlg, M_PROTOACK); // start server modifications here ppro->servlistPostPacket(NULL, 0, SSO_BEGIN_OPERATION | SSOF_IMPORT_OPERATION, 100); PostMessage(hwndDlg, M_UPLOADMORE, 0, 0); break; case IDCANCEL: // TODO: this must be clean DestroyWindow(hwndDlg); break; } break; case WM_NOTIFY: switch(((NMHDR*)lParam)->idFrom) { case IDC_CLIST: { HWND hClist = GetDlgItem(hwndDlg, IDC_CLIST); switch(((NMHDR*)lParam)->code) { case CLN_OPTIONSCHANGED: ResetCListOptions(hClist); break; case CLN_NEWCONTACT: case CLN_CONTACTMOVED: // Delete non-icq contacts DeleteOtherContactsFromControl(hClist, ppro); if (hItemAll) UpdateAllContactsCheckmark(hClist, ppro, hItemAll); break; case CLN_LISTREBUILT: { int bCheck = false; // Delete non-icq contacts if ( ppro ) { DeleteOtherContactsFromControl(hClist, ppro); if (!bListInit) // do not enter twice bCheck = UpdateCheckmarks(hClist, ppro, NULL); } if (!hItemAll) // Add the "All contacts" item { CLCINFOITEM cii = {0}; cii.cbSize = sizeof(cii); cii.flags = CLCIIF_GROUPFONT | CLCIIF_CHECKBOX; cii.pszText = TranslateT(LPGEN("** All contacts **")); hItemAll = (HANDLE)SendMessage(hClist, CLM_ADDINFOITEM, 0, (LPARAM)&cii); } SendMessage(hClist, CLM_SETCHECKMARK, (WPARAM)hItemAll, bCheck); } break; case CLN_CHECKCHANGED: { NMCLISTCONTROL *nm = (NMCLISTCONTROL*)lParam; HANDLE hContact; HANDLE hItem; if (bListInit) break; if (nm->flags&CLNF_ISINFO) { int check; check = SendMessage(hClist, CLM_GETCHECKMARK, (WPARAM)hItemAll, 0); hContact = ppro->FindFirstContact(); while (hContact) { hItem = (HANDLE)SendMessage(hClist, CLM_FINDCONTACT, (WPARAM)hContact, 0); if (hItem) SendMessage(hClist, CLM_SETCHECKMARK, (WPARAM)hItem, check); hContact = ppro->FindNextContact(hContact); } } else UpdateAllContactsCheckmark(hClist, ppro, hItemAll); } break; } } break; } break; case WM_CLOSE: DestroyWindow(hwndDlg); break; case WM_DESTROY: if (hProtoAckHook) UnhookEvent(hProtoAckHook); if (working) { // end server modifications here ppro->servlistPostPacket(NULL, 0, SSO_END_OPERATION, 100); } hwndUploadContacts = NULL; working = 0; break; } return FALSE; } void CIcqProto::ShowUploadContactsDialog(void) { if (hwndUploadContacts == NULL) { hItemAll = NULL; hwndUploadContacts = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_ICQUPLOADLIST), NULL, DlgProcUploadList, LPARAM(this)); } SetForegroundWindow(hwndUploadContacts); }