/* Variables Plugin for Miranda-IM (www.miranda-im.org) Copyright 2003-2006 P. Boon This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "variables.h" struct _tagType { int cnfCode; TCHAR* str; } static builtinCnfs[] = { { CNF_FIRSTNAME, STR_FIRSTNAME }, { CNF_LASTNAME, STR_LASTNAME }, { CNF_NICK, STR_NICK }, { CNF_CUSTOMNICK, STR_CUSTOMNICK }, { CNF_EMAIL, STR_EMAIL }, { CNF_CITY, STR_CITY }, { CNF_STATE, STR_STATE }, { CNF_COUNTRY, STR_COUNTRY }, { CNF_PHONE, STR_PHONE }, { CNF_HOMEPAGE, STR_HOMEPAGE }, { CNF_ABOUT, STR_ABOUT }, { CNF_GENDER, STR_GENDER }, { CNF_AGE, STR_AGE }, { CNF_FIRSTLAST, STR_FIRSTLAST }, { CNF_UNIQUEID, STR_UNIQUEID }, { CNF_DISPLAY, STR_DISPLAY }, { CNF_FAX, STR_FAX }, { CNF_CELLULAR, STR_CELLULAR }, { CNF_TIMEZONE, STR_TIMEZONE }, { CNF_MYNOTES, STR_MYNOTES }, { CNF_BIRTHDAY, STR_BIRTHDAY }, { CNF_BIRTHMONTH, STR_BIRTHMONTH }, { CNF_BIRTHYEAR, STR_BIRTHYEAR }, { CNF_STREET, STR_STREET }, { CNF_ZIP, STR_ZIP }, { CNF_LANGUAGE1, STR_LANGUAGE1 }, { CNF_LANGUAGE2, STR_LANGUAGE2 }, { CNF_LANGUAGE3, STR_LANGUAGE3 }, { CNF_CONAME, STR_CONAME }, { CNF_CODEPT, STR_CODEPT }, { CNF_COPOSITION, STR_COPOSITION }, { CNF_COSTREET, STR_COSTREET }, { CNF_COCITY, STR_COCITY }, { CNF_COSTATE, STR_COSTATE }, { CNF_COZIP, STR_COZIP }, { CNF_COCOUNTRY, STR_COCOUNTRY }, { CNF_COHOMEPAGE, STR_COHOMEPAGE }, { CCNF_ACCOUNT, STR_ACCOUNT }, { CCNF_PROTOCOL, STR_PROTOCOL }, { CCNF_STATUS, STR_STATUS }, { CCNF_INTERNALIP, STR_INTERNALIP }, { CCNF_EXTERNALIP, STR_EXTERNALIP }, { CCNF_GROUP, STR_GROUP }, { CCNF_PROTOID, STR_PROTOID } }; typedef struct { TCHAR* tszContact; MCONTACT hContact; DWORD flags; } CONTACTCE; /* contact cache entry */ /* cache for 'getcontactfromstring' service */ static CONTACTCE *cce = NULL; static int cacheSize = 0; static CRITICAL_SECTION csContactCache; static HANDLE hContactSettingChangedHook; /* converts a string into a CNF_ type */ BYTE getContactInfoType(TCHAR* type) { if (type == NULL || _tcslen(type) == 0 ) return 0; for (int i=0; i < SIZEOF(builtinCnfs); i++ ) if (!_tcscmp( builtinCnfs[i].str, type )) return builtinCnfs[i].cnfCode; return 0; } /* returns info about a contact as a string */ TCHAR* getContactInfoT(BYTE type, MCONTACT hContact) { /* returns dynamic allocated buffer with info, or NULL if failed */ TCHAR *res = NULL; char protoname[128], szVal[16]; PROTOACCOUNT *pa; if (hContact == NULL) return NULL; char *szProto = GetContactProto(hContact); if (szProto == NULL) return NULL; switch (type) { case CCNF_PROTOID: return mir_a2t(szProto); case CCNF_ACCOUNT: pa = ProtoGetAccount(szProto); return pa ? mir_tstrdup(pa->tszAccountName) : NULL; case CCNF_PROTOCOL: if (CallProtoService(szProto, PS_GETNAME, (WPARAM)sizeof(protoname), (LPARAM)protoname)) return NULL; return mir_a2t(protoname); case CCNF_STATUS: return mir_tstrdup((TCHAR*)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE), GSMDF_UNICODE)); case CCNF_INTERNALIP: case CCNF_EXTERNALIP: { DWORD ip = db_get_dw(hContact, szProto, (type == CCNF_INTERNALIP) ? "RealIP" : "IP", 0); if (ip == 0) return NULL; struct in_addr in; in.s_addr = htonl(ip); return mir_a2t( inet_ntoa(in)); } case CCNF_GROUP: if ((res = db_get_tsa(hContact, "CList", "Group")) != NULL) return res; break; case CNF_UNIQUEID: //UID for ChatRoom if (db_get_b(hContact, szProto, "ChatRoom", 0) == 1) if ((res = db_get_tsa(hContact, szProto, "ChatRoomID")) != NULL) return res; //UID for other contact break; } CONTACTINFO ci = { sizeof(ci) }; ci.hContact = hContact; ci.dwFlag = type | CNF_UNICODE; CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci); memset(szVal, '\0', sizeof(szVal)); switch(ci.type) { case CNFT_BYTE: if (type == CNF_GENDER) { szVal[0] = (char)ci.bVal; szVal[1] = 0; return mir_a2t(szVal); } return itot(ci.bVal); case CNFT_WORD: return itot(ci.wVal); case CNFT_DWORD: return itot(ci.dVal); case CNFT_ASCIIZ: return ci.pszVal; } return NULL; } /* MS_VARS_GETCONTACTFROMSTRING */ int getContactFromString(CONTACTSINFO *ci) { /* service to retrieve a contact's HANDLE from a given string */ if (ci == NULL) return -1; TCHAR *tszContact; if (ci->flags & CI_UNICODE) tszContact = NEWTSTR_ALLOCA(ci->tszContact); else { WCHAR* tmp = mir_a2t(ci->szContact); tszContact = NEWTSTR_ALLOCA(tmp); mir_free(tmp); } if ((tszContact == NULL) || (_tcslen(tszContact) == 0)) return -1; ci->hContacts = NULL; int count = 0; /* search the cache */ { mir_cslock lck(csContactCache); for (int i=0; i < cacheSize; i++) { if ((!_tcscmp(cce[i].tszContact, tszContact)) && (ci->flags == cce[i].flags)) { /* found in cache */ ci->hContacts = (MCONTACT*)mir_alloc(sizeof(MCONTACT)); if (ci->hContacts == NULL) return -1; ci->hContacts[0] = cce[i].hContact; return 1; } } } /* contact was not in cache, do a search */ for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { TCHAR *szFind = NULL; BOOL bMatch = FALSE; // <_HANDLE_:hContact> size_t size = _tcslen(_T(PROTOID_HANDLE)) + 36; szFind = (TCHAR *)mir_alloc(size * sizeof(TCHAR)); if (szFind != NULL) { mir_sntprintf(szFind, size, _T("<%s:%p>"), _T(PROTOID_HANDLE), hContact); if (!_tcsncmp(tszContact, szFind, _tcslen(tszContact))) bMatch = TRUE; mir_free(szFind); } char *szProto = GetContactProto(hContact); if (szProto == NULL) continue; // (exact) if ((ci->flags & CI_PROTOID) && !bMatch) { TCHAR *cInfo = getContactInfoT(CNF_UNIQUEID, hContact); if (cInfo) { size_t size = _tcslen(cInfo) + strlen(szProto) + 4; szFind = (TCHAR *)mir_alloc(size * sizeof(TCHAR)); if (szFind != NULL) { mir_sntprintf(szFind, size, _T("<%S:%s>"), szProto, cInfo); mir_free(cInfo); if (!_tcsncmp(tszContact, szFind, _tcslen(tszContact))) bMatch = TRUE; mir_free(szFind); } } } // id (exact) if ((ci->flags & CI_UNIQUEID) && (!bMatch)) { szFind = getContactInfoT(CNF_UNIQUEID, hContact); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) bMatch = TRUE; mir_free(szFind); } } // nick (not exact) if ((ci->flags & CI_NICK) && (!bMatch)) { szFind = getContactInfoT(CNF_NICK, hContact); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) bMatch = TRUE; mir_free(szFind); } } // list name (not exact) if ((ci->flags & CI_LISTNAME) && (!bMatch)) { szFind = getContactInfoT(CNF_DISPLAY, hContact); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) bMatch = TRUE; mir_free(szFind); } } // firstname (exact) if ((ci->flags & CI_FIRSTNAME) && (!bMatch)) { szFind = getContactInfoT(CNF_FIRSTNAME, hContact); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) bMatch = TRUE; mir_free(szFind); } } // lastname (exact) if ((ci->flags & CI_LASTNAME) && (!bMatch)) { szFind = getContactInfoT(CNF_LASTNAME, hContact); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) bMatch = TRUE; mir_free(szFind); } } // email (exact) if ((ci->flags & CI_EMAIL) && (!bMatch)) { szFind = getContactInfoT(CNF_EMAIL, hContact); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) bMatch = TRUE; mir_free(szFind); } } // CNF_ (exact) if ((ci->flags & CI_CNFINFO) && (!bMatch)) { szFind = getContactInfoT((BYTE)(ci->flags&~(CI_CNFINFO|CI_TCHAR)), hContact); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) bMatch = TRUE; mir_free(szFind); } } if (bMatch) { ci->hContacts = (MCONTACT*)mir_realloc(ci->hContacts, (count + 1)*sizeof(MCONTACT)); if (ci->hContacts == NULL) return -1; ci->hContacts[count] = hContact; count += 1; } } if (count == 1) { /* cache the found result */ mir_cslock lck(csContactCache); cce = (CONTACTCE*)mir_realloc(cce, (cacheSize+1)*sizeof(CONTACTCE)); if (cce != NULL) { cce[cacheSize].hContact = ci->hContacts[0]; cce[cacheSize].flags = ci->flags; cce[cacheSize].tszContact = mir_tstrdup(tszContact); if (cce[cacheSize].tszContact != NULL) cacheSize += 1; } } return count; } /* keep cache consistent */ static int contactSettingChanged(WPARAM hContact, LPARAM lParam) { DBCONTACTWRITESETTING *dbw = (DBCONTACTWRITESETTING*)lParam; mir_cslock lck(csContactCache); for (int i=0; i < cacheSize; i++) { if (hContact != cce[i].hContact && (cce[i].flags & CI_CNFINFO) == 0 ) continue; char *szProto = GetContactProto(hContact); if (szProto == NULL) continue; char *uid = (char*)CallProtoService(szProto,PS_GETCAPS,PFLAG_UNIQUEIDSETTING,0); if (((!strcmp(dbw->szSetting, "Nick")) && (cce[i].flags & CI_NICK)) || ((!strcmp(dbw->szSetting, "FirstName")) && (cce[i].flags & CI_FIRSTNAME)) || ((!strcmp(dbw->szSetting, "LastName")) && (cce[i].flags & CI_LASTNAME)) || ((!strcmp(dbw->szSetting, "e-mail")) && (cce[i].flags & CI_EMAIL)) || ((!strcmp(dbw->szSetting, "MyHandle")) && (cce[i].flags & CI_LISTNAME)) || (cce[i].flags & CI_CNFINFO) != 0 || // lazy; always invalidate CNF info cache entries (( ((INT_PTR)uid != CALLSERVICE_NOTFOUND) && (uid != NULL)) && (!strcmp(dbw->szSetting, uid)) && (cce[i].flags & CI_UNIQUEID))) { /* remove from cache */ mir_free(cce[i].tszContact); if (cacheSize > 1) { memmove(&cce[i], &cce[cacheSize-1], sizeof(CONTACTCE)); cce = ( CONTACTCE* )mir_realloc(cce, (cacheSize-1)*sizeof(CONTACTCE)); cacheSize -= 1; } else { mir_free(cce); cce = NULL; cacheSize = 0; } break; } } return 0; } int initContactModule() { InitializeCriticalSection(&csContactCache); hContactSettingChangedHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, contactSettingChanged); return 0; } int deinitContactModule() { UnhookEvent(hContactSettingChangedHook); DeleteCriticalSection(&csContactCache); return 0; } // returns a string in the form , cannot be _HANDLE_! // result must be freed TCHAR* encodeContactToString(MCONTACT hContact) { char *szProto = GetContactProto(hContact); TCHAR *tszUniqueId = getContactInfoT(CNF_UNIQUEID, hContact); if (szProto == NULL || tszUniqueId == NULL) return NULL; size_t size = _tcslen(tszUniqueId) + strlen(szProto) + 4; TCHAR *tszResult = (TCHAR *)mir_calloc(size * sizeof(TCHAR)); if (tszResult) mir_sntprintf(tszResult, size, _T("<%S:%s>"), szProto, tszUniqueId); return tszResult; } // returns a contact from a string in the form // returns INVALID_HANDLE_VALUE in case of an error. MCONTACT decodeContactFromString(TCHAR *tszContact) { MCONTACT hContact = INVALID_CONTACT_ID; CONTACTSINFO ci = { sizeof(ci) }; ci.tszContact = tszContact; ci.flags = CI_PROTOID|CI_TCHAR; int count = getContactFromString( &ci ); if (count != 1) { mir_free(ci.hContacts); return hContact; } if (ci.hContacts != NULL) { hContact = ci.hContacts[0]; mir_free(ci.hContacts); } return hContact; }