/* Jabber Protocol Plugin for Miranda IM Copyright (C) 2002-04 Santithorn Bunchua Copyright (C) 2005-12 George Hazan Copyright (C) 2012-13 Miranda NG Project 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 "jabber.h" #include #include "jabber_list.h" #include "jabber_caps.h" #include "m_clistint.h" extern CRITICAL_SECTION mutex; extern int bSecureIM, bMirOTR, bNewGPG, bPlatform; void CJabberProto::SerialInit(void) { InitializeCriticalSection(&m_csSerial); m_nSerial = 0; } void CJabberProto::SerialUninit(void) { DeleteCriticalSection(&m_csSerial); } int CJabberProto::SerialNext(void) { unsigned int ret; EnterCriticalSection(&m_csSerial); ret = m_nSerial; m_nSerial++; LeaveCriticalSection(&m_csSerial); return ret; } void CJabberProto::Log(const char* fmt, ...) { va_list vararg; va_start(vararg, fmt); char* str = (char*)alloca(32000); mir_vsnprintf(str, 32000, fmt, vararg); va_end(vararg); CallService(MS_NETLIB_LOG, (WPARAM)m_hNetlibUser, (LPARAM)str); } /////////////////////////////////////////////////////////////////////////////// // JabberChatRoomHContactFromJID - looks for the char room HCONTACT with required JID HANDLE CJabberProto::ChatRoomHContactFromJID(const TCHAR *jid) { if (jid == NULL) return NULL; for (HANDLE hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) { DBVARIANT dbv; int result = getTString(hContact, "ChatRoomID", &dbv); if (result) result = getTString(hContact, "jid", &dbv); if ( !result) { int result; result = lstrcmpi(jid, dbv.ptszVal); db_free(&dbv); if ( !result && getByte(hContact, "ChatRoom", 0) != 0) return hContact; } } return NULL; } /////////////////////////////////////////////////////////////////////////////// // JabberHContactFromJID - looks for the HCONTACT with required JID HANDLE CJabberProto::HContactFromJID(const TCHAR *jid , BOOL bStripResource) { if (jid == NULL) return (HANDLE)NULL; JABBER_LIST_ITEM* item = ListGetItemPtr(LIST_CHATROOM, jid); for (HANDLE hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) { DBVARIANT dbv; int result; //safer way to check UID (coz some contact have both setting from convert to chat) if (db_get_b(hContact, m_szModuleName, "ChatRoom",0)) result = getTString(hContact, "ChatRoomID", &dbv); else result = getTString(hContact, "jid", &dbv); if ( !result) { int result; if (item != NULL) result = lstrcmpi(jid, dbv.ptszVal); else { if (bStripResource == 3) { if (getByte(hContact, "ChatRoom", 0)) result = lstrcmpi(jid, dbv.ptszVal); // for chat room we have to have full contact matched else if (TRUE) result = _tcsnicmp(jid, dbv.ptszVal, _tcslen(dbv.ptszVal)); else result = JabberCompareJids(jid, dbv.ptszVal); } // most probably it should just look full matching contact else result = lstrcmpi(jid, dbv.ptszVal); } db_free(&dbv); if ( !result) return hContact; } } return NULL; } TCHAR* __stdcall JabberNickFromJID(const TCHAR *jid) { if ( !jid) return mir_tstrdup(_T("")); const TCHAR *p; TCHAR* nick; if ((p = _tcschr(jid, '@')) == NULL) p = _tcschr(jid, '/'); if (p != NULL) { if ((nick=(TCHAR*)mir_alloc(sizeof(TCHAR)*(int(p-jid)+1))) != NULL) { _tcsncpy(nick, jid, p-jid); nick[p-jid] = '\0'; } } else nick = mir_tstrdup(jid); return nick; } JABBER_RESOURCE_STATUS* CJabberProto::ResourceInfoFromJID(const TCHAR *jid) { if ( !jid) return NULL; JABBER_LIST_ITEM *item = NULL; if ((item = ListGetItemPtr(LIST_VCARD_TEMP, jid)) == NULL) item = ListGetItemPtr(LIST_ROSTER, jid); if (item == NULL) return NULL; const TCHAR *p = _tcschr(jid, '/'); if (p == NULL) return &item->itemResource; if (*++p == '\0') return NULL; JABBER_RESOURCE_STATUS *r = item->resource; if (r == NULL) return NULL; int i; for (i=0; iresourceCount && _tcscmp(r->resourceName, p); i++, r++); if (i >= item->resourceCount) return NULL; return r; } JABBER_LIST_ITEM* CJabberProto::GetItemFromContact(HANDLE hContact) { DBVARIANT dbv; if (getTString(hContact, "jid", &dbv)) return NULL; JABBER_LIST_ITEM *pItem = ListGetItemPtr(LIST_ROSTER, dbv.ptszVal); db_free(&dbv); return pItem; } TCHAR* JabberPrepareJid(LPCTSTR jid) { if ( !jid) return NULL; TCHAR* szNewJid = mir_tstrdup(jid); if ( !szNewJid) return NULL; TCHAR* pDelimiter = _tcschr(szNewJid, _T('/')); if (pDelimiter) *pDelimiter = _T('\0'); CharLower(szNewJid); if (pDelimiter) *pDelimiter = _T('/'); return szNewJid; } void strdel(char* parBuffer, int len) { char* p; for (p = parBuffer+len; *p != 0; p++) p[ -len ] = *p; p[ -len ] = '\0'; } char* __stdcall JabberUrlDecode(char* str) { char* p, *q; if (str == NULL) return NULL; for (p=q=str; *p!='\0'; p++,q++) { if (*p == '<') { // skip CDATA if ( !strncmp(p, ""); size_t count = tail ? (tail-p) : strlen(p); memmove(q, p, count); q += count-1; p = (tail ? (tail+3) : (p+count)) - 1; } else { *q = *p; } } else if (*p == '&') { if ( !strncmp(p, "&", 5)) { *q = '&'; p += 4; } else if ( !strncmp(p, "'", 6)) { *q = '\''; p += 5; } else if ( !strncmp(p, ">", 4)) { *q = '>'; p += 3; } else if ( !strncmp(p, "<", 4)) { *q = '<'; p += 3; } else if ( !strncmp(p, """, 6)) { *q = '"'; p += 5; } else { *q = *p; } } else { *q = *p; } } *q = '\0'; return str; } void __stdcall JabberUrlDecodeW(WCHAR* str) { if (str == NULL) return; WCHAR* p, *q; for (p=q=str; *p!='\0'; p++,q++) { if (*p == '&') { if ( !wcsncmp(p, L"&", 5)) { *q = '&'; p += 4; } else if ( !wcsncmp(p, L"'", 6)) { *q = '\''; p += 5; } else if ( !wcsncmp(p, L">", 4)) { *q = '>'; p += 3; } else if ( !wcsncmp(p, L"<", 4)) { *q = '<'; p += 3; } else if ( !wcsncmp(p, L""", 6)) { *q = '"'; p += 5; } else { *q = *p; } } else { *q = *p; } } *q = '\0'; } char* __stdcall JabberUrlEncode(const char* str) { char* s, *p, *q; int c; if (str == NULL) return NULL; for (c=0,p=(char*)str; *p!='\0'; p++) { switch (*p) { case '&': c += 5; break; case '\'': c += 6; break; case '>': c += 4; break; case '<': c += 4; break; case '"': c += 6; break; default: c++; break; } } if ((s=(char*)mir_alloc(c+1)) != NULL) { for (p=(char*)str,q=s; *p!='\0'; p++) { switch (*p) { case '&': strcpy(q, "&"); q += 5; break; case '\'': strcpy(q, "'"); q += 6; break; case '>': strcpy(q, ">"); q += 4; break; case '<': strcpy(q, "<"); q += 4; break; case '"': strcpy(q, """); q += 6; break; default: if (*p > 0 && *p < 32) { switch(*p) { case '\r': case '\n': case '\t': *q = *p; break; default: *q = '?'; } } else *q = *p; q++; break; } } *q = '\0'; } return s; } void __stdcall JabberUtfToTchar(const char* pszValue, size_t cbLen, LPTSTR& dest) { char* pszCopy = NULL; bool bNeedsFree = false; __try { // this code can cause access violation when a stack overflow occurs pszCopy = (char*)alloca(cbLen+1); } __except(EXCEPTION_EXECUTE_HANDLER) { bNeedsFree = true; pszCopy = (char*)malloc(cbLen+1); } if (pszCopy == NULL) return; memcpy(pszCopy, pszValue, cbLen); pszCopy[ cbLen ] = 0; JabberUrlDecode(pszCopy); mir_utf8decode(pszCopy, &dest); if (bNeedsFree) free(pszCopy); } char* __stdcall JabberSha1(char* str) { mir_sha1_ctx sha; mir_sha1_byte_t digest[20]; char* result; int i; if (str == NULL) return NULL; mir_sha1_init(&sha); mir_sha1_append(&sha, (mir_sha1_byte_t*)str, (int)strlen(str)); mir_sha1_finish(&sha, digest); if ((result=(char*)mir_alloc(41)) == NULL) return NULL; for (i=0; i<20; i++) sprintf(result+(i<<1), "%02x", digest[i]); return result; } TCHAR* __stdcall JabberStrFixLines(const TCHAR *str) { if ( !str) return NULL; const TCHAR *p; int add = 0; bool prev_r = false; bool prev_n = false; for (p = str; p && *p; ++p) if (*p == _T('\r') || *p == _T('\n')) ++add; TCHAR *buf = (TCHAR *)mir_alloc((lstrlen(str) + add + 1) * sizeof(TCHAR)); TCHAR *res = buf; for (p = str; p && *p; ++p) { if (*p == _T('\n') && !prev_r) *res++ = _T('\r'); if (*p != _T('\r') && *p != _T('\n') && prev_r) *res++ = _T('\n'); *res++ = *p; prev_r = *p == _T('\r'); prev_n = *p == _T('\n'); } *res = 0; return buf; } char* __stdcall JabberUnixToDos(const char* str) { char* p, *q, *res; int extra; if (str==NULL || str[0]=='\0') return NULL; extra = 0; for (p=(char*)str; *p!='\0'; p++) { if (*p == '\n') extra++; } if ((res=(char*)mir_alloc(strlen(str)+extra+1)) != NULL) { for (p=(char*)str,q=res; *p!='\0'; p++,q++) { if (*p == '\n') { *q = '\r'; q++; } *q = *p; } *q = '\0'; } return res; } WCHAR* __stdcall JabberUnixToDosW(const WCHAR* str) { if (str==NULL || str[0]=='\0') return NULL; const WCHAR* p; WCHAR* q, *res; int extra = 0; for (p = str; *p!='\0'; p++) { if (*p == '\n') extra++; } if ((res = (WCHAR*)mir_alloc(sizeof(WCHAR)*(wcslen(str) + extra + 1))) != NULL) { for (p = str,q=res; *p!='\0'; p++,q++) { if (*p == '\n') { *q = '\r'; q++; } *q = *p; } *q = '\0'; } return res; } TCHAR* __stdcall JabberHttpUrlEncode(const TCHAR *str) { TCHAR* p, *q, *res; if (str == NULL) return NULL; res = (TCHAR*) mir_alloc(3*_tcslen(str) + 1); for (p = (TCHAR*)str, q = res; *p!='\0'; p++,q++) { if ((*p>='A' && *p<='Z') || (*p>='a' && *p<='z') || (*p>='0' && *p<='9') || strchr("$-_.+!*'(),", *p)!=NULL) { *q = *p; } else { wsprintf(q, _T("%%%02X"), *p); q += 2; } } *q = '\0'; return res; } void __stdcall JabberHttpUrlDecode(TCHAR* str) { TCHAR* p, *q; unsigned int code; if (str == NULL) return; for (p = q = (TCHAR*)str; *p!='\0'; p++,q++) { if (*p=='%' && *(p+1)!='\0' && isxdigit(*(p+1)) && *(p+2)!='\0' && isxdigit(*(p+2))) { _stscanf((TCHAR*)p+1, _T("%2x"), &code); *q = (unsigned char) code; p += 2; } else { *q = *p; } } *q = '\0'; } int __stdcall JabberCombineStatus(int status1, int status2) { // Combine according to the following priority (high to low) // ID_STATUS_FREECHAT // ID_STATUS_ONLINE // ID_STATUS_DND // ID_STATUS_AWAY // ID_STATUS_NA // ID_STATUS_INVISIBLE (valid only for TLEN_PLUGIN) // ID_STATUS_OFFLINE // other ID_STATUS in random order (actually return status1) if (status1==ID_STATUS_FREECHAT || status2==ID_STATUS_FREECHAT) return ID_STATUS_FREECHAT; if (status1==ID_STATUS_ONLINE || status2==ID_STATUS_ONLINE) return ID_STATUS_ONLINE; if (status1==ID_STATUS_DND || status2==ID_STATUS_DND) return ID_STATUS_DND; if (status1==ID_STATUS_AWAY || status2==ID_STATUS_AWAY) return ID_STATUS_AWAY; if (status1==ID_STATUS_NA || status2==ID_STATUS_NA) return ID_STATUS_NA; if (status1==ID_STATUS_INVISIBLE || status2==ID_STATUS_INVISIBLE) return ID_STATUS_INVISIBLE; if (status1==ID_STATUS_OFFLINE || status2==ID_STATUS_OFFLINE) return ID_STATUS_OFFLINE; return status1; } struct tagErrorCodeToStr { int code; TCHAR* str; } static JabberErrorCodeToStrMapping[] = { { JABBER_ERROR_REDIRECT, LPGENT("Redirect") }, { JABBER_ERROR_BAD_REQUEST, LPGENT("Bad request") }, { JABBER_ERROR_UNAUTHORIZED, LPGENT("Unauthorized") }, { JABBER_ERROR_PAYMENT_REQUIRED, LPGENT("Payment required") }, { JABBER_ERROR_FORBIDDEN, LPGENT("Forbidden") }, { JABBER_ERROR_NOT_FOUND, LPGENT("Not found") }, { JABBER_ERROR_NOT_ALLOWED, LPGENT("Not allowed") }, { JABBER_ERROR_NOT_ACCEPTABLE, LPGENT("Not acceptable") }, { JABBER_ERROR_REGISTRATION_REQUIRED, LPGENT("Registration required") }, { JABBER_ERROR_REQUEST_TIMEOUT, LPGENT("Request timeout") }, { JABBER_ERROR_CONFLICT, LPGENT("Conflict") }, { JABBER_ERROR_INTERNAL_SERVER_ERROR, LPGENT("Internal server error") }, { JABBER_ERROR_NOT_IMPLEMENTED, LPGENT("Not implemented") }, { JABBER_ERROR_REMOTE_SERVER_ERROR, LPGENT("Remote server error") }, { JABBER_ERROR_SERVICE_UNAVAILABLE, LPGENT("Service unavailable") }, { JABBER_ERROR_REMOTE_SERVER_TIMEOUT, LPGENT("Remote server timeout") }, { -1, LPGENT("Unknown error") } }; TCHAR* __stdcall JabberErrorStr(int errorCode) { int i; for (i=0; JabberErrorCodeToStrMapping[i].code!=-1 && JabberErrorCodeToStrMapping[i].code!=errorCode; i++); return JabberErrorCodeToStrMapping[i].str; } TCHAR* __stdcall JabberErrorMsg(HXML errorNode, int* pErrorCode) { TCHAR* errorStr = (TCHAR*)mir_alloc(256 * sizeof(TCHAR)); if (errorNode == NULL) { if (pErrorCode) *pErrorCode = -1; mir_sntprintf(errorStr, 256, _T("%s -1: %s"), TranslateT("Error"), TranslateT("Unknown error message")); return errorStr; } int errorCode = -1; const TCHAR *str = xmlGetAttrValue(errorNode, _T("code")); if (str != NULL) errorCode = _ttoi(str); str = xmlGetText(errorNode); if (str == NULL) str = xmlGetText(xmlGetChild(errorNode, _T("text"))); if (str == NULL) { for (int i = 0; ; i++) { HXML c = xmlGetChild(errorNode, i); if (c == NULL) break; const TCHAR *attr = xmlGetAttrValue(c, _T("xmlns")); if (attr && !_tcscmp(attr, _T("urn:ietf:params:xml:ns:xmpp-stanzas"))) { str = xmlGetName(c); break; } } } if (str != NULL) mir_sntprintf(errorStr, 256, _T("%s %d: %s\r\n%s"), TranslateT("Error"), errorCode, TranslateTS(JabberErrorStr(errorCode)), str); else mir_sntprintf(errorStr, 256, _T("%s %d: %s"), TranslateT("Error"), errorCode, TranslateTS(JabberErrorStr(errorCode))); if (pErrorCode) *pErrorCode = errorCode; return errorStr; } void CJabberProto::SendVisibleInvisiblePresence(BOOL invisible) { if ( !m_bJabberOnline) return; LISTFOREACH(i, this, LIST_ROSTER) { JABBER_LIST_ITEM *item = ListGetItemPtrFromIndex(i); if (item == NULL) continue; HANDLE hContact = HContactFromJID(item->jid); if (hContact == NULL) continue; WORD apparentMode = getWord(hContact, "ApparentMode", 0); if (invisible==TRUE && apparentMode==ID_STATUS_OFFLINE) m_ThreadInfo->send(XmlNode(_T("presence")) << XATTR(_T("to"), item->jid) << XATTR(_T("type"), _T("invisible"))); else if (invisible==FALSE && apparentMode==ID_STATUS_ONLINE) SendPresenceTo(m_iStatus, item->jid, NULL); } } ///////////////////////////////////////////////////////////////////////////////////////// // JabberBase64Encode static char b64table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; char* __stdcall JabberBase64Encode(const char* buffer, int bufferLen) { if (buffer==NULL || bufferLen<=0) return NULL; char* res = (char*)mir_alloc((((bufferLen+2)*4)/3) + 1); if (res == NULL) return NULL; unsigned char igroup[3]; char *r = res; const char* peob = buffer + bufferLen; for (const char* p = buffer; p < peob;) { igroup[ 0 ] = igroup[ 1 ] = igroup[ 2 ] = 0; int n; for (n=0; n<3; n++) { if (p >= peob) break; igroup[n] = (unsigned char) *p; p++; } if (n > 0) { r[0] = b64table[ igroup[0]>>2 ]; r[1] = b64table[ ((igroup[0]&3)<<4) | (igroup[1]>>4) ]; r[2] = b64table[ ((igroup[1]&0xf)<<2) | (igroup[2]>>6) ]; r[3] = b64table[ igroup[2]&0x3f ]; if (n < 3) { r[3] = '='; if (n < 2) r[2] = '='; } r += 4; } } *r = '\0'; return res; } ///////////////////////////////////////////////////////////////////////////////////////// // JabberBase64Decode static unsigned char b64rtable[256]; char* __stdcall JabberBase64DecodeW(const WCHAR* str, int *resultLen) { char *stra = mir_u2a(str); char *res = JabberBase64Decode(stra, resultLen); mir_free(stra); return res; } char* __stdcall JabberBase64Decode(const char* str, int *resultLen) { char* res; unsigned char* r, igroup[4], a[4]; int n, num, count; if (str==NULL || resultLen==NULL) return NULL; if ((res=(char*)mir_alloc(((strlen(str)+3)/4)*3)) == NULL) return NULL; for (n=0; n<256; n++) b64rtable[n] = (unsigned char) 0x80; for (n=0; n<26; n++) b64rtable['A'+n] = n; for (n=0; n<26; n++) b64rtable['a'+n] = n + 26; for (n=0; n<10; n++) b64rtable['0'+n] = n + 52; b64rtable['+'] = 62; b64rtable['/'] = 63; b64rtable['='] = 0; count = 0; for (r=(unsigned char*)res; *str != '\0';) { for (n=0; n<4; n++) { if (BYTE(*str) == '\r' || BYTE(*str) == '\n') { n--; str++; continue; } if (BYTE(*str)=='\0') { if (n == 0) goto LBL_Exit; mir_free(res); return NULL; } if (b64rtable[BYTE(*str)]==0x80) { mir_free(res); return NULL; } a[n] = BYTE(*str); igroup[n] = b64rtable[BYTE(*str)]; str++; } r[0] = igroup[0]<<2 | igroup[1]>>4; r[1] = igroup[1]<<4 | igroup[2]>>2; r[2] = igroup[2]<<6 | igroup[3]; r += 3; num = (a[2]=='='?1:(a[3]=='='?2:3)); count += num; if (num < 3) break; } LBL_Exit: *resultLen = count; return res; } time_t __stdcall JabberIsoToUnixTime(const TCHAR *stamp) { struct tm timestamp; TCHAR date[9]; int i, y; time_t t; if (stamp == NULL) return (time_t) 0; const TCHAR *p = stamp; // Get the date part for (i=0; *p!='\0' && i<8 && isdigit(*p); p++,i++) date[i] = *p; // Parse year if (i == 6) { // 2-digit year (1970-2069) y = (date[0]-'0')*10 + (date[1]-'0'); if (y < 70) y += 100; } else if (i == 8) { // 4-digit year y = (date[0]-'0')*1000 + (date[1]-'0')*100 + (date[2]-'0')*10 + date[3]-'0'; y -= 1900; } else return (time_t) 0; timestamp.tm_year = y; // Parse month timestamp.tm_mon = (date[i-4]-'0')*10 + date[i-3]-'0' - 1; // Parse date timestamp.tm_mday = (date[i-2]-'0')*10 + date[i-1]-'0'; // Skip any date/time delimiter for (; *p!='\0' && !isdigit(*p); p++); // Parse time if (_stscanf(p, _T("%d:%d:%d"), ×tamp.tm_hour, ×tamp.tm_min, ×tamp.tm_sec) != 3) return (time_t) 0; timestamp.tm_isdst = 0; // DST is already present in _timezone below t = mktime(×tamp); _tzset(); t -= _timezone; if (t >= 0) return t; else return (time_t) 0; } void CJabberProto::SendPresenceTo(int status, TCHAR* to, HXML extra, const TCHAR *msg) { if ( !m_bJabberOnline) return; // Send update for status (we won't handle ID_STATUS_OFFLINE here) short iPriority = (short)getWord("Priority", 0); UpdatePriorityMenu(iPriority); TCHAR szPriority[40]; _itot(iPriority, szPriority, 10); XmlNode p(_T("presence")); p << XCHILD(_T("priority"), szPriority); if (to != NULL) p << XATTR(_T("to"), to); if (extra) xmlAddChild(p, extra); // XEP-0115:Entity Capabilities HXML c = p << XCHILDNS(_T("c"), _T(JABBER_FEAT_ENTITY_CAPS)) << XATTR(_T("node"), _T(JABBER_CAPS_MIRANDA_NODE)) << XATTR(_T("ver"), szCoreVersion); TCHAR szExtCaps[ 512 ] = _T(""); if (m_bGoogleTalk) _tcscat(szExtCaps, _T(JABBER_EXT_GTALK_PMUC)); if (bSecureIM) { if (szExtCaps[0]) _tcscat(szExtCaps, _T(" ")); _tcscat(szExtCaps, _T(JABBER_EXT_SECUREIM)); } if (bMirOTR) { if (szExtCaps[0]) _tcscat(szExtCaps, _T(" ")); _tcscat(szExtCaps, _T(JABBER_EXT_MIROTR)); } if (bNewGPG) { if (szExtCaps[0]) _tcscat(szExtCaps, _T(" ")); _tcscat(szExtCaps, _T(JABBER_EXT_NEWGPG)); } if (bPlatform) { if (szExtCaps[0]) _tcscat(szExtCaps, _T(" ")); _tcscat(szExtCaps, _T(JABBER_EXT_PLATFORMX64)); } else { if (szExtCaps[0]) _tcscat(szExtCaps, _T(" ")); _tcscat(szExtCaps, _T(JABBER_EXT_PLATFORMX86)); } if (m_options.EnableRemoteControl) { if (szExtCaps[0]) _tcscat(szExtCaps, _T(" ")); _tcscat(szExtCaps, _T(JABBER_EXT_COMMANDS)); } if (m_options.EnableUserMood) { if (szExtCaps[0]) _tcscat(szExtCaps, _T(" ")); _tcscat(szExtCaps, _T(JABBER_EXT_USER_MOOD)); } if (m_options.EnableUserTune) { if (szExtCaps[0]) _tcscat(szExtCaps, _T(" ")); _tcscat(szExtCaps, _T(JABBER_EXT_USER_TUNE)); } if (m_options.EnableUserActivity) { if (szExtCaps[0]) _tcscat(szExtCaps, _T(" ")); _tcscat(szExtCaps, _T(JABBER_EXT_USER_ACTIVITY)); } if (m_options.AcceptNotes) { if (szExtCaps[0]) _tcscat(szExtCaps, _T(" ")); _tcscat(szExtCaps, _T(JABBER_EXT_MIR_NOTES)); } // add features enabled through IJabberNetInterface::AddFeatures() for (int i = 0; i < m_lstJabberFeatCapPairsDynamic.getCount(); i++) { if (m_uEnabledFeatCapsDynamic & m_lstJabberFeatCapPairsDynamic[i]->jcbCap) { if (szExtCaps[0]) _tcscat(szExtCaps, _T(" ")); _tcscat(szExtCaps, m_lstJabberFeatCapPairsDynamic[i]->szExt); } } if (szExtCaps[0]) xmlAddAttr(c, _T("ext"), szExtCaps); if (m_options.EnableAvatars) { HXML x = p << XCHILDNS(_T("x"), _T("vcard-temp:x:update")); ptrA hashValue( getStringA("AvatarHash")); if (hashValue != NULL) // XEP-0153: vCard-Based Avatars x << XCHILD(_T("photo"), _A2T(hashValue)); else x << XCHILD(_T("photo")); } EnterCriticalSection(&m_csModeMsgMutex); switch (status) { case ID_STATUS_ONLINE: if ( !msg) msg = m_modeMsgs.szOnline; break; case ID_STATUS_INVISIBLE: if ( !m_bGoogleSharedStatus) p << XATTR(_T("type"), _T("invisible")); break; case ID_STATUS_AWAY: case ID_STATUS_ONTHEPHONE: case ID_STATUS_OUTTOLUNCH: p << XCHILD(_T("show"), _T("away")); if ( !msg) msg = m_modeMsgs.szAway; break; case ID_STATUS_NA: p << XCHILD(_T("show"), _T("xa")); if ( !msg) msg = m_modeMsgs.szNa; break; case ID_STATUS_DND: case ID_STATUS_OCCUPIED: p << XCHILD(_T("show"), _T("dnd")); if ( !msg) msg = m_modeMsgs.szDnd; break; case ID_STATUS_FREECHAT: p << XCHILD(_T("show"), _T("chat")); if ( !msg) msg = m_modeMsgs.szFreechat; break; default: // Should not reach here break; } if (msg) p << XCHILD(_T("status"), msg); if (m_bGoogleSharedStatus && !to) SendIqGoogleSharedStatus(status, msg); LeaveCriticalSection(&m_csModeMsgMutex); m_ThreadInfo->send(p); } void CJabberProto::SendPresence(int status, bool bSendToAll) { SendPresenceTo(status, NULL, NULL); SendVisibleInvisiblePresence(status == ID_STATUS_INVISIBLE); // Also update status in all chatrooms if (bSendToAll) { LISTFOREACH(i, this, LIST_CHATROOM) { JABBER_LIST_ITEM *item = ListGetItemPtrFromIndex(i); if (item != NULL) { TCHAR text[ 1024 ]; mir_sntprintf(text, SIZEOF(text), _T("%s/%s"), item->jid, item->nick); SendPresenceTo(status == ID_STATUS_INVISIBLE ? ID_STATUS_ONLINE : status, text, NULL); } } } } ///////////////////////////////////////////////////////////////////////////////////////// // Google Shared Status void CJabberProto::OnIqResultGoogleSharedStatus(HXML iqNode, CJabberIqInfo* pInfo) { m_bGoogleSharedStatus = (JABBER_IQ_TYPE_RESULT == pInfo->GetIqType()); m_bGoogleSharedStatusLock = FALSE; } BOOL CJabberProto::OnIqSetGoogleSharedStatus(HXML iqNode, CJabberIqInfo* pInfo) { if (JABBER_IQ_TYPE_SET != pInfo->GetIqType()) return FALSE; if (m_bGoogleSharedStatusLock) return TRUE; int status; HXML query = xmlGetChild(iqNode, _T("query")); HXML node = xmlGetChild(query, _T("invisible")); if (0 == _tcsicmp(_T("true"), xmlGetAttrValue(node, _T("value")))) status = ID_STATUS_INVISIBLE; else { LPCTSTR txt = xmlGetText(xmlGetChild(query, _T("show"))); if (txt && 0 == _tcsicmp(_T("dnd"), txt)) status = ID_STATUS_DND; else if (m_iStatus == ID_STATUS_DND || m_iStatus == ID_STATUS_INVISIBLE) status = ID_STATUS_ONLINE; else status = m_iStatus; } if (status != m_iStatus) SetStatus(status); return TRUE; } void CJabberProto::SendIqGoogleSharedStatus(int status, const TCHAR *msg) { XmlNodeIq iq(m_iqManager.AddHandler(&CJabberProto::OnIqResultGoogleSharedStatus, JABBER_IQ_TYPE_SET)); HXML query = iq << XQUERY(_T(JABBER_FEAT_GTALK_SHARED_STATUS)) << XATTR(_T("version"), _T("2")); query << XCHILD(_T("status"), msg); if (status == ID_STATUS_INVISIBLE) { query << XCHILD(_T("show"), _T("default")); query << XCHILD(_T("invisible")) << XATTR(_T("value"), _T("true")); } else { if (status == ID_STATUS_DND) query << XCHILD(_T("show"), _T("dnd")); else query << XCHILD(_T("show"), _T("default")); query << XCHILD(_T("invisible")) << XATTR(_T("value"), _T("false")); } m_bGoogleSharedStatusLock = TRUE; m_ThreadInfo->send(iq); } void __stdcall JabberStringAppend(char* *str, int *sizeAlloced, const char* fmt, ...) { va_list vararg; char* p; size_t size, len; if (str == NULL) return; if (*str==NULL || *sizeAlloced<=0) { *sizeAlloced = 2048; size = 2048; *str = (char*)mir_alloc(size); len = 0; } else { len = strlen(*str); size = *sizeAlloced - strlen(*str); } p = *str + len; va_start(vararg, fmt); while (_vsnprintf(p, size, fmt, vararg) == -1) { size += 2048; (*sizeAlloced) += 2048; *str = (char*)mir_realloc(*str, *sizeAlloced); p = *str + len; } va_end(vararg); } /////////////////////////////////////////////////////////////////////////////// // JabberGetPacketID - converts the xml id attribute into an integer int __stdcall JabberGetPacketID(HXML n) { int result = -1; const TCHAR *str = xmlGetAttrValue(n, _T("id")); if (str) if ( !_tcsncmp(str, _T(JABBER_IQID), SIZEOF(JABBER_IQID)-1)) result = _ttoi(str + SIZEOF(JABBER_IQID)-1); return result; } /////////////////////////////////////////////////////////////////////////////// // JabberGetClientJID - adds a resource postfix to a JID TCHAR* CJabberProto::GetClientJID(const TCHAR *jid, TCHAR* dest, size_t destLen) { if (jid == NULL) return NULL; size_t len = _tcslen(jid); if (len >= destLen) len = destLen-1; _tcsncpy(dest, jid, len); dest[ len ] = '\0'; TCHAR* p = _tcschr(dest, '/'); JABBER_LIST_ITEM* LI = ListGetItemPtr(LIST_ROSTER, jid); if (LI && LI->resourceCount == 1 && LI->resource[ 0 ].szCapsNode && _tcsicmp(LI->resource[ 0 ].szCapsNode, _T("http://talk.google.com/xmpp/bot/caps")) == 0) { if (p) *p = 0; return dest; } if (p == NULL) { TCHAR* resource = ListGetBestResourceNamePtr(jid); if (resource != NULL) mir_sntprintf(dest+len, destLen-len-1, _T("/%s"), resource); } return dest; } /////////////////////////////////////////////////////////////////////////////// // JabberStripJid - strips a resource postfix from a JID TCHAR* __stdcall JabberStripJid(const TCHAR *jid, TCHAR* dest, size_t destLen) { if (jid == NULL) *dest = 0; else { size_t len = _tcslen(jid); if (len >= destLen) len = destLen-1; memcpy(dest, jid, len * sizeof(TCHAR)); dest[ len ] = 0; TCHAR* p = _tcschr(dest, '/'); if (p != NULL) *p = 0; } return dest; } ///////////////////////////////////////////////////////////////////////////////////////// // JabberGetPictureType - tries to autodetect the picture type from the buffer int __stdcall JabberGetPictureType(const char* buf) { if (buf != NULL) { if (memcmp(buf, "GIF8", 4) == 0) return PA_FORMAT_GIF; if (memcmp(buf, "\x89PNG", 4) == 0) return PA_FORMAT_PNG; if (memcmp(buf, "BM", 2) == 0) return PA_FORMAT_BMP; if (memcmp(buf, "\xFF\xD8", 2) == 0) return PA_FORMAT_JPEG; } return PA_FORMAT_UNKNOWN; } ///////////////////////////////////////////////////////////////////////////////////////// // TStringPairs class members TStringPairs::TStringPairs(char* buffer) : elems(NULL) { TStringPairsElem tempElem[ 100 ]; char* token = strtok(buffer, ","); for (numElems=0; token != NULL; numElems++) { char* p = strchr(token, '='), *p1; if (p == NULL) break; while(isspace(*token)) token++; tempElem[ numElems ].name = rtrim(token); *p++ = 0; if ((p1 = strchr(p, '\"')) != NULL) { *p1 = 0; p = p1+1; } if ((p1 = strrchr(p, '\"')) != NULL) *p1 = 0; tempElem[ numElems ].value = rtrim(p); token = strtok(NULL, ","); } if (numElems) { elems = new TStringPairsElem[ numElems ]; memcpy(elems, tempElem, sizeof(tempElem[0]) * numElems); } } TStringPairs::~TStringPairs() { delete[] elems; } const char* TStringPairs::operator[](const char* key) const { for (int i = 0; i < numElems; i++) if ( !strcmp(elems[i].name, key)) return elems[i].value; return ""; } //////////////////////////////////////////////////////////////////////// // Manage combo boxes with recent item list void CJabberProto::ComboLoadRecentStrings(HWND hwndDlg, UINT idcCombo, char *param, int recentCount) { for (int i = 0; i < recentCount; i++) { DBVARIANT dbv; char setting[MAXMODULELABELLENGTH]; mir_snprintf(setting, sizeof(setting), "%s%d", param, i); if ( !getTString(setting, &dbv)) { SendDlgItemMessage(hwndDlg, idcCombo, CB_ADDSTRING, 0, (LPARAM)dbv.ptszVal); db_free(&dbv); } } if ( !SendDlgItemMessage(hwndDlg, idcCombo, CB_GETCOUNT, 0, 0)) SendDlgItemMessage(hwndDlg, idcCombo, CB_ADDSTRING, 0, (LPARAM)_T("")); } void CJabberProto::ComboAddRecentString(HWND hwndDlg, UINT idcCombo, char *param, TCHAR *string, int recentCount) { if ( !string || !*string) return; if (SendDlgItemMessage(hwndDlg, idcCombo, CB_FINDSTRING, (WPARAM)-1, (LPARAM)string) != CB_ERR) return; int id; SendDlgItemMessage(hwndDlg, idcCombo, CB_ADDSTRING, 0, (LPARAM)string); if ((id = SendDlgItemMessage(hwndDlg, idcCombo, CB_FINDSTRING, (WPARAM)-1, (LPARAM)_T(""))) != CB_ERR) SendDlgItemMessage(hwndDlg, idcCombo, CB_DELETESTRING, id, 0); id = getByte(param, 0); char setting[MAXMODULELABELLENGTH]; mir_snprintf(setting, sizeof(setting), "%s%d", param, id); setTString(setting, string); setByte(param, (id+1)%recentCount); } ///////////////////////////////////////////////////////////////////////////////////////// // jabber frame maintenance code static VOID CALLBACK sttRebuildInfoFrameApcProc(void* param) { CJabberProto *ppro = (CJabberProto *)param; if ( !ppro->m_pInfoFrame) return; ppro->m_pInfoFrame->LockUpdates(); if ( !ppro->m_bJabberOnline) { ppro->m_pInfoFrame->RemoveInfoItem("$/PEP"); ppro->m_pInfoFrame->RemoveInfoItem("$/Transports"); ppro->m_pInfoFrame->UpdateInfoItem("$/JID", LoadSkinnedIconHandle(SKINICON_OTHER_USERDETAILS), TranslateT("Offline")); } else { ppro->m_pInfoFrame->UpdateInfoItem("$/JID", LoadSkinnedIconHandle(SKINICON_OTHER_USERDETAILS), ppro->m_szJabberJID); if ( !ppro->m_bPepSupported) ppro->m_pInfoFrame->RemoveInfoItem("$/PEP"); else { ppro->m_pInfoFrame->RemoveInfoItem("$/PEP/"); ppro->m_pInfoFrame->CreateInfoItem("$/PEP", false); ppro->m_pInfoFrame->UpdateInfoItem("$/PEP", ppro->GetIconHandle(IDI_PL_LIST_ANY), TranslateT("Advanced Status")); ppro->m_pInfoFrame->CreateInfoItem("$/PEP/mood", true); ppro->m_pInfoFrame->SetInfoItemCallback("$/PEP/mood", &CJabberProto::InfoFrame_OnUserMood); ppro->m_pInfoFrame->UpdateInfoItem("$/PEP/mood", LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT), TranslateT("Set mood...")); ppro->m_pInfoFrame->CreateInfoItem("$/PEP/activity", true); ppro->m_pInfoFrame->SetInfoItemCallback("$/PEP/activity", &CJabberProto::InfoFrame_OnUserActivity); ppro->m_pInfoFrame->UpdateInfoItem("$/PEP/activity", LoadSkinnedIconHandle(SKINICON_OTHER_SMALLDOT), TranslateT("Set activity...")); } ppro->m_pInfoFrame->RemoveInfoItem("$/Transports/"); ppro->m_pInfoFrame->CreateInfoItem("$/Transports", false); ppro->m_pInfoFrame->UpdateInfoItem("$/Transports", ppro->GetIconHandle(IDI_TRANSPORT), TranslateT("Transports")); JABBER_LIST_ITEM *item = NULL; LISTFOREACH(i, ppro, LIST_ROSTER) { if ((item=ppro->ListGetItemPtrFromIndex(i)) != NULL) { if (_tcschr(item->jid, '@')==NULL && _tcschr(item->jid, '/')==NULL && item->subscription!=SUB_NONE) { HANDLE hContact = ppro->HContactFromJID(item->jid); if (hContact == NULL) continue; char name[128]; char *jid_copy = mir_t2a(item->jid); mir_snprintf(name, SIZEOF(name), "$/Transports/%s", jid_copy); ppro->m_pInfoFrame->CreateInfoItem(name, true, (LPARAM)hContact); ppro->m_pInfoFrame->UpdateInfoItem(name, ppro->GetIconHandle(IDI_TRANSPORTL), (TCHAR *)item->jid); ppro->m_pInfoFrame->SetInfoItemCallback(name, &CJabberProto::InfoFrame_OnTransport); mir_free(jid_copy); } } } } ppro->m_pInfoFrame->Update(); } void CJabberProto::RebuildInfoFrame() { CallFunctionAsync(sttRebuildInfoFrameApcProc, this); } //////////////////////////////////////////////////////////////////////// // time2str & str2time TCHAR* time2str(time_t _time, TCHAR *buf, size_t bufLen) { struct tm* T = localtime(&_time); mir_sntprintf(buf, bufLen, _T("%04d-%02d-%02dT%02d:%02d:%02dZ"), T->tm_year+1900, T->tm_mon+1, T->tm_mday, T->tm_hour, T->tm_min, T->tm_sec); return buf; } time_t str2time(const TCHAR *buf) { struct tm T = { 0 }; if ( _stscanf(buf, _T("%04d-%02d-%02dT%02d:%02d:%02dZ"), &T.tm_year, &T.tm_mon, &T.tm_mday, &T.tm_hour, &T.tm_min, &T.tm_sec) != 6) { int boo; if ( _stscanf(buf, _T("%04d-%02d-%02dT%02d:%02d:%02d.%dZ"), &T.tm_year, &T.tm_mon, &T.tm_mday, &T.tm_hour, &T.tm_min, &T.tm_sec, &boo) != 7) return 0; } T.tm_year -= 1900; T.tm_mon--; return mktime(&T); } //////////////////////////////////////////////////////////////////////// // case-insensitive _tcsstr const TCHAR *JabberStrIStr(const TCHAR *str, const TCHAR *substr) { TCHAR *str_up = NEWTSTR_ALLOCA(str); TCHAR *substr_up = NEWTSTR_ALLOCA(substr); CharUpperBuff(str_up, lstrlen(str_up)); CharUpperBuff(substr_up, lstrlen(substr_up)); TCHAR* p = _tcsstr(str_up, substr_up); return p ? (str + (p - str_up)) : NULL; } //////////////////////////////////////////////////////////////////////// // clipboard processing void JabberCopyText(HWND hwnd, TCHAR *text) { if ( !hwnd || !text) return; OpenClipboard(hwnd); EmptyClipboard(); HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, sizeof(TCHAR)*(lstrlen(text)+1)); TCHAR *s = (TCHAR *)GlobalLock(hMem); lstrcpy(s, text); GlobalUnlock(hMem); SetClipboardData(CF_UNICODETEXT, hMem); CloseClipboard(); } ///////////////////////////////////////////////////////////////////////////////////////// // One string entry dialog struct JabberEnterStringParams { CJabberProto* ppro; int type; TCHAR* caption; TCHAR* result; size_t resultLen; char *windowName; int recentCount; int timeout; int idcControl; int height; }; static int sttEnterStringResizer(HWND, LPARAM, UTILRESIZECONTROL *urc) { switch (urc->wId) { case IDC_TXT_MULTILINE: case IDC_TXT_COMBO: case IDC_TXT_RICHEDIT: return RD_ANCHORX_LEFT|RD_ANCHORY_TOP|RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT; case IDOK: case IDCANCEL: return RD_ANCHORX_RIGHT|RD_ANCHORY_BOTTOM; } return RD_ANCHORX_LEFT|RD_ANCHORY_TOP; } static INT_PTR CALLBACK sttEnterStringDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { JabberEnterStringParams *params = (JabberEnterStringParams *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); switch (msg) { case WM_INITDIALOG: { //SetWindowPos(hwndDlg, HWND_TOPMOST ,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE); TranslateDialogDefault(hwndDlg); SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadSkinnedIconBig(SKINICON_OTHER_RENAME)); SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadSkinnedIcon(SKINICON_OTHER_RENAME)); JabberEnterStringParams *params = (JabberEnterStringParams *)lParam; SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)params); SetWindowText(hwndDlg, params->caption); RECT rc; GetWindowRect(hwndDlg, &rc); switch (params->type) { case JES_PASSWORD: { params->idcControl = IDC_TXT_PASSWORD; params->height = rc.bottom-rc.top; break; } case JES_MULTINE: { params->idcControl = IDC_TXT_MULTILINE; params->height = 0; rc.bottom += (rc.bottom-rc.top) * 2; SetWindowPos(hwndDlg, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top, SWP_NOMOVE|SWP_NOREPOSITION); break; } case JES_COMBO: { params->idcControl = IDC_TXT_COMBO; params->height = rc.bottom-rc.top; if (params->windowName && params->recentCount) params->ppro->ComboLoadRecentStrings(hwndDlg, IDC_TXT_COMBO, params->windowName, params->recentCount); break; } case JES_RICHEDIT: { params->idcControl = IDC_TXT_RICHEDIT; SendDlgItemMessage(hwndDlg, IDC_TXT_RICHEDIT, EM_AUTOURLDETECT, TRUE, 0); SendDlgItemMessage(hwndDlg, IDC_TXT_RICHEDIT, EM_SETEVENTMASK, 0, ENM_LINK); params->height = 0; rc.bottom += (rc.bottom-rc.top) * 2; SetWindowPos(hwndDlg, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top, SWP_NOMOVE|SWP_NOREPOSITION); break; } } ShowWindow(GetDlgItem(hwndDlg, params->idcControl), SW_SHOW); SetDlgItemText(hwndDlg, params->idcControl, params->result); if (params->windowName) Utils_RestoreWindowPosition(hwndDlg, NULL, params->ppro->m_szModuleName, params->windowName); SetTimer(hwndDlg, 1000, 50, NULL); if (params->timeout > 0) { SetTimer(hwndDlg, 1001, 1000, NULL); TCHAR buf[128]; mir_sntprintf(buf, SIZEOF(buf), _T("%s (%d)"), TranslateT("OK"), params->timeout); SetDlgItemText(hwndDlg, IDOK, buf); } return TRUE; } case WM_DESTROY: WindowFreeIcon(hwndDlg); break; case WM_TIMER: { switch (wParam) { case 1000: KillTimer(hwndDlg,1000); EnableWindow(GetParent(hwndDlg), TRUE); return TRUE; case 1001: { TCHAR buf[128]; mir_sntprintf(buf, SIZEOF(buf), _T("%s (%d)"), TranslateT("OK"), --params->timeout); SetDlgItemText(hwndDlg, IDOK, buf); if (params->timeout < 0) { KillTimer(hwndDlg, 1001); UIEmulateBtnClick(hwndDlg, IDOK); } return TRUE; } } } case WM_SIZE: { UTILRESIZEDIALOG urd = {0}; urd.cbSize = sizeof(urd); urd.hInstance = hInst; urd.hwndDlg = hwndDlg; urd.lpTemplate = MAKEINTRESOURCEA(IDD_GROUPCHAT_INPUT); urd.pfnResizer = sttEnterStringResizer; CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd); break; } case WM_GETMINMAXINFO: { LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam; if (params && params->height) lpmmi->ptMaxSize.y = lpmmi->ptMaxTrackSize.y = params->height; break; } case WM_NOTIFY: { ENLINK *param = (ENLINK *)lParam; if (param->nmhdr.idFrom != IDC_TXT_RICHEDIT) break; if (param->nmhdr.code != EN_LINK) break; if (param->msg != WM_LBUTTONUP) break; CHARRANGE sel; SendMessage(param->nmhdr.hwndFrom, EM_EXGETSEL, 0, (LPARAM) & sel); if (sel.cpMin != sel.cpMax) break; // allow link selection TEXTRANGE tr; tr.chrg = param->chrg; tr.lpstrText = (TCHAR *)mir_alloc(sizeof(TCHAR)*(tr.chrg.cpMax - tr.chrg.cpMin + 2)); SendMessage(param->nmhdr.hwndFrom, EM_GETTEXTRANGE, 0, (LPARAM) & tr); char *tmp = mir_t2a(tr.lpstrText); CallService(MS_UTILS_OPENURL, 1, (LPARAM)tmp); mir_free(tmp); mir_free(tr.lpstrText); return TRUE; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: GetDlgItemText(hwndDlg, params->idcControl, params->result, (int)params->resultLen); params->result[ params->resultLen-1 ] = 0; if ((params->type == JES_COMBO) && params->windowName && params->recentCount) params->ppro->ComboAddRecentString(hwndDlg, IDC_TXT_COMBO, params->windowName, params->result, params->recentCount); if (params->windowName) Utils_SaveWindowPosition(hwndDlg, NULL, params->ppro->m_szModuleName, params->windowName); EndDialog(hwndDlg, 1); break; case IDCANCEL: if (params->windowName) Utils_SaveWindowPosition(hwndDlg, NULL, params->ppro->m_szModuleName, params->windowName); EndDialog(hwndDlg, 0); break; case IDC_TXT_MULTILINE: case IDC_TXT_RICHEDIT: if ((HIWORD(wParam) != EN_SETFOCUS) && (HIWORD(wParam) != EN_KILLFOCUS)) { SetDlgItemText(hwndDlg, IDOK, TranslateT("OK")); KillTimer(hwndDlg, 1001); } break; case IDC_TXT_COMBO: if ((HIWORD(wParam) != CBN_SETFOCUS) && (HIWORD(wParam) != CBN_KILLFOCUS)) { SetDlgItemText(hwndDlg, IDOK, TranslateT("OK")); KillTimer(hwndDlg, 1001); } break; } } return FALSE; } BOOL CJabberProto::EnterString(TCHAR *result, size_t resultLen, TCHAR *caption, int type, char *windowName, int recentCount, int timeout) { bool free_caption = false; if ( !caption || (caption==result)) { free_caption = true; caption = mir_tstrdup(result); result[ 0 ] = _T('\0'); } JabberEnterStringParams params = { this, type, caption, result, resultLen, windowName, recentCount, timeout }; BOOL bRetVal = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_GROUPCHAT_INPUT), GetForegroundWindow(), sttEnterStringDlgProc, LPARAM(¶ms)); if (free_caption) mir_free(caption); return bRetVal; } // XEP-0203 delay support bool JabberReadXep203delay(HXML node, time_t &msgTime) { HXML n = xmlGetChildByTag(node, "delay", "xmlns", _T("urn:xmpp:delay")); if (n == NULL) return false; const TCHAR *ptszTimeStamp = xmlGetAttrValue(n, _T("stamp")); if (ptszTimeStamp == NULL) return false; // skip '-' chars TCHAR* szStamp = NEWTSTR_ALLOCA(ptszTimeStamp); int si = 0, sj = 0; while (1) { if (szStamp[si] == _T('-')) si++; else if ( !(szStamp[sj++] = szStamp[si++])) break; }; msgTime = JabberIsoToUnixTime(szStamp); return msgTime != 0; } //////////////////////////////////////////////////////////////////////// // Premultiply bitmap channels for 32-bit bitmaps void JabberBitmapPremultiplyChannels(HBITMAP hBitmap) { BITMAP bmp; DWORD dwLen; BYTE *p; int x, y; GetObject(hBitmap, sizeof(bmp), &bmp); if (bmp.bmBitsPixel != 32) return; dwLen = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8); p = (BYTE *)malloc(dwLen); if (p == NULL) return; memset(p, 0, dwLen); GetBitmapBits(hBitmap, dwLen, p); for (y = 0; y < bmp.bmHeight; ++y) { BYTE *px = p + bmp.bmWidth * 4 * y; for (x = 0; x < bmp.bmWidth; ++x) { px[0] = px[0] * px[3] / 255; px[1] = px[1] * px[3] / 255; px[2] = px[2] * px[3] / 255; px += 4; } } SetBitmapBits(hBitmap, dwLen, p); free(p); } // Last resource map void CJabberProto::CleanLastResourceMap() { EnterCriticalSection(&m_csLastResourceMap); m_dwResourceMapPointer = 0; ZeroMemory(m_ulpResourceToDbEventMap, sizeof(m_ulpResourceToDbEventMap)); while (m_pLastResourceList) { void *pNext = ((void **)m_pLastResourceList)[ 0 ]; mir_free(m_pLastResourceList); m_pLastResourceList = pNext; } LeaveCriticalSection(&m_csLastResourceMap); } // lock CS before use BOOL CJabberProto::IsLastResourceExists(void *pResource) { if ( !pResource) return FALSE; void *pOurResource = m_pLastResourceList; while (pOurResource) { if (pOurResource == pResource) return TRUE; pOurResource = ((void **)pOurResource)[ 0 ]; } return FALSE; } // lock CS before use void* CJabberProto::AddToLastResourceMap(LPCTSTR szFullJid) { // detach resource from full jid const TCHAR *szResource = _tcschr(szFullJid, '/'); if (szResource == NULL) return NULL; if (*++szResource == '\0') return NULL; DWORD dwResourceCount = 0; void *pNewTailResource = NULL; void *pOurResource = m_pLastResourceList; while (pOurResource) { dwResourceCount++; if ( !_tcscmp((TCHAR *)((BYTE *)pOurResource + sizeof(void *)), szResource)) return pOurResource; void *pTmp = ((void **)pOurResource)[ 0 ]; if (pTmp && !(((void **)pTmp)[ 0 ])) pNewTailResource = pOurResource; pOurResource = pTmp; } if (pNewTailResource && (dwResourceCount > (SIZEOF(m_ulpResourceToDbEventMap) / 2))) { void *pTmp = ((void **)pNewTailResource)[ 0 ]; ((void **)pNewTailResource)[ 0 ] = NULL; mir_free(pTmp); } void *pNewResource = mir_alloc(sizeof(void *) + sizeof(TCHAR) * (_tcslen(szResource) + 1)); if ( !pNewResource) return NULL; ((void **)pNewResource)[ 0 ] = m_pLastResourceList; _tcscpy((TCHAR *)((BYTE *)pNewResource + sizeof(void *)), szResource); m_pLastResourceList = pNewResource; return pNewResource; } // lock CS before use TCHAR* CJabberProto::FindLastResourceByDbEvent(HANDLE hDbEvent) { for (int i = 0; i < SIZEOF(m_ulpResourceToDbEventMap); i += 2) { if (m_ulpResourceToDbEventMap[ i ] == (ULONG_PTR)hDbEvent) { TCHAR *szRetVal = (TCHAR *)(m_ulpResourceToDbEventMap[ i + 1 ] + sizeof(void *)); m_ulpResourceToDbEventMap[ i ] = 0; m_ulpResourceToDbEventMap[ i + 1 ] = 0; return szRetVal; } } return NULL; } BOOL CJabberProto::IsMyOwnJID(LPCTSTR szJID) { if ( !m_ThreadInfo) return FALSE; TCHAR* szFrom = JabberPrepareJid(szJID); if ( !szFrom) return FALSE; TCHAR* szTo = JabberPrepareJid(m_ThreadInfo->fullJID); if ( !szTo) { mir_free(szFrom); return FALSE; } TCHAR* pDelimiter = _tcschr(szFrom, _T('/')); if (pDelimiter) *pDelimiter = _T('\0'); pDelimiter = _tcschr(szTo, _T('/')); if (pDelimiter) *pDelimiter = _T('\0'); BOOL bRetVal = _tcscmp(szFrom, szTo) == 0; mir_free(szFrom); mir_free(szTo); return bRetVal; } void __cdecl CJabberProto::LoadHttpAvatars(void* param) { OBJLIST &avs = *(OBJLIST*)param; HANDLE hHttpCon = NULL; for (int i = 0; i < avs.getCount(); i++) { NETLIBHTTPREQUEST nlhr = {0}; nlhr.cbSize = sizeof(nlhr); nlhr.requestType = REQUEST_GET; nlhr.flags = NLHRF_HTTP11 | NLHRF_REDIRECT | NLHRF_PERSISTENT; nlhr.szUrl = avs[i].Url; nlhr.nlc = hHttpCon; NETLIBHTTPREQUEST * res = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)m_hNetlibUser, (LPARAM)&nlhr); if (res) { hHttpCon = res->nlc; if (res->resultCode == 200 && res->dataLength) { int pictureType = JabberGetPictureType(res->pData); if (pictureType != PA_FORMAT_UNKNOWN) { PROTO_AVATAR_INFORMATIONT AI; AI.cbSize = sizeof(AI); AI.format = pictureType; AI.hContact = avs[i].hContact; if (getByte(AI.hContact, "AvatarType", PA_FORMAT_UNKNOWN) != (unsigned char)pictureType) { TCHAR tszFileName[ MAX_PATH ]; GetAvatarFileName(AI.hContact, tszFileName, SIZEOF(tszFileName)); DeleteFile(tszFileName); } setByte(AI.hContact, "AvatarType", pictureType); char buffer[ 41 ]; mir_sha1_byte_t digest[20]; mir_sha1_ctx sha; mir_sha1_init(&sha); mir_sha1_append(&sha, (mir_sha1_byte_t*)res->pData, res->dataLength); mir_sha1_finish(&sha, digest); for (int i=0; i<20; i++) sprintf(buffer+(i<<1), "%02x", digest[i]); ptrA cmpsha( getStringA(AI.hContact, "AvatarSaved")); if (cmpsha == NULL || strnicmp(cmpsha, buffer, sizeof(buffer))) { TCHAR tszFileName[ MAX_PATH ]; GetAvatarFileName(AI.hContact, tszFileName, SIZEOF(tszFileName)); _tcsncpy(AI.filename, tszFileName, SIZEOF(AI.filename)); FILE* out = _tfopen(tszFileName, _T("wb")); if (out != NULL) { fwrite(res->pData, res->dataLength, 1, out); fclose(out); setString(AI.hContact, "AvatarSaved", buffer); ProtoBroadcastAck(AI.hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, &AI, 0); Log("Broadcast new avatar: %s",AI.filename); } else ProtoBroadcastAck(AI.hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, &AI, 0); } } } CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)res); } else hHttpCon = NULL; } delete &avs; if (hHttpCon) Netlib_CloseHandle(hHttpCon); }