/* 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" #include "contact.h" struct _tagType { int cnfCode; TCHAR* str; } static builtinCnfs[] = { { CNF_FIRSTNAME, _T(STR_FIRSTNAME) }, { CNF_LASTNAME, _T(STR_LASTNAME) }, { CNF_NICK, _T(STR_NICK) }, { CNF_CUSTOMNICK, _T(STR_CUSTOMNICK) }, { CNF_EMAIL, _T(STR_EMAIL) }, { CNF_CITY, _T(STR_CITY) }, { CNF_STATE, _T(STR_STATE) }, { CNF_COUNTRY, _T(STR_COUNTRY) }, { CNF_PHONE, _T(STR_PHONE) }, { CNF_HOMEPAGE, _T(STR_HOMEPAGE) }, { CNF_ABOUT, _T(STR_ABOUT) }, { CNF_GENDER, _T(STR_GENDER) }, { CNF_AGE, _T(STR_AGE) }, { CNF_FIRSTLAST, _T(STR_FIRSTLAST) }, { CNF_UNIQUEID, _T(STR_UNIQUEID) }, { CNF_DISPLAY, _T(STR_DISPLAY) }, { CNF_FAX, _T(STR_FAX) }, { CNF_CELLULAR, _T(STR_CELLULAR) }, { CNF_TIMEZONE, _T(STR_TIMEZONE) }, { CNF_MYNOTES, _T(STR_MYNOTES) }, { CNF_BIRTHDAY, _T(STR_BIRTHDAY) }, { CNF_BIRTHMONTH, _T(STR_BIRTHMONTH) }, { CNF_BIRTHYEAR, _T(STR_BIRTHYEAR) }, { CNF_STREET, _T(STR_STREET) }, { CNF_ZIP, _T(STR_ZIP) }, { CNF_LANGUAGE1, _T(STR_LANGUAGE1) }, { CNF_LANGUAGE2, _T(STR_LANGUAGE2) }, { CNF_LANGUAGE3, _T(STR_LANGUAGE3) }, { CNF_CONAME, _T(STR_CONAME) }, { CNF_CODEPT, _T(STR_CODEPT) }, { CNF_COPOSITION, _T(STR_COPOSITION) }, { CNF_COSTREET, _T(STR_COSTREET) }, { CNF_COCITY, _T(STR_COCITY) }, { CNF_COSTATE, _T(STR_COSTATE) }, { CNF_COZIP, _T(STR_COZIP) }, { CNF_COCOUNTRY, _T(STR_COCOUNTRY) }, { CNF_COHOMEPAGE, _T(STR_COHOMEPAGE) }, { CCNF_ACCOUNT, _T(STR_ACCOUNT) }, { CCNF_PROTOCOL, _T(STR_PROTOCOL) }, { CCNF_STATUS, _T(STR_STATUS) }, { CCNF_INTERNALIP, _T(STR_INTERNALIP) }, { CCNF_EXTERNALIP, _T(STR_EXTERNALIP) }, { CCNF_GROUP, _T(STR_GROUP) }, { CCNF_PROTOID, _T(STR_PROTOID) } }; #define NEWTSTR_ALLOCA(A) (A==NULL)?NULL:_tcscpy((TCHAR*)alloca(sizeof(TCHAR)*(_tcslen(A)+1)),A) typedef struct { TCHAR* tszContact; HANDLE 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; static HANDLE hGetContactFromStringService; /* converts a string into a CNF_ type */ BYTE getContactInfoType(TCHAR* type) { int i; if ( type == NULL || _tcslen(type) == 0 ) return 0; for ( 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 */ static TCHAR* getContactInfo(BYTE type, HANDLE hContact) { /* returns dynamic allocated buffer with info, or NULL if failed */ CONTACTINFO ci; TCHAR *res, *szStatus; char *szProto, protoname[128], szVal[16]; int status; BYTE realType; DBVARIANT dbv; res = NULL; if (hContact == NULL) return NULL; realType = (type & ~CNF_UNICODE); switch ( realType ) { case CCNF_PROTOID: szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); if (szProto == NULL) return NULL; if ( type & CNF_UNICODE ) return (TCHAR *)a2u(szProto); return (TCHAR *)_strdup(szProto); case CCNF_ACCOUNT: if ( g_mirandaVersion < PLUGIN_MAKE_VERSION( 0,8,0,0 )) return NULL; szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); if (szProto) { PROTOACCOUNT* pa = ProtoGetAccount( szProto ); if ( pa ) return _tcsdup( pa->tszAccountName ); } return NULL; case CCNF_PROTOCOL: szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); if (szProto == NULL) return NULL; if (CallProtoService(szProto, PS_GETNAME, (WPARAM)sizeof(protoname), (LPARAM)protoname)) return NULL; if ( type & CNF_UNICODE ) return (TCHAR *)a2u(protoname); return (TCHAR *)_strdup(protoname); case CCNF_STATUS: szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); if (szProto == NULL) return NULL; status = DBGetContactSettingWord(hContact, szProto, "Status", 0); if (status == 0) return NULL; if (type & CNF_UNICODE) { szStatus = (TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM)status, (LPARAM)GSMDF_UNICODE); if (szStatus == NULL) return NULL; return _tcsdup(szStatus); } else { char *aszStatus = (char *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM)status, 0); if (aszStatus == NULL) return NULL; return (TCHAR *)_strdup(aszStatus); } case CCNF_INTERNALIP: case CCNF_EXTERNALIP: szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); if (szProto == NULL) return NULL; else { DWORD ip = DBGetContactSettingDword(hContact, szProto, (realType==CCNF_INTERNALIP)?"RealIP":"IP", 0); if (ip == 0) return NULL; struct in_addr in; in.s_addr = htonl(ip); char* dotted = inet_ntoa(in); if ( dotted == NULL ) return NULL; if ( type & CNF_UNICODE ) return (TCHAR *)a2u(dotted); return (TCHAR *)_strdup(dotted); } case CCNF_GROUP: if (!DBGetContactSetting(hContact, "CList", "Group", &dbv)) { if ( type & CNF_UNICODE ) res = (TCHAR *)a2u(dbv.pszVal); else res = (TCHAR *)_strdup(dbv.pszVal); DBFreeVariant(&dbv); return res; } if (!DBGetContactSettingW(hContact, "CList", "Group", &dbv)) { if ( type & CNF_UNICODE ) res = (TCHAR *)_wcsdup(dbv.pwszVal); else res = (TCHAR *)u2a(dbv.pwszVal); DBFreeVariant(&dbv); return res; } break; case CNF_UNIQUEID: //UID for ChatRoom szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); if ( szProto != NULL ) { if ( DBGetContactSettingByte(hContact, szProto, "ChatRoom", 0) == 1) { DBVARIANT dbv; if ( !DBGetContactSettingTString(hContact, szProto, "ChatRoomID", &dbv )) { res = _tcsdup( dbv.ptszVal ); DBFreeVariant( &dbv ); return res; } } } //UID for other contact break; } ZeroMemory(&ci,sizeof(CONTACTINFO)); ci.cbSize = sizeof(CONTACTINFO); ci.hContact = hContact; ci.dwFlag = type; if (CallService(MS_CONTACT_GETCONTACTINFO,(WPARAM)0,(LPARAM)&ci)) { if ( !( type & CNF_UNICODE )) return NULL; // retrieving the data using UNICODE failed, try without ci.dwFlag &= ~CNF_UNICODE; if ( CallService( MS_CONTACT_GETCONTACTINFO,(WPARAM)0,(LPARAM)&ci )) return NULL; if (ci.type == CNFT_ASCIIZ) { if (type & CNF_UNICODE) { TCHAR *ptszVal; ptszVal = (TCHAR *)a2u((char *)ci.pszVal); mir_free(ci.pszVal); return ptszVal; } else { res = (TCHAR *)_strdup((char *)ci.pszVal); mir_free(ci.pszVal); return res; } } } memset(szVal, '\0', sizeof(szVal)); switch(ci.type){ case CNFT_BYTE: if (realType != CNF_GENDER) return itot(ci.bVal); szVal[0] = (char)ci.bVal; if ( type & CNF_UNICODE ) return (TCHAR *)a2u(szVal); return (TCHAR *)_strdup(szVal); case CNFT_WORD: return itot(ci.wVal); case CNFT_DWORD: return itot(ci.dVal); case CNFT_ASCIIZ: if (ci.pszVal != NULL) { if (type&CNF_UNICODE) res = _tcsdup(ci.pszVal); else res = (TCHAR *)_strdup((char *)ci.pszVal); mir_free(ci.pszVal); } break; } return res; } TCHAR *getContactInfoT(BYTE type, HANDLE hContact, int tchar) { if (tchar) { return getContactInfo((BYTE)(type|CNF_UNICODE), hContact); } return getContactInfo(type, hContact); } /* MS_VARS_GETCONTACTFROMSTRING */ int getContactFromString( CONTACTSINFO* ci ) { /* service to retrieve a contact's HANDLE from a given string */ char *szProto; TCHAR *szFind, *cInfo, *tszContact, *tszProto; BOOL bMatch; DBVARIANT dbv; HANDLE hContact; int count, i; if (ci == NULL) return -1; if (ci->flags&CI_UNICODE) { tszContact = NEWTSTR_ALLOCA(ci->tszContact); } else { WCHAR* tmp = a2u(ci->szContact); tszContact = NEWTSTR_ALLOCA(tmp); free(tmp); } if ( (tszContact == NULL) || (_tcslen(tszContact) == 0)) return -1; ci->hContacts = NULL; count = 0; /* search the cache */ EnterCriticalSection(&csContactCache); for (i=0;iflags == cce[i].flags)) { /* found in cache */ ci->hContacts = ( HANDLE* )malloc(sizeof(HANDLE)); if (ci->hContacts == NULL) { LeaveCriticalSection(&csContactCache); return -1; } ci->hContacts[0] = cce[i].hContact; LeaveCriticalSection(&csContactCache); return 1; } } LeaveCriticalSection(&csContactCache); /* contact was not in cache, do a search */ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); while (hContact != NULL) { szFind = NULL; bMatch = FALSE; ZeroMemory(&dbv, sizeof(DBVARIANT)); szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); if (szProto == NULL) { hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact, 0); continue; } // (exact) if (ci->flags&CI_PROTOID) { cInfo = getContactInfoT(CNF_UNIQUEID, hContact, ci->flags&CI_TCHAR); if (cInfo == NULL) { // cInfo = itot((int)hContact); szFind = ( TCHAR* )malloc((_tcslen(cInfo) + _tcslen(_T(PROTOID_HANDLE)) + 4)*sizeof(TCHAR)); if (szFind != NULL) { wsprintf(szFind, _T("<%s:%s>"), _T(PROTOID_HANDLE), cInfo); if (!_tcsncmp(tszContact, szFind, _tcslen(tszContact))) { bMatch = TRUE; } free(cInfo); free(szFind); } } else { szFind = ( TCHAR* )malloc((_tcslen(cInfo) + strlen(szProto) + 4)*sizeof(TCHAR)); if (szFind != NULL) { tszProto = a2u(szProto); if ( (tszProto != NULL) && (szFind != NULL)) { wsprintf(szFind, _T("<%s:%s>"), tszProto, cInfo); free(cInfo); free(tszProto); if (!_tcsncmp(tszContact, szFind, _tcslen(tszContact))) bMatch = TRUE; free(szFind); } } } } // id (exact) if ( (ci->flags&CI_UNIQUEID) && (!bMatch)) { szFind = getContactInfoT(CNF_UNIQUEID, hContact, ci->flags&CI_TCHAR); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) bMatch = TRUE; free(szFind); } } // nick (not exact) if ( (ci->flags&CI_NICK) && (!bMatch)) { szFind = getContactInfoT(CNF_NICK, hContact, ci->flags&CI_TCHAR); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) bMatch = TRUE; free(szFind); } } // list name (not exact) if ( (ci->flags&CI_LISTNAME) && (!bMatch)) { szFind = getContactInfoT(CNF_DISPLAY, hContact, ci->flags&CI_TCHAR); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) bMatch = TRUE; free(szFind); } } // firstname (exact) if ( (ci->flags&CI_FIRSTNAME) && (!bMatch)) { szFind = getContactInfoT(CNF_FIRSTNAME, hContact, ci->flags&CI_TCHAR); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) { bMatch = TRUE; } free(szFind); } } // lastname (exact) if ( (ci->flags&CI_LASTNAME) && (!bMatch)) { szFind = getContactInfoT(CNF_LASTNAME, hContact, ci->flags&CI_TCHAR); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) { bMatch = TRUE; } free(szFind); } } // email (exact) if ( (ci->flags&CI_EMAIL) && (!bMatch)) { szFind = getContactInfoT(CNF_EMAIL, hContact, ci->flags&CI_TCHAR); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) { bMatch = TRUE; } free(szFind); } } // CNF_ (exact) if ( (ci->flags&CI_CNFINFO) && (!bMatch)) { szFind = getContactInfoT((BYTE)(ci->flags&~(CI_CNFINFO|CI_TCHAR)), hContact, ci->flags&CI_TCHAR); if (szFind != NULL) { if (!_tcscmp(tszContact, szFind)) { bMatch = TRUE; } free(szFind); } } if (bMatch) { ci->hContacts = ( HANDLE* )realloc(ci->hContacts, (count+1)*sizeof(HANDLE)); if (ci->hContacts == NULL) { return -1; } ci->hContacts[count] = hContact; count += 1; } hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0); } if (count == 1) { /* cache the found result */ EnterCriticalSection(&csContactCache); cce = ( CONTACTCE* )realloc(cce, (cacheSize+1)*sizeof(CONTACTCE)); if (cce == NULL) { LeaveCriticalSection(&csContactCache); return count; } cce[cacheSize].hContact = ci->hContacts[0]; cce[cacheSize].flags = ci->flags; cce[cacheSize].tszContact = _tcsdup(tszContact); if (cce[cacheSize].tszContact != NULL) cacheSize += 1; LeaveCriticalSection(&csContactCache); } return count; } /* keep cache consistent */ static int contactSettingChanged(WPARAM wParam, LPARAM lParam) { int i; DBCONTACTWRITESETTING *dbw; char *szProto, *uid; uid = NULL; EnterCriticalSection(&csContactCache); for (i=0;iszSetting, "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)uid != CALLSERVICE_NOTFOUND) && (uid != NULL)) && (!strcmp(dbw->szSetting, uid)) && (cce[i].flags & CI_UNIQUEID))) { /* remove from cache */ free(cce[i].tszContact); if (cacheSize > 1) { MoveMemory(&cce[i], &cce[cacheSize-1], sizeof(CONTACTCE)); cce = ( CONTACTCE* )realloc(cce, (cacheSize-1)*sizeof(CONTACTCE)); cacheSize -= 1; } else { free(cce); cce = NULL; cacheSize = 0; } break; } } LeaveCriticalSection(&csContactCache); return 0; } static INT_PTR getContactFromStringSvc( WPARAM wParam, LPARAM lParam) { return getContactFromString(( CONTACTSINFO* )wParam ); } int initContactModule() { InitializeCriticalSection(&csContactCache); hContactSettingChangedHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, contactSettingChanged); hGetContactFromStringService = CreateServiceFunction(MS_VARS_GETCONTACTFROMSTRING, getContactFromStringSvc); return 0; } int deinitContactModule() { DestroyServiceFunction(hGetContactFromStringService); UnhookEvent(hContactSettingChangedHook); DeleteCriticalSection(&csContactCache); return 0; } // returns a string in the form , cannot be _HANDLE_! // result must be freed TCHAR *encodeContactToString(HANDLE hContact) { char *szProto; TCHAR *tszUniqueId, *tszResult, *tszProto; DBVARIANT dbv; ZeroMemory(&dbv, sizeof(DBVARIANT)); szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); tszUniqueId = getContactInfoT(CNF_UNIQUEID, hContact, CI_TCHAR); if ( szProto == NULL || tszUniqueId == NULL ) return NULL; tszResult = ( TCHAR* )calloc((_tcslen(tszUniqueId) + strlen(szProto) + 4), sizeof(TCHAR)); if (tszResult == NULL) return NULL; tszProto = a2u(szProto); if (tszProto == NULL) return NULL; wsprintf(tszResult, _T("<%s:%s>"), tszProto, tszUniqueId); return tszResult; } // returns a contact from a string in the form // returns INVALID_HANDLE_VALUE in case of an error. HANDLE *decodeContactFromString(TCHAR *tszContact) { int count; HANDLE hContact; CONTACTSINFO ci; hContact = INVALID_HANDLE_VALUE; ZeroMemory(&ci, sizeof(CONTACTSINFO)); ci.cbSize = sizeof(CONTACTSINFO); ci.tszContact = tszContact; ci.flags = CI_PROTOID|CI_TCHAR; count = getContactFromString( &ci ); if (count != 1) { if (ci.hContacts != NULL) free(ci.hContacts); return ( HANDLE* )hContact; } if (ci.hContacts != NULL) { hContact = ci.hContacts[0]; free(ci.hContacts); } return ( HANDLE* )hContact; }