From 4649bcfc2b1cbbe2f004d7bec963a7528866c072 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Sun, 15 Jul 2012 16:44:52 +0000 Subject: =?UTF-8?q?z=20ca=C5=82ym=20szacunkiem=20dla=20naszych=20polskich?= =?UTF-8?q?=20u=C5=BCytkownik=C3=B3w?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: http://svn.miranda-ng.org/main/trunk@977 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj | 48 +- protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters | 114 +- protocols/Gadu-Gadu/avatar.c | 461 ------ protocols/Gadu-Gadu/avatar.cpp | 458 ++++++ protocols/Gadu-Gadu/core.c | 1841 ---------------------- protocols/Gadu-Gadu/core.cpp | 1765 +++++++++++++++++++++ protocols/Gadu-Gadu/dialogs.c | 1042 ------------ protocols/Gadu-Gadu/dialogs.cpp | 1016 ++++++++++++ protocols/Gadu-Gadu/dynstuff.c | 613 ------- protocols/Gadu-Gadu/dynstuff.cpp | 612 +++++++ protocols/Gadu-Gadu/filetransfer.c | 982 ------------ protocols/Gadu-Gadu/filetransfer.cpp | 987 ++++++++++++ protocols/Gadu-Gadu/gg.c | 749 --------- protocols/Gadu-Gadu/gg.cpp | 527 +++++++ protocols/Gadu-Gadu/gg.h | 194 +-- protocols/Gadu-Gadu/groupchat.c | 681 -------- protocols/Gadu-Gadu/groupchat.cpp | 669 ++++++++ protocols/Gadu-Gadu/icolib.c | 109 -- protocols/Gadu-Gadu/icolib.cpp | 109 ++ protocols/Gadu-Gadu/image.c | 1186 -------------- protocols/Gadu-Gadu/image.cpp | 1191 ++++++++++++++ protocols/Gadu-Gadu/import.c | 670 -------- protocols/Gadu-Gadu/import.cpp | 651 ++++++++ protocols/Gadu-Gadu/keepalive.c | 92 -- protocols/Gadu-Gadu/keepalive.cpp | 92 ++ protocols/Gadu-Gadu/libgadu/libgadu.h | 4 +- protocols/Gadu-Gadu/libgadu/pthread.c | 2 +- protocols/Gadu-Gadu/links.c | 173 -- protocols/Gadu-Gadu/links.cpp | 173 ++ protocols/Gadu-Gadu/oauth.c | 588 ------- protocols/Gadu-Gadu/oauth.cpp | 584 +++++++ protocols/Gadu-Gadu/ownerinfo.c | 88 -- protocols/Gadu-Gadu/ownerinfo.cpp | 78 + protocols/Gadu-Gadu/popups.c | 172 -- protocols/Gadu-Gadu/popups.cpp | 174 ++ protocols/Gadu-Gadu/services.c | 1021 ------------ protocols/Gadu-Gadu/services.cpp | 330 ++++ protocols/Gadu-Gadu/sessions.c | 445 ------ protocols/Gadu-Gadu/sessions.cpp | 447 ++++++ protocols/Gadu-Gadu/token.c | 178 --- protocols/Gadu-Gadu/token.cpp | 161 ++ protocols/Gadu-Gadu/userutils.c | 307 ---- protocols/Gadu-Gadu/userutils.cpp | 324 ++++ 43 files changed, 10452 insertions(+), 11656 deletions(-) delete mode 100644 protocols/Gadu-Gadu/avatar.c create mode 100644 protocols/Gadu-Gadu/avatar.cpp delete mode 100644 protocols/Gadu-Gadu/core.c create mode 100644 protocols/Gadu-Gadu/core.cpp delete mode 100644 protocols/Gadu-Gadu/dialogs.c create mode 100644 protocols/Gadu-Gadu/dialogs.cpp delete mode 100644 protocols/Gadu-Gadu/dynstuff.c create mode 100644 protocols/Gadu-Gadu/dynstuff.cpp delete mode 100644 protocols/Gadu-Gadu/filetransfer.c create mode 100644 protocols/Gadu-Gadu/filetransfer.cpp delete mode 100644 protocols/Gadu-Gadu/gg.c create mode 100644 protocols/Gadu-Gadu/gg.cpp delete mode 100644 protocols/Gadu-Gadu/groupchat.c create mode 100644 protocols/Gadu-Gadu/groupchat.cpp delete mode 100644 protocols/Gadu-Gadu/icolib.c create mode 100644 protocols/Gadu-Gadu/icolib.cpp delete mode 100644 protocols/Gadu-Gadu/image.c create mode 100644 protocols/Gadu-Gadu/image.cpp delete mode 100644 protocols/Gadu-Gadu/import.c create mode 100644 protocols/Gadu-Gadu/import.cpp delete mode 100644 protocols/Gadu-Gadu/keepalive.c create mode 100644 protocols/Gadu-Gadu/keepalive.cpp delete mode 100644 protocols/Gadu-Gadu/links.c create mode 100644 protocols/Gadu-Gadu/links.cpp delete mode 100644 protocols/Gadu-Gadu/oauth.c create mode 100644 protocols/Gadu-Gadu/oauth.cpp delete mode 100644 protocols/Gadu-Gadu/ownerinfo.c create mode 100644 protocols/Gadu-Gadu/ownerinfo.cpp delete mode 100644 protocols/Gadu-Gadu/popups.c create mode 100644 protocols/Gadu-Gadu/popups.cpp delete mode 100644 protocols/Gadu-Gadu/services.c create mode 100644 protocols/Gadu-Gadu/services.cpp delete mode 100644 protocols/Gadu-Gadu/sessions.c create mode 100644 protocols/Gadu-Gadu/sessions.cpp delete mode 100644 protocols/Gadu-Gadu/token.c create mode 100644 protocols/Gadu-Gadu/token.cpp delete mode 100644 protocols/Gadu-Gadu/userutils.c create mode 100644 protocols/Gadu-Gadu/userutils.cpp diff --git a/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj b/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj index fbbf4ef0f1..e4cb1fcd92 100644 --- a/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj +++ b/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj @@ -25,21 +25,21 @@ DynamicLibrary - MultiByte + Unicode true DynamicLibrary - MultiByte + Unicode DynamicLibrary - MultiByte + Unicode true DynamicLibrary - MultiByte + Unicode @@ -205,27 +205,28 @@ - - - - - - + + + + + + Create - - - - - - - - - - - - - + + + + + + + + + + + + + + NotUsing @@ -264,6 +265,7 @@ + diff --git a/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters b/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters index 762ede137b..0057590422 100644 --- a/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters +++ b/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters @@ -18,98 +18,101 @@ - - Source Files + + Source Files\libgadu - - Source Files + + Source Files\libgadu - - Source Files + + Source Files\libgadu - - Source Files + + Source Files\libgadu - - Source Files + + Source Files\libgadu - - Source Files + + Source Files\libgadu - - Source Files + + Source Files\libgadu - - Source Files + + Source Files\libgadu - - Source Files + + Source Files\libgadu - - Source Files + + Source Files\libgadu - - Source Files + + Source Files\libgadu - + + Source Files\libgadu + + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - - Source Files\libgadu + + Source Files - - Source Files\libgadu + + Source Files - - Source Files\libgadu + + Source Files - - Source Files\libgadu + + Source Files - - Source Files\libgadu + + Source Files - - Source Files\libgadu + + Source Files - - Source Files\libgadu + + Source Files - - Source Files\libgadu + + Source Files - - Source Files\libgadu + + Source Files - - Source Files\libgadu + + Source Files - - Source Files\libgadu + + Source Files - - Source Files\libgadu + + Source Files @@ -146,6 +149,9 @@ Header Files + + Header Files + diff --git a/protocols/Gadu-Gadu/avatar.c b/protocols/Gadu-Gadu/avatar.c deleted file mode 100644 index 39e49c12bf..0000000000 --- a/protocols/Gadu-Gadu/avatar.c +++ /dev/null @@ -1,461 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2009-2012 Bartosz Białek -// -// 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 "gg.h" -#include -#include -#include "protocol.h" - -////////////////////////////////////////////////////////// -// Avatars support -void gg_getavatarfilename(GGPROTO *gg, HANDLE hContact, char *pszDest, int cbLen) -{ - int tPathLen; - char *path = (char *)alloca(cbLen); - char *avatartype = NULL; - - if (gg->hAvatarsFolder == NULL || FoldersGetCustomPath(gg->hAvatarsFolder, path, cbLen, "")) { - char *tmpPath = Utils_ReplaceVars("%miranda_avatarcache%"); - tPathLen = mir_snprintf(pszDest, cbLen, "%s\\%s", tmpPath, GG_PROTO); - mir_free(tmpPath); - } - else { - strcpy(pszDest, path); - tPathLen = (int)strlen(pszDest); - } - - if (_access(pszDest, 0)) - CallService(MS_UTILS_CREATEDIRTREE, 0, (LPARAM)pszDest); - - switch (DBGetContactSettingByte(hContact, GG_PROTO, GG_KEY_AVATARTYPE, GG_KEYDEF_AVATARTYPE)) { - case PA_FORMAT_JPEG: avatartype = "jpg"; break; - case PA_FORMAT_GIF: avatartype = "gif"; break; - case PA_FORMAT_PNG: avatartype = "png"; break; - } - - if (hContact != NULL) { - DBVARIANT dbv; - if (!DBGetContactSettingString(hContact, GG_PROTO, GG_KEY_AVATARHASH, &dbv)) { - mir_snprintf(pszDest + tPathLen, cbLen - tPathLen, "\\%s.%s", dbv.pszVal, avatartype); - DBFreeVariant(&dbv); - } - } - else - mir_snprintf(pszDest + tPathLen, cbLen - tPathLen, "\\%s avatar.%s", GG_PROTO, avatartype); -} - -void gg_getavatarfileinfo(GGPROTO *gg, uin_t uin, char **avatarurl, int *type) -{ - NETLIBHTTPREQUEST req = {0}; - NETLIBHTTPREQUEST *resp; - char szUrl[128]; - *avatarurl = NULL; - *type = PA_FORMAT_UNKNOWN; - - req.cbSize = sizeof(req); - req.requestType = REQUEST_GET; - req.szUrl = szUrl; - mir_snprintf(szUrl, 128, "http://api.gadu-gadu.pl/avatars/%d/0.xml", uin); - req.flags = NLHRF_NODUMP | NLHRF_HTTP11 | NLHRF_REDIRECT; - resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req); - if (resp) { - if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { - HXML hXml; - TCHAR *xmlAction; - TCHAR *tag; - - xmlAction = gg_a2t(resp->pData); - tag = gg_a2t("result"); - hXml = xi.parseString(xmlAction, 0, tag); - - if (hXml != NULL) { - HXML node; - char *blank; - - mir_free(tag); tag = gg_a2t("users/user/avatars/avatar"); - node = xi.getChildByPath(hXml, tag, 0); - mir_free(tag); tag = gg_a2t("blank"); - blank = node != NULL ? gg_t2a(xi.getAttrValue(node, tag)) : NULL; - - if (blank != NULL && strcmp(blank, "1")) { - mir_free(tag); tag = gg_a2t("users/user/avatars/avatar/bigAvatar"); - node = xi.getChildByPath(hXml, tag, 0); - *avatarurl = node != NULL ? gg_t2a(xi.getText(node)) : NULL; - - mir_free(tag); tag = gg_a2t("users/user/avatars/avatar/originBigAvatar"); - node = xi.getChildByPath(hXml, tag, 0); - if (node != NULL) { - char *orgavurl = gg_t2a(xi.getText(node)); - char *avtype = strrchr(orgavurl, '.'); - avtype++; - if (!_stricmp(avtype, "jpg")) - *type = PA_FORMAT_JPEG; - else if (!_stricmp(avtype, "gif")) - *type = PA_FORMAT_GIF; - else if (!_stricmp(avtype, "png")) - *type = PA_FORMAT_PNG; - mir_free(orgavurl); - } - } - else *avatarurl = mir_strdup(""); - mir_free(blank); - xi.destroyNode(hXml); - } - mir_free(tag); - mir_free(xmlAction); - } - else gg_netlog(gg, "gg_getavatarfileinfo(): Invalid response code from HTTP request"); - CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); - } - else gg_netlog(gg, "gg_getavatarfileinfo(): No response from HTTP request"); -} - -char *gg_avatarhash(char *param) -{ - mir_sha1_byte_t digest[MIR_SHA1_HASH_SIZE]; - char *result; - int i; - - if (param == NULL || (result = (char *)mir_alloc(MIR_SHA1_HASH_SIZE * 2 + 1)) == NULL) - return NULL; - - mir_sha1_hash(param, (int)strlen(param), digest); - for (i = 0; i < MIR_SHA1_HASH_SIZE; i++) - sprintf(result + (i<<1), "%02x", digest[i]); - - return result; -} - -typedef struct -{ - HANDLE hContact; - char *AvatarURL; -} GGGETAVATARDATA; - -void gg_getavatar(GGPROTO *gg, HANDLE hContact, char *szAvatarURL) -{ - if (gg->pth_avatar.dwThreadId) { - GGGETAVATARDATA *data = mir_alloc(sizeof(GGGETAVATARDATA)); - data->hContact = hContact; - data->AvatarURL = mir_strdup(szAvatarURL); - EnterCriticalSection(&gg->avatar_mutex); - list_add(&gg->avatar_transfers, data, 0); - LeaveCriticalSection(&gg->avatar_mutex); - } -} - -typedef struct -{ - HANDLE hContact; - int iWaitFor; -} GGREQUESTAVATARDATA; - -void gg_requestavatar(GGPROTO *gg, HANDLE hContact, int iWaitFor) -{ - if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS) - && gg->pth_avatar.dwThreadId) { - GGREQUESTAVATARDATA *data = mir_alloc(sizeof(GGREQUESTAVATARDATA)); - data->hContact = hContact; - data->iWaitFor = iWaitFor; - EnterCriticalSection(&gg->avatar_mutex); - list_add(&gg->avatar_requests, data, 0); - LeaveCriticalSection(&gg->avatar_mutex); - } -} - -void __cdecl gg_avatarrequestthread(GGPROTO *gg, void *empty) -{ - list_t l; - - gg_netlog(gg, "gg_avatarrequestthread(): Avatar Request Thread Starting"); - while (gg->pth_avatar.dwThreadId) - { - EnterCriticalSection(&gg->avatar_mutex); - if (gg->avatar_requests) { - GGREQUESTAVATARDATA *data = (GGREQUESTAVATARDATA *)gg->avatar_requests->data; - char *AvatarURL; - int AvatarType, iWaitFor = data->iWaitFor; - HANDLE hContact = data->hContact; - - list_remove(&gg->avatar_requests, data, 0); - mir_free(data); - LeaveCriticalSection(&gg->avatar_mutex); - - gg_getavatarfileinfo(gg, DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0), &AvatarURL, &AvatarType); - if (AvatarURL != NULL && strlen(AvatarURL) > 0) - DBWriteContactSettingString(hContact, GG_PROTO, GG_KEY_AVATARURL, AvatarURL); - else - DBDeleteContactSetting(hContact, GG_PROTO, GG_KEY_AVATARURL); - DBWriteContactSettingByte(hContact, GG_PROTO, GG_KEY_AVATARTYPE, (BYTE)AvatarType); - DBWriteContactSettingByte(hContact, GG_PROTO, GG_KEY_AVATARREQUESTED, 1); - - if (iWaitFor) { - PROTO_AVATAR_INFORMATIONT pai = {0}; - pai.cbSize = sizeof(pai); - pai.hContact = hContact; - if (gg_getavatarinfo(gg, (WPARAM)GAIF_FORCE, (LPARAM)&pai) != GAIR_WAITFOR) - ProtoBroadcastAck(GG_PROTO, hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, (HANDLE)&pai, 0); - } - else ProtoBroadcastAck(GG_PROTO, hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, 0, 0); - } - else LeaveCriticalSection(&gg->avatar_mutex); - - EnterCriticalSection(&gg->avatar_mutex); - if (gg->avatar_transfers) { - GGGETAVATARDATA *data = (GGGETAVATARDATA *)gg->avatar_transfers->data; - NETLIBHTTPREQUEST req = {0}; - NETLIBHTTPREQUEST *resp; - PROTO_AVATAR_INFORMATIONT pai = {0}; - int result = 0; - - pai.cbSize = sizeof(pai); - pai.hContact = data->hContact; - pai.format = DBGetContactSettingByte(pai.hContact, GG_PROTO, GG_KEY_AVATARTYPE, GG_KEYDEF_AVATARTYPE); - - req.cbSize = sizeof(req); - req.requestType = REQUEST_GET; - req.szUrl = data->AvatarURL; - req.flags = NLHRF_NODUMP | NLHRF_HTTP11 | NLHRF_REDIRECT; - resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req); - if (resp) { - if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { - int file_fd; - - gg_getavatarfilename(gg, pai.hContact, pai.filename, sizeof(pai.filename)); - file_fd = _open(pai.filename, _O_WRONLY | _O_TRUNC | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE); - if (file_fd != -1) { - _write(file_fd, resp->pData, resp->dataLength); - _close(file_fd); - result = 1; - } - } - else gg_netlog(gg, "gg_avatarrequestthread(): Invalid response code from HTTP request"); - CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); - } - else gg_netlog(gg, "gg_avatarrequestthread(): No response from HTTP request"); - - ProtoBroadcastAck(GG_PROTO, pai.hContact, ACKTYPE_AVATAR, - result ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, (HANDLE)&pai, 0); - - if (!pai.hContact) - CallService(MS_AV_REPORTMYAVATARCHANGED, (WPARAM)GG_PROTO, 0); - - list_remove(&gg->avatar_transfers, data, 0); - mir_free(data->AvatarURL); - mir_free(data); - } - LeaveCriticalSection(&gg->avatar_mutex); - SleepEx(100, FALSE); - } - - for (l = gg->avatar_requests; l; l = l->next) { - GGREQUESTAVATARDATA *data = (GGREQUESTAVATARDATA *)l->data; - mir_free(data); - } - for (l = gg->avatar_transfers; l; l = l->next) { - GGGETAVATARDATA *data = (GGGETAVATARDATA *)l->data; - mir_free(data->AvatarURL); - mir_free(data); - } - list_destroy(gg->avatar_requests, 0); - list_destroy(gg->avatar_transfers, 0); - gg_netlog(gg, "gg_avatarrequestthread(): Avatar Request Thread Ending"); -} - -void gg_initavatarrequestthread(GGPROTO *gg) -{ - DWORD exitCode = 0; - - GetExitCodeThread(gg->pth_avatar.hThread, &exitCode); - if (exitCode != STILL_ACTIVE) { - gg->avatar_requests = gg->avatar_transfers = NULL; - gg->pth_avatar.hThread = gg_forkthreadex(gg, gg_avatarrequestthread, NULL, &gg->pth_avatar.dwThreadId); - } -} - -void gg_uninitavatarrequestthread(GGPROTO *gg) -{ - gg->pth_avatar.dwThreadId = 0; -#ifdef DEBUGMODE - gg_netlog(gg, "gg_uninitavatarrequestthread(): Waiting until Avatar Request Thread finished, if needed."); -#endif - gg_threadwait(gg, &gg->pth_avatar); -} - -void __cdecl gg_getuseravatarthread(GGPROTO *gg, void *empty) -{ - PROTO_AVATAR_INFORMATIONT pai = {0}; - char *AvatarURL; - int AvatarType; - - gg_getavatarfileinfo(gg, DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0), &AvatarURL, &AvatarType); - if (AvatarURL != NULL && strlen(AvatarURL) > 0) - DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_AVATARURL, AvatarURL); - else - DBDeleteContactSetting(NULL, GG_PROTO, GG_KEY_AVATARURL); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_AVATARTYPE, (BYTE)AvatarType); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_AVATARREQUESTED, 1); - - pai.cbSize = sizeof(pai); - gg_getavatarinfo(gg, (WPARAM)GAIF_FORCE, (LPARAM)&pai); -} - -void gg_getuseravatar(GGPROTO *gg) -{ - if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS) - && DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0)) - gg_forkthread(gg, gg_getuseravatarthread, NULL); -} - -void __cdecl gg_setavatarthread(GGPROTO *gg, void *param) -{ - NETLIBHTTPHEADER httpHeaders[4]; - NETLIBHTTPREQUEST req = {0}; - NETLIBHTTPREQUEST *resp; - char *szFilename = (char *)param; - const char *contentend = "\r\n--AaB03x--\r\n"; - char szUrl[128], uin[32], *authHeader, *data, *avatardata, content[256], - *fileext, image_ext[4], image_type[11]; - int file_fd, avatardatalen, datalen, contentlen, contentendlen, res = 0, repeat = 0; - - gg_netlog(gg, "gg_setavatar(): Trying to set user avatar using %s...", szFilename); - UIN2ID(DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0), uin); - - file_fd = _open(szFilename, _O_RDONLY | _O_BINARY, _S_IREAD); - if (file_fd == -1) { - gg_netlog(gg, "gg_setavatar(): Failed to open avatar file (%s).", strerror(errno)); - mir_free(szFilename); - gg_getuseravatar(gg); - return; - } - avatardatalen = _filelength(file_fd); - avatardata = (char *)mir_alloc(avatardatalen); - - _read(file_fd, avatardata, avatardatalen); - _close(file_fd); - - fileext = strrchr(szFilename, '.'); - fileext++; - if (!_stricmp(fileext, "jpg")) { - strcpy(image_ext, "jpg"); - strcpy(image_type, "image/jpeg"); - } - else if (!_stricmp(fileext, "gif")) { - strcpy(image_ext, "gif"); - strcpy(image_type, "image/gif"); - } - else /*if (!_stricmp(fileext, "png"))*/ { - strcpy(image_ext, "png"); - strcpy(image_type, "image/png"); - } - - mir_snprintf(content, 256, "--AaB03x\r\nContent-Disposition: form-data; name=\"_method\"\r\n\r\nPUT\r\n--AaB03x\r\nContent-Disposition: form-data; name=\"avatar\"; filename=\"%s.%s\"\r\nContent-Type: %s\r\n\r\n", - uin, image_ext, image_type); - contentlen = (int)strlen(content); - contentendlen = (int)strlen(contentend); - - datalen = contentlen + avatardatalen + contentendlen; - data = (char *)mir_alloc(datalen); - memcpy(data, content, contentlen); - memcpy(data + contentlen, avatardata, avatardatalen); - memcpy(data + contentlen + avatardatalen, contentend, contentendlen); - - mir_snprintf(szUrl, 128, "http://api.gadu-gadu.pl/avatars/%s/0.xml", uin); - gg_oauth_checktoken(gg, 0); - authHeader = gg_oauth_header(gg, "PUT", szUrl); - - req.cbSize = sizeof(req); - req.requestType = REQUEST_POST; - req.szUrl = szUrl; - req.flags = NLHRF_NODUMP | NLHRF_HTTP11; - req.headersCount = 4; - req.headers = httpHeaders; - httpHeaders[0].szName = "User-Agent"; - httpHeaders[0].szValue = GG8_VERSION; - httpHeaders[1].szName = "Authorization"; - httpHeaders[1].szValue = authHeader; - httpHeaders[2].szName = "Accept"; - httpHeaders[2].szValue = "*/*"; - httpHeaders[3].szName = "Content-Type"; - httpHeaders[3].szValue = "multipart/form-data; boundary=AaB03x"; - req.pData = data; - req.dataLength = datalen; - - resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req); - if (resp) { - if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { -#ifdef DEBUGMODE - gg_netlog(gg, "%s", resp->pData); -#endif - res = 1; - } - else gg_netlog(gg, "gg_setavatar(): Invalid response code from HTTP request"); - if (resp->resultCode == 403 || resp->resultCode == 401) - repeat = 1; - CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); - } - else gg_netlog(gg, "gg_setavatar(): No response from HTTP request"); - - if (repeat) { // Access Token expired - we need to obtain new - mir_free(authHeader); - gg_oauth_checktoken(gg, 1); - authHeader = gg_oauth_header(gg, "PUT", szUrl); - - ZeroMemory(&req, sizeof(req)); - req.cbSize = sizeof(req); - req.requestType = REQUEST_POST; - req.szUrl = szUrl; - req.flags = NLHRF_NODUMP | NLHRF_HTTP11; - req.headersCount = 4; - req.headers = httpHeaders; - req.pData = data; - req.dataLength = datalen; - - resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req); - if (resp) { - if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { -#ifdef DEBUGMODE - gg_netlog(gg, "%s", resp->pData); -#endif - res = 1; - } - else gg_netlog(gg, "gg_setavatar(): Invalid response code from HTTP request"); - CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); - } - else gg_netlog(gg, "gg_setavatar(): No response from HTTP request"); - } - - mir_free(authHeader); - mir_free(avatardata); - mir_free(data); - - if (res) - gg_netlog(gg, "gg_setavatar(): User avatar set successfully."); - else - gg_netlog(gg, "gg_setavatar(): Failed to set user avatar."); - - mir_free(szFilename); - gg_getuseravatar(gg); -} - -void gg_setavatar(GGPROTO *gg, const char *szFilename) -{ - gg_forkthread(gg, gg_setavatarthread, (void*)mir_strdup(szFilename)); -} diff --git a/protocols/Gadu-Gadu/avatar.cpp b/protocols/Gadu-Gadu/avatar.cpp new file mode 100644 index 0000000000..74c90748cc --- /dev/null +++ b/protocols/Gadu-Gadu/avatar.cpp @@ -0,0 +1,458 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2009-2012 Bartosz Białek +// +// 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 "gg.h" +#include +#include +#include "protocol.h" + +////////////////////////////////////////////////////////// +// Avatars support +void GGPROTO::getAvatarFilename(HANDLE hContact, TCHAR *pszDest, int cbLen) +{ + int tPathLen; + TCHAR *path = (TCHAR*)alloca(cbLen * sizeof(TCHAR)); + TCHAR *avatartype = NULL; + + if (hAvatarsFolder == NULL || FoldersGetCustomPathT(hAvatarsFolder, path, cbLen, _T(""))) { + mir_ptr tmpPath( Utils_ReplaceVarsT( _T("%miranda_avatarcache%"))); + tPathLen = mir_sntprintf(pszDest, cbLen, _T("%s\\%s"), (TCHAR*)tmpPath, m_tszUserName); + } + else { + _tcscpy(pszDest, path); + tPathLen = (int)_tcslen(pszDest); + } + + if (_taccess(pszDest, 0)) + CallService(MS_UTILS_CREATEDIRTREE, 0, (LPARAM)pszDest); + + switch (db_get_b(hContact, m_szModuleName, GG_KEY_AVATARTYPE, GG_KEYDEF_AVATARTYPE)) { + case PA_FORMAT_JPEG: avatartype = _T("jpg"); break; + case PA_FORMAT_GIF: avatartype = _T("gif"); break; + case PA_FORMAT_PNG: avatartype = _T("png"); break; + } + + if (hContact != NULL) { + DBVARIANT dbv; + if (!db_get_s(hContact, m_szModuleName, GG_KEY_AVATARHASH, &dbv, DBVT_ASCIIZ)) { + mir_sntprintf(pszDest + tPathLen, cbLen - tPathLen, _T("\\%s.%s"), dbv.pszVal, avatartype); + DBFreeVariant(&dbv); + } + } + else mir_sntprintf(pszDest + tPathLen, cbLen - tPathLen, _T("\\%s avatar.%s"), m_szModuleName, avatartype); +} + +void GGPROTO::getAvatarFileInfo(uin_t uin, char **avatarurl, int *type) +{ + NETLIBHTTPREQUEST req = {0}; + NETLIBHTTPREQUEST *resp; + char szUrl[128]; + *avatarurl = NULL; + *type = PA_FORMAT_UNKNOWN; + + req.cbSize = sizeof(req); + req.requestType = REQUEST_GET; + req.szUrl = szUrl; + mir_snprintf(szUrl, 128, "http://api.gadu-gadu.pl/avatars/%d/0.xml", uin); + req.flags = NLHRF_NODUMP | NLHRF_HTTP11 | NLHRF_REDIRECT; + resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)netlib, (LPARAM)&req); + if (resp) { + if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { + HXML hXml; + TCHAR *xmlAction; + TCHAR *tag; + + xmlAction = mir_a2t(resp->pData); + tag = mir_a2t("result"); + hXml = xi.parseString(xmlAction, 0, tag); + + if (hXml != NULL) { + HXML node; + char *blank; + + mir_free(tag); tag = mir_a2t("users/user/avatars/avatar"); + node = xi.getChildByPath(hXml, tag, 0); + mir_free(tag); tag = mir_a2t("blank"); + blank = (node != NULL) ? mir_t2a(xi.getAttrValue(node, tag)) : NULL; + + if (blank != NULL && strcmp(blank, "1")) { + mir_free(tag); tag = mir_a2t("users/user/avatars/avatar/bigAvatar"); + node = xi.getChildByPath(hXml, tag, 0); + *avatarurl = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + + mir_free(tag); tag = mir_a2t("users/user/avatars/avatar/originBigAvatar"); + node = xi.getChildByPath(hXml, tag, 0); + if (node != NULL) { + char *orgavurl = mir_t2a(xi.getText(node)); + char *avtype = strrchr(orgavurl, '.'); + avtype++; + if (!_stricmp(avtype, "jpg")) + *type = PA_FORMAT_JPEG; + else if (!_stricmp(avtype, "gif")) + *type = PA_FORMAT_GIF; + else if (!_stricmp(avtype, "png")) + *type = PA_FORMAT_PNG; + mir_free(orgavurl); + } + } + else *avatarurl = mir_strdup(""); + mir_free(blank); + xi.destroyNode(hXml); + } + mir_free(tag); + mir_free(xmlAction); + } + else netlog("gg_getavatarfileinfo(): Invalid response code from HTTP request"); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); + } + else netlog("gg_getavatarfileinfo(): No response from HTTP request"); +} + +char *gg_avatarhash(char *param) +{ + mir_sha1_byte_t digest[MIR_SHA1_HASH_SIZE]; + char *result; + int i; + + if (param == NULL || (result = (char *)mir_alloc(MIR_SHA1_HASH_SIZE * 2 + 1)) == NULL) + return NULL; + + mir_sha1_hash((BYTE*)param, (int)strlen(param), digest); + for (i = 0; i < MIR_SHA1_HASH_SIZE; i++) + sprintf(result + (i<<1), "%02x", digest[i]); + + return result; +} + +typedef struct +{ + HANDLE hContact; + char *AvatarURL; +} GGGETAVATARDATA; + +void GGPROTO::getAvatar(HANDLE hContact, char *szAvatarURL) +{ + if (pth_avatar.dwThreadId) { + GGGETAVATARDATA *data = (GGGETAVATARDATA*)mir_alloc(sizeof(GGGETAVATARDATA)); + data->hContact = hContact; + data->AvatarURL = mir_strdup(szAvatarURL); + EnterCriticalSection(&avatar_mutex); + list_add(&avatar_transfers, data, 0); + LeaveCriticalSection(&avatar_mutex); + } +} + +typedef struct +{ + HANDLE hContact; + int iWaitFor; +} GGREQUESTAVATARDATA; + +void GGPROTO::requestAvatar(HANDLE hContact, int iWaitFor) +{ + if (db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS) + && pth_avatar.dwThreadId) { + GGREQUESTAVATARDATA *data = (GGREQUESTAVATARDATA*)mir_alloc(sizeof(GGREQUESTAVATARDATA)); + data->hContact = hContact; + data->iWaitFor = iWaitFor; + EnterCriticalSection(&avatar_mutex); + list_add(&avatar_requests, data, 0); + LeaveCriticalSection(&avatar_mutex); + } +} + +void __cdecl GGPROTO::avatarrequestthread(void*) +{ + list_t l; + + netlog("gg_avatarrequestthread(): Avatar Request Thread Starting"); + while (pth_avatar.dwThreadId) + { + EnterCriticalSection(&avatar_mutex); + if (avatar_requests) { + GGREQUESTAVATARDATA *data = (GGREQUESTAVATARDATA *)avatar_requests->data; + char *AvatarURL; + int AvatarType, iWaitFor = data->iWaitFor; + HANDLE hContact = data->hContact; + + list_remove(&avatar_requests, data, 0); + mir_free(data); + LeaveCriticalSection(&avatar_mutex); + + getAvatarFileInfo( db_get_b(hContact, m_szModuleName, GG_KEY_UIN, 0), &AvatarURL, &AvatarType); + if (AvatarURL != NULL && strlen(AvatarURL) > 0) + db_set_s(hContact, m_szModuleName, GG_KEY_AVATARURL, AvatarURL); + else + db_unset(hContact, m_szModuleName, GG_KEY_AVATARURL); + db_set_b(hContact, m_szModuleName, GG_KEY_AVATARTYPE, (BYTE)AvatarType); + db_set_b(hContact, m_szModuleName, GG_KEY_AVATARREQUESTED, 1); + + if (iWaitFor) { + PROTO_AVATAR_INFORMATIONT pai = {0}; + pai.cbSize = sizeof(pai); + pai.hContact = hContact; + if (getavatarinfo((WPARAM)GAIF_FORCE, (LPARAM)&pai) != GAIR_WAITFOR) + ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, (HANDLE)&pai, 0); + } + else ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, 0, 0); + } + else LeaveCriticalSection(&avatar_mutex); + + EnterCriticalSection(&avatar_mutex); + if (avatar_transfers) { + GGGETAVATARDATA *data = (GGGETAVATARDATA *)avatar_transfers->data; + NETLIBHTTPREQUEST req = {0}; + NETLIBHTTPREQUEST *resp; + PROTO_AVATAR_INFORMATIONT pai = {0}; + int result = 0; + + pai.cbSize = sizeof(pai); + pai.hContact = data->hContact; + pai.format = db_get_b(pai.hContact, m_szModuleName, GG_KEY_AVATARTYPE, GG_KEYDEF_AVATARTYPE); + + req.cbSize = sizeof(req); + req.requestType = REQUEST_GET; + req.szUrl = data->AvatarURL; + req.flags = NLHRF_NODUMP | NLHRF_HTTP11 | NLHRF_REDIRECT; + resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)netlib, (LPARAM)&req); + if (resp) { + if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { + int file_fd; + + getAvatarFilename(pai.hContact, pai.filename, sizeof(pai.filename)); + file_fd = _topen(pai.filename, _O_WRONLY | _O_TRUNC | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE); + if (file_fd != -1) { + _write(file_fd, resp->pData, resp->dataLength); + _close(file_fd); + result = 1; + } + } + else netlog("gg_avatarrequestthread(): Invalid response code from HTTP request"); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); + } + else netlog("gg_avatarrequestthread(): No response from HTTP request"); + + ProtoBroadcastAck(m_szModuleName, pai.hContact, ACKTYPE_AVATAR, + result ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, (HANDLE)&pai, 0); + + if (!pai.hContact) + CallService(MS_AV_REPORTMYAVATARCHANGED, (WPARAM)m_szModuleName, 0); + + list_remove(&avatar_transfers, data, 0); + mir_free(data->AvatarURL); + mir_free(data); + } + LeaveCriticalSection(&avatar_mutex); + SleepEx(100, FALSE); + } + + for (l = avatar_requests; l; l = l->next) { + GGREQUESTAVATARDATA *data = (GGREQUESTAVATARDATA *)l->data; + mir_free(data); + } + for (l = avatar_transfers; l; l = l->next) { + GGGETAVATARDATA *data = (GGGETAVATARDATA *)l->data; + mir_free(data->AvatarURL); + mir_free(data); + } + list_destroy(avatar_requests, 0); + list_destroy(avatar_transfers, 0); + netlog("gg_avatarrequestthread(): Avatar Request Thread Ending"); +} + +void GGPROTO::initavatarrequestthread() +{ + DWORD exitCode = 0; + + GetExitCodeThread(pth_avatar.hThread, &exitCode); + if (exitCode != STILL_ACTIVE) { + avatar_requests = avatar_transfers = NULL; + pth_avatar.hThread = forkthreadex(&GGPROTO::avatarrequestthread, NULL, &pth_avatar.dwThreadId); + } +} + +void GGPROTO::uninitavatarrequestthread() +{ + pth_avatar.dwThreadId = 0; +#ifdef DEBUGMODE + netlog("gg_uninitavatarrequestthread(): Waiting until Avatar Request Thread finished, if needed."); +#endif + threadwait(&pth_avatar); +} + +void __cdecl GGPROTO::getuseravatarthread(void*) +{ + PROTO_AVATAR_INFORMATIONT pai = {0}; + char *AvatarURL; + int AvatarType; + + getAvatarFileInfo( db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0), &AvatarURL, &AvatarType); + if (AvatarURL != NULL && strlen(AvatarURL) > 0) + db_set_s(NULL, m_szModuleName, GG_KEY_AVATARURL, AvatarURL); + else + db_unset(NULL, m_szModuleName, GG_KEY_AVATARURL); + db_set_b(NULL, m_szModuleName, GG_KEY_AVATARTYPE, (BYTE)AvatarType); + db_set_b(NULL, m_szModuleName, GG_KEY_AVATARREQUESTED, 1); + + pai.cbSize = sizeof(pai); + getavatarinfo((WPARAM)GAIF_FORCE, (LPARAM)&pai); +} + +void GGPROTO::getUserAvatar() +{ + if (db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS) + && db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0)) + forkthread(&GGPROTO::getuseravatarthread, NULL); +} + +void __cdecl GGPROTO::setavatarthread(void *param) +{ + NETLIBHTTPHEADER httpHeaders[4]; + NETLIBHTTPREQUEST req = {0}; + NETLIBHTTPREQUEST *resp; + TCHAR *szFilename = (TCHAR*)param; + const char *contentend = "\r\n--AaB03x--\r\n"; + char szUrl[128], uin[32], *authHeader, *data, *avatardata, content[256], image_ext[4], image_type[11]; + int file_fd, avatardatalen, datalen, contentlen, contentendlen, res = 0, repeat = 0; + + netlog("gg_setavatar(): Trying to set user avatar using %s...", szFilename); + UIN2ID(db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0), uin); + + file_fd = _topen(szFilename, _O_RDONLY | _O_BINARY, _S_IREAD); + if (file_fd == -1) { + netlog("gg_setavatar(): Failed to open avatar file (%s).", strerror(errno)); + mir_free(szFilename); + getUserAvatar(); + return; + } + avatardatalen = _filelength(file_fd); + avatardata = (char *)mir_alloc(avatardatalen); + + _read(file_fd, avatardata, avatardatalen); + _close(file_fd); + + TCHAR *fileext = _tcsrchr(szFilename, '.'); + fileext++; + if (!_tcsicmp(fileext, _T("jpg"))) { + strcpy(image_ext, "jpg"); + strcpy(image_type, "image/jpeg"); + } + else if (!_tcsicmp(fileext, _T("gif"))) { + strcpy(image_ext, "gif"); + strcpy(image_type, "image/gif"); + } + else { + strcpy(image_ext, "png"); + strcpy(image_type, "image/png"); + } + + mir_snprintf(content, 256, "--AaB03x\r\nContent-Disposition: form-data; name=\"_method\"\r\n\r\nPUT\r\n--AaB03x\r\nContent-Disposition: form-data; name=\"avatar\"; filename=\"%s.%s\"\r\nContent-Type: %s\r\n\r\n", + uin, image_ext, image_type); + contentlen = (int)strlen(content); + contentendlen = (int)strlen(contentend); + + datalen = contentlen + avatardatalen + contentendlen; + data = (char *)mir_alloc(datalen); + memcpy(data, content, contentlen); + memcpy(data + contentlen, avatardata, avatardatalen); + memcpy(data + contentlen + avatardatalen, contentend, contentendlen); + + mir_snprintf(szUrl, 128, "http://api.gadu-gadu.pl/avatars/%s/0.xml", uin); + oauth_checktoken(0); + authHeader = oauth_header("PUT", szUrl); + + req.cbSize = sizeof(req); + req.requestType = REQUEST_POST; + req.szUrl = szUrl; + req.flags = NLHRF_NODUMP | NLHRF_HTTP11; + req.headersCount = 4; + req.headers = httpHeaders; + httpHeaders[0].szName = "User-Agent"; + httpHeaders[0].szValue = GG8_VERSION; + httpHeaders[1].szName = "Authorization"; + httpHeaders[1].szValue = authHeader; + httpHeaders[2].szName = "Accept"; + httpHeaders[2].szValue = "*/*"; + httpHeaders[3].szName = "Content-Type"; + httpHeaders[3].szValue = "multipart/form-data; boundary=AaB03x"; + req.pData = data; + req.dataLength = datalen; + + resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)netlib, (LPARAM)&req); + if (resp) { + if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { +#ifdef DEBUGMODE + netlog("%s", resp->pData); +#endif + res = 1; + } + else netlog("gg_setavatar(): Invalid response code from HTTP request"); + if (resp->resultCode == 403 || resp->resultCode == 401) + repeat = 1; + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); + } + else netlog("gg_setavatar(): No response from HTTP request"); + + if (repeat) { // Access Token expired - we need to obtain new + mir_free(authHeader); + oauth_checktoken(1); + authHeader = oauth_header("PUT", szUrl); + + ZeroMemory(&req, sizeof(req)); + req.cbSize = sizeof(req); + req.requestType = REQUEST_POST; + req.szUrl = szUrl; + req.flags = NLHRF_NODUMP | NLHRF_HTTP11; + req.headersCount = 4; + req.headers = httpHeaders; + req.pData = data; + req.dataLength = datalen; + + resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)netlib, (LPARAM)&req); + if (resp) { + if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { +#ifdef DEBUGMODE + netlog("%s", resp->pData); +#endif + res = 1; + } + else netlog("gg_setavatar(): Invalid response code from HTTP request"); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); + } + else netlog("gg_setavatar(): No response from HTTP request"); + } + + mir_free(authHeader); + mir_free(avatardata); + mir_free(data); + + if (res) + netlog("gg_setavatar(): User avatar set successfully."); + else + netlog("gg_setavatar(): Failed to set user avatar."); + + mir_free(szFilename); + getUserAvatar(); +} + +void GGPROTO::setAvatar(const TCHAR *szFilename) +{ + forkthread(&GGPROTO::setavatarthread, mir_tstrdup(szFilename)); +} diff --git a/protocols/Gadu-Gadu/core.c b/protocols/Gadu-Gadu/core.c deleted file mode 100644 index 0641f7df25..0000000000 --- a/protocols/Gadu-Gadu/core.c +++ /dev/null @@ -1,1841 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2009 Adam Strzelecki -// Copyright (c) 2009-2012 Bartosz Białek -// -// 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 "gg.h" -#include -#include - -//////////////////////////////////////////////////////////// -// Swap bits in DWORD -uint32_t swap32(uint32_t x) -{ - return (uint32_t) - (((x & (uint32_t) 0x000000ffU) << 24) | - ((x & (uint32_t) 0x0000ff00U) << 8) | - ((x & (uint32_t) 0x00ff0000U) >> 8) | - ((x & (uint32_t) 0xff000000U) >> 24)); -} - -//////////////////////////////////////////////////////////// -// Is online function -GGINLINE int gg_isonline(GGPROTO *gg) -{ - int isonline; - EnterCriticalSection(&gg->sess_mutex); - isonline = (gg->sess != NULL); - LeaveCriticalSection(&gg->sess_mutex); - - return isonline; -} - -//////////////////////////////////////////////////////////// -// Send disconnect request and wait for server thread to die -void gg_disconnect(GGPROTO *gg) -{ - // If main loop then send disconnect request - if (gg_isonline(gg)) - { - // Fetch proper status msg - char *szMsg = NULL; - - // Loadup status - if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_LEAVESTATUSMSG, GG_KEYDEF_LEAVESTATUSMSG)) - { - DBVARIANT dbv; - switch (DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, GG_KEYDEF_LEAVESTATUS)) - { - case ID_STATUS_ONLINE: - EnterCriticalSection(&gg->modemsg_mutex); - szMsg = mir_strdup(gg->modemsg.online); - LeaveCriticalSection(&gg->modemsg_mutex); - if (!szMsg && - !DBGetContactSettingString(NULL, "SRAway", gg_status2db(ID_STATUS_ONLINE, "Default"), &dbv)) - { - if (dbv.pszVal && *(dbv.pszVal)) - szMsg = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - break; - case ID_STATUS_AWAY: - EnterCriticalSection(&gg->modemsg_mutex); - szMsg = mir_strdup(gg->modemsg.away); - LeaveCriticalSection(&gg->modemsg_mutex); - if (!szMsg && - !DBGetContactSettingString(NULL, "SRAway", gg_status2db(ID_STATUS_AWAY, "Default"), &dbv)) - { - if (dbv.pszVal && *(dbv.pszVal)) - szMsg = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - break; - case ID_STATUS_DND: - EnterCriticalSection(&gg->modemsg_mutex); - szMsg = mir_strdup(gg->modemsg.dnd); - LeaveCriticalSection(&gg->modemsg_mutex); - if (!szMsg && - !DBGetContactSettingString(NULL, "SRAway", gg_status2db(ID_STATUS_DND, "Default"), &dbv)) - { - if (dbv.pszVal && *(dbv.pszVal)) - szMsg = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - break; - case ID_STATUS_FREECHAT: - EnterCriticalSection(&gg->modemsg_mutex); - szMsg = mir_strdup(gg->modemsg.freechat); - LeaveCriticalSection(&gg->modemsg_mutex); - if (!szMsg && - !DBGetContactSettingString(NULL, "SRAway", gg_status2db(ID_STATUS_FREECHAT, "Default"), &dbv)) - { - if (dbv.pszVal && *(dbv.pszVal)) - szMsg = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - break; - case ID_STATUS_INVISIBLE: - EnterCriticalSection(&gg->modemsg_mutex); - szMsg = mir_strdup(gg->modemsg.invisible); - LeaveCriticalSection(&gg->modemsg_mutex); - if (!szMsg && - !DBGetContactSettingString(NULL, "SRAway", gg_status2db(ID_STATUS_INVISIBLE, "Default"), &dbv)) - { - if (dbv.pszVal && *(dbv.pszVal)) - szMsg = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - break; - default: - // Set last status - EnterCriticalSection(&gg->modemsg_mutex); - szMsg = mir_strdup(gg_getstatusmsg(gg, gg->proto.m_iStatus)); - LeaveCriticalSection(&gg->modemsg_mutex); - } - } - - EnterCriticalSection(&gg->sess_mutex); - // Check if it has message - if (szMsg) - { - gg_change_status_descr(gg->sess, GG_STATUS_NOT_AVAIL_DESCR, szMsg); - mir_free(szMsg); - // Wait for disconnection acknowledge - } - else - { - gg_change_status(gg->sess, GG_STATUS_NOT_AVAIL); - // Send logoff immediately - gg_logoff(gg->sess); - } - LeaveCriticalSection(&gg->sess_mutex); - } - // Else cancel connection attempt - else if (gg->sock) - closesocket(gg->sock); -} - -//////////////////////////////////////////////////////////// -// DNS lookup function -uint32_t gg_dnslookup(GGPROTO *gg, char *host) -{ - uint32_t ip; - struct hostent *he; - - ip = inet_addr(host); - if(ip != INADDR_NONE) - { -#ifdef DEBUGMODE - gg_netlog(gg, "gg_dnslookup(): Parameter \"%s\" is already IP number.", host); -#endif - return ip; - } - he = gethostbyname(host); - if(he) - { - ip = *(uint32_t *) he->h_addr_list[0]; -#ifdef DEBUGMODE - gg_netlog(gg, "gg_dnslookup(): Parameter \"%s\" was resolved to %d.%d.%d.%d.", host, - LOBYTE(LOWORD(ip)), HIBYTE(LOWORD(ip)), LOBYTE(HIWORD(ip)), HIBYTE(HIWORD(ip))); -#endif - return ip; - } - gg_netlog(gg, "gg_dnslookup(): Cannot resolve hostname \"%s\".", host); - return 0; -} - -//////////////////////////////////////////////////////////// -// Host list decoder -typedef struct -{ - char hostname[128]; - int port; -} GGHOST; -#define ISHOSTALPHA(a) (((a) >= '0' && (a) <= '9') || ((a) >= 'a' && (a) <= 'z') || (a) == '.' || (a) == '-') -int gg_decodehosts(char *var, GGHOST *hosts, int max) -{ - int hp = 0; - char *hostname = NULL; - char *portname = NULL; - - while(var && *var && hp < max) - { - if(ISHOSTALPHA(*var)) - { - hostname = var; - - while(var && *var && ISHOSTALPHA(*var)) var ++; - - if(var && *var == ':' && var++ && *var && isdigit(*var)) - { - *(var - 1) = 0; - portname = var; - while(var && *var && isdigit(*var)) var++; - if (*var) { *var = 0; var ++; } - } - else - if (*var) { *var = 0; var ++; } - - // Insert new item - hosts[hp].hostname[127] = 0; - strncpy(hosts[hp].hostname, hostname, 127); - hosts[hp].port = portname ? atoi(portname) : 443; - hp ++; - - // Zero the names - hostname = NULL; - portname = NULL; - } - else - var ++; - } - return hp; -} - -//////////////////////////////////////////////////////////// -// Main connection session thread -void __cdecl gg_mainthread(GGPROTO *gg, void *empty) -{ - // Miranda variables - NETLIBUSERSETTINGS nlus = {0}; - DBVARIANT dbv; - // Gadu-Gadu variables - struct gg_login_params p = {0}; - struct gg_event *e; - struct gg_session *sess; - // Host cycling variables - int hostnum = 0, hostcount = 0; - GGHOST hosts[64]; - // Gadu-gadu login errors - static const struct tagReason { int type; char *str; } reason[] = { - { GG_FAILURE_RESOLVING, "Miranda was unable to resolve the name of the Gadu-Gadu server to its numeric address." }, - { GG_FAILURE_CONNECTING, "Miranda was unable to make a connection with a server. It is likely that the server is down, in which case you should wait for a while and try again later." }, - { GG_FAILURE_INVALID, "Received invalid server response." }, - { GG_FAILURE_READING, "The connection with the server was abortively closed during the connection attempt. You may have lost your local network connection." }, - { GG_FAILURE_WRITING, "The connection with the server was abortively closed during the connection attempt. You may have lost your local network connection." }, - { GG_FAILURE_PASSWORD, "Your Gadu-Gadu number and password combination was rejected by the Gadu-Gadu server. Please check login details at M->Options->Network->Gadu-Gadu and try again." }, - { GG_FAILURE_404, "Connecting to Gadu-Gadu hub failed." }, - { GG_FAILURE_TLS, "Cannot establish secure connection." }, - { GG_FAILURE_NEED_EMAIL, "Server disconnected asking you for changing your e-mail." }, - { GG_FAILURE_INTRUDER, "Too many login attempts with invalid password." }, - { GG_FAILURE_UNAVAILABLE, "Gadu-Gadu servers are now down. Try again later." }, - { 0, "Unknown" } - }; - time_t logonTime = 0; - time_t timeDeviation = DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_TIMEDEVIATION, GG_KEYDEF_TIMEDEVIATION); - int gg_failno = 0; - - gg_netlog(gg, "gg_mainthread(%x): Server Thread Starting", gg); -#ifdef DEBUGMODE - gg_debug_level = GG_DEBUG_NET | GG_DEBUG_TRAFFIC | GG_DEBUG_FUNCTION | GG_DEBUG_MISC; -#else - gg_debug_level = 0; -#endif - - // Broadcast that service is connecting - gg_broadcastnewstatus(gg, ID_STATUS_CONNECTING); - - // Client version and misc settings - p.client_version = GG_DEFAULT_CLIENT_VERSION; - p.protocol_version = GG_DEFAULT_PROTOCOL_VERSION; - p.protocol_features = GG_FEATURE_DND_FFC | GG_FEATURE_UNKNOWN_100 | GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | GG_FEATURE_TYPING_NOTIFICATION | GG_FEATURE_MULTILOGON; - p.encoding = GG_ENCODING_CP1250; - p.status_flags = GG_STATUS_FLAG_UNKNOWN; - if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWLINKS, GG_KEYDEF_SHOWLINKS)) - p.status_flags |= GG_STATUS_FLAG_SPAM; - - // Use audio - /* p.has_audio = 1; */ - - // Use async connections - /* p.async = 1; */ - - // Send Era Omnix info if set - p.era_omnix = DBGetContactSettingByte(NULL, GG_PROTO, "EraOmnix", 0); - - // Setup proxy - nlus.cbSize = sizeof(nlus); - if(CallService(MS_NETLIB_GETUSERSETTINGS, (WPARAM)gg->netlib, (LPARAM)&nlus)) - { - if(nlus.useProxy) - gg_netlog(gg, "gg_mainthread(%x): Using proxy %s:%d.", gg, nlus.szProxyServer, nlus.wProxyPort); - gg_proxy_enabled = nlus.useProxy; - gg_proxy_host = nlus.szProxyServer; - gg_proxy_port = nlus.wProxyPort; - if(nlus.useProxyAuth) - { - gg_proxy_username = nlus.szProxyAuthUser; - gg_proxy_password = nlus.szProxyAuthPassword; - } - else - gg_proxy_username = gg_proxy_password = NULL; - } - else - { - gg_netlog(gg, "gg_mainthread(%x): Failed loading proxy settings.", gg); - gg_proxy_enabled = 0; - } - - // Check out manual host setting - if(DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_MANUALHOST, GG_KEYDEF_MANUALHOST)) - { - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_SERVERHOSTS, &dbv)) - { - hostcount = gg_decodehosts(dbv.pszVal, hosts, 64); - DBFreeVariant(&dbv); - } - } - - // Readup password - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) - { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - p.password = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - else - { - gg_netlog(gg, "gg_mainthread(%x): No password specified. Exiting.", gg); - gg_broadcastnewstatus(gg, ID_STATUS_OFFLINE); - return; - } - - // Readup number - if (!(p.uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0))) - { - gg_netlog(gg, "gg_mainthread(%x): No Gadu-Gadu number specified. Exiting.", gg); - gg_broadcastnewstatus(gg, ID_STATUS_OFFLINE); - mir_free(p.password); - return; - } - - // Readup SSL/TLS setting - if(p.tls = DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SSLCONN, GG_KEYDEF_SSLCONN)) - gg_netlog(gg, "gg_mainthread(%x): Using TLS/SSL for connections.", gg); - - // Gadu-Gadu accepts image sizes upto 255 - p.image_size = 255; - - ////////////////////////////// DCC STARTUP ///////////////////////////// - // Uin is ok so startup dcc if not started already - if (!gg->dcc) - { - gg->event = CreateEvent(NULL, TRUE, FALSE, NULL); - gg_dccstart(gg); - - // Wait for DCC -#ifdef DEBUGMODE - gg_netlog(gg, "gg_mainthread(%x): Waiting DCC service to start...", gg); -#endif - while (WaitForSingleObjectEx(gg->event, INFINITE, TRUE) != WAIT_OBJECT_0); - CloseHandle(gg->event); gg->event = NULL; - } - // Check if dcc is running and setup forwarding port - if(gg->dcc && DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_FORWARDING, GG_KEYDEF_FORWARDING)) - { - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_FORWARDHOST, &dbv)) - { - if (!(p.external_addr = gg_dnslookup(gg, dbv.pszVal))) - { - char error[128]; - mir_snprintf(error, sizeof(error), Translate("External direct connections hostname %s is invalid. Disabling external host forwarding."), dbv.pszVal); - gg_showpopup(gg, GG_PROTONAME, error, GG_POPUP_WARNING | GG_POPUP_ALLOW_MSGBOX); - } - else - gg_netlog(gg, "gg_mainthread(%x): Loading forwarding host %s and port %d.", dbv.pszVal, p.external_port, gg); - if(p.external_addr) p.external_port = DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_FORWARDPORT, GG_KEYDEF_FORWARDPORT); - DBFreeVariant(&dbv); - } - } - // Setup client port - if(gg->dcc) p.client_port = gg->dcc->port; - -retry: - // Loadup startup status & description - EnterCriticalSection(&gg->modemsg_mutex); - p.status_descr = mir_strdup(gg_getstatusmsg(gg, gg->proto.m_iDesiredStatus)); - p.status = status_m2gg(gg, gg->proto.m_iDesiredStatus, p.status_descr != NULL); - - gg_netlog(gg, "gg_mainthread(%x): Connecting with number %d, status %d and description \"%s\".", gg, p.uin, gg->proto.m_iDesiredStatus, - p.status_descr ? p.status_descr : ""); - LeaveCriticalSection(&gg->modemsg_mutex); - - // Check manual hosts - if(hostnum < hostcount) - { - if (!(p.server_addr = gg_dnslookup(gg, hosts[hostnum].hostname))) - { - char error[128]; - mir_snprintf(error, sizeof(error), Translate("Server hostname %s is invalid. Using default hostname provided by the network."), hosts[hostnum].hostname); - gg_showpopup(gg, GG_PROTONAME, error, GG_POPUP_WARNING | GG_POPUP_ALLOW_MSGBOX); - } - else - { - p.server_port = hosts[hostnum].port; - gg_netlog(gg, "gg_mainthread(%x): Connecting to manually specified host %s (%d.%d.%d.%d) and port %d.", gg, - hosts[hostnum].hostname, LOBYTE(LOWORD(p.server_addr)), HIBYTE(LOWORD(p.server_addr)), - LOBYTE(HIWORD(p.server_addr)), HIBYTE(HIWORD(p.server_addr)), p.server_port); - } - } - else - p.server_port = p.server_addr = 0; - - // Send login request - if (!(sess = gg_login(&p, &gg->sock, &gg_failno))) - { - gg_broadcastnewstatus(gg, ID_STATUS_OFFLINE); - // Check if connection attempt wasn't cancelled by the user - if (gg->proto.m_iDesiredStatus != ID_STATUS_OFFLINE) - { - char error[128], *perror = NULL; - // Lookup for error desciption - if (errno == EACCES) - { - int i; - for (i = 0; reason[i].type; i++) if (reason[i].type == gg_failno) - { - perror = Translate(reason[i].str); - break; - } - } - if (!perror) - { - mir_snprintf(error, sizeof(error), Translate("Connection cannot be established because of error:\n\t%s"), strerror(errno)); - perror = error; - } - gg_netlog(gg, "gg_mainthread(%x): %s", gg, perror); - if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWCERRORS, GG_KEYDEF_SHOWCERRORS)) - gg_showpopup(gg, GG_PROTONAME, perror, GG_POPUP_ERROR | GG_POPUP_ALLOW_MSGBOX | GG_POPUP_ONCE); - - // Check if we should reconnect - if ((gg_failno >= GG_FAILURE_RESOLVING && gg_failno != GG_FAILURE_PASSWORD && gg_failno != GG_FAILURE_INTRUDER && gg_failno != GG_FAILURE_UNAVAILABLE) - && errno == EACCES - && (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT) || (hostnum < hostcount - 1))) - { - DWORD dwInterval = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_RECONNINTERVAL, GG_KEYDEF_RECONNINTERVAL), dwResult; - BOOL bRetry = TRUE; - - gg->hConnStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - dwResult = WaitForSingleObjectEx(gg->hConnStopEvent, dwInterval, TRUE); - if ((dwResult == WAIT_OBJECT_0 && gg->proto.m_iDesiredStatus == ID_STATUS_OFFLINE) - || (dwResult == WAIT_IO_COMPLETION && Miranda_Terminated())) - bRetry = FALSE; - CloseHandle(gg->hConnStopEvent); - gg->hConnStopEvent = NULL; - - // Reconnect to the next server on the list - if (bRetry) - { - if (hostnum < hostcount - 1) hostnum++; - mir_free(p.status_descr); - gg_broadcastnewstatus(gg, ID_STATUS_CONNECTING); - goto retry; - } - } - // We cannot do more about this - EnterCriticalSection(&gg->modemsg_mutex); - gg->proto.m_iDesiredStatus = ID_STATUS_OFFLINE; - LeaveCriticalSection(&gg->modemsg_mutex); - } - else - gg_netlog(gg, "gg_mainthread(%x)): Connection attempt cancelled by the user.", gg); - } - else - { - // Successfully connected - logonTime = time(NULL); - DBWriteContactSettingDword(NULL, GG_PROTO, GG_KEY_LOGONTIME, logonTime); - EnterCriticalSection(&gg->sess_mutex); - gg->sess = sess; - LeaveCriticalSection(&gg->sess_mutex); - // Subscribe users status notifications - gg_notifyall(gg); - // Set startup status - if (gg->proto.m_iDesiredStatus != status_gg2m(gg, p.status)) - gg_refreshstatus(gg, gg->proto.m_iDesiredStatus); - else - { - gg_broadcastnewstatus(gg, gg->proto.m_iDesiredStatus); - // Change status of the contact with our own UIN (if got yourself added to the contact list) - gg_changecontactstatus(gg, p.uin, p.status, p.status_descr, 0, 0, 0, 0); - } - if (gg->check_first_conn) // First connection to the account - { - // Start search for user data - gg_getinfo((PROTO_INTERFACE *)gg, NULL, 0); - // Fetch user avatar - gg_getuseravatar(gg); - gg->check_first_conn = 0; - } - } - - ////////////////////////////////////////////////////////////////////////////////// - // Main loop - while(gg_isonline(gg)) - { - // Connection broken/closed - if (!(e = gg_watch_fd(gg->sess))) - { - gg_netlog(gg, "gg_mainthread(%x): Connection closed.", gg); - EnterCriticalSection(&gg->sess_mutex); - gg_free_session(gg->sess); - gg->sess = NULL; - LeaveCriticalSection(&gg->sess_mutex); - break; - } - else - gg_netlog(gg, "gg_mainthread(%x): Event: %s", gg, ggdebug_eventtype(e)); - - switch(e->type) - { - // Client connected - case GG_EVENT_CONN_SUCCESS: - // Nada - break; - - // Client disconnected or connection failure - case GG_EVENT_CONN_FAILED: - case GG_EVENT_DISCONNECT: - EnterCriticalSection(&gg->sess_mutex); - gg_free_session(gg->sess); - gg->sess = NULL; - LeaveCriticalSection(&gg->sess_mutex); - break; - - // Client allowed to disconnect - case GG_EVENT_DISCONNECT_ACK: - // Send logoff - gg_logoff(gg->sess); - break; - - // Received ackowledge - case GG_EVENT_ACK: - if(e->event.ack.seq && e->event.ack.recipient) - { - ProtoBroadcastAck(GG_PROTO, gg_getcontact(gg, (DWORD)e->event.ack.recipient, 0, 0, NULL), - ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE) e->event.ack.seq, 0); - } - break; - - // Statuslist notify (deprecated) - case GG_EVENT_NOTIFY: - case GG_EVENT_NOTIFY_DESCR: - { - struct gg_notify_reply *n; - - n = (e->type == GG_EVENT_NOTIFY) ? e->event.notify : e->event.notify_descr.notify; - - for (; n->uin; n++) - { - char *descr = (e->type == GG_EVENT_NOTIFY_DESCR) ? e->event.notify_descr.descr : NULL; - gg_changecontactstatus(gg, n->uin, n->status, descr, 0, n->remote_ip, n->remote_port, n->version); - } - break; - } - // Statuslist notify (version >= 6.0) - case GG_EVENT_NOTIFY60: - { - uin_t uin = (uin_t)DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0); - int i; - for(i = 0; e->event.notify60[i].uin; i++) { - if (e->event.notify60[i].uin == uin) continue; - gg_changecontactstatus(gg, e->event.notify60[i].uin, e->event.notify60[i].status, e->event.notify60[i].descr, - e->event.notify60[i].time, e->event.notify60[i].remote_ip, e->event.notify60[i].remote_port, - e->event.notify60[i].version); - gg_requestavatar(gg, gg_getcontact(gg, e->event.notify60[i].uin, 0, 0, NULL), 0); - } - break; - } - - // Pubdir search reply && read own data reply - case GG_EVENT_PUBDIR50_SEARCH_REPLY: - case GG_EVENT_PUBDIR50_READ: - case GG_EVENT_PUBDIR50_WRITE: - { - gg_pubdir50_t res = e->event.pubdir50; - int i, count; - - if (e->type == GG_EVENT_PUBDIR50_SEARCH_REPLY) - { - gg_netlog(gg, "gg_mainthread(%x): Got user info.", gg); - // Store next search UIN - if (res->seq == GG_SEQ_SEARCH) - gg->next_uin = gg_pubdir50_next(res); - } - else if (e->type == GG_EVENT_PUBDIR50_READ) - { - gg_netlog(gg, "gg_mainthread(%x): Got owner info.", gg); - } - else if (e->type == GG_EVENT_PUBDIR50_WRITE) - { - gg_netlog(gg, "gg_mainthread(%x): Public directory save succesful.", gg); - // Update user details - gg_getinfo((PROTO_INTERFACE *)gg, NULL, 0); - } - - if ((count = gg_pubdir50_count(res)) > 0) - { - for (i = 0; i < count; i++) - { - // Loadup fields - const char *__fmnumber = gg_pubdir50_get(res, i, GG_PUBDIR50_UIN); - const char *__nick = gg_pubdir50_get(res, i, GG_PUBDIR50_NICKNAME); - const char *__firstname = gg_pubdir50_get(res, i, GG_PUBDIR50_FIRSTNAME); - const char *__lastname = gg_pubdir50_get(res, i, GG_PUBDIR50_LASTNAME); - const char *__familyname = gg_pubdir50_get(res, i, GG_PUBDIR50_FAMILYNAME); - const char *__birthyear = gg_pubdir50_get(res, i, GG_PUBDIR50_BIRTHYEAR); - const char *__city = gg_pubdir50_get(res, i, GG_PUBDIR50_CITY); - const char *__origincity = gg_pubdir50_get(res, i, GG_PUBDIR50_FAMILYCITY); - const char *__gender = gg_pubdir50_get(res, i, GG_PUBDIR50_GENDER); - const char *__status = gg_pubdir50_get(res, i, GG_PUBDIR50_STATUS); - uin_t uin = __fmnumber ? atoi(__fmnumber) : 0; - - HANDLE hContact = (res->seq == GG_SEQ_CHINFO) ? NULL : gg_getcontact(gg, uin, 0, 0, NULL); - gg_netlog(gg, "gg_mainthread(%x): Search result for uin %d, seq %d.", gg, uin, res->seq); - if (res->seq == GG_SEQ_SEARCH) - { - char strFmt1[64]; - char strFmt2[64]; - GGSEARCHRESULT sr = {0}; - - mir_snprintf(strFmt2, sizeof(strFmt2), "%s", (char *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, status_gg2m(gg, atoi(__status)), 0)); - if (__city) - { - mir_snprintf(strFmt1, sizeof(strFmt1), ", %s %s", Translate("City:"), __city); - strncat(strFmt2, strFmt1, sizeof(strFmt2) - strlen(strFmt2)); - } - if (__birthyear) - { - time_t t = time(NULL); - struct tm *lt = localtime(&t); - int br = atoi(__birthyear); - - if (br < (lt->tm_year + 1900) && br > 1900) - { - mir_snprintf(strFmt1, sizeof(strFmt1), ", %s %d", Translate("Age:"), (lt->tm_year + 1900) - br); - strncat(strFmt2, strFmt1, sizeof(strFmt2) - strlen(strFmt2)); - } - } - - sr.hdr.cbSize = sizeof(sr); - sr.hdr.nick = (char *)__nick; - sr.hdr.firstName = (char *)__firstname; - sr.hdr.lastName = (char *)__lastname; - sr.hdr.email = strFmt2; - sr.hdr.id = _ultoa(uin, strFmt1, 10); - sr.uin = uin; - - ProtoBroadcastAck(GG_PROTO, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE) 1, (LPARAM)&sr); - } - - if (((res->seq == GG_SEQ_INFO || res->seq == GG_SEQ_GETNICK) && hContact != NULL) - || res->seq == GG_SEQ_CHINFO) - { - // Change nickname if it's not present - if (__nick && (res->seq == GG_SEQ_GETNICK || res->seq == GG_SEQ_CHINFO)) - DBWriteContactSettingString(hContact, GG_PROTO, GG_KEY_NICK, __nick); - - if (__nick) - DBWriteContactSettingString(hContact, GG_PROTO, "NickName", __nick); - else if (res->seq == GG_SEQ_CHINFO) - DBDeleteContactSetting(NULL, GG_PROTO, "NickName"); - - // Change other info - if (__city) - DBWriteContactSettingString(hContact, GG_PROTO, "City", __city); - else if (res->seq == GG_SEQ_CHINFO) - DBDeleteContactSetting(NULL, GG_PROTO, "City"); - - if (__firstname) - DBWriteContactSettingString(hContact, GG_PROTO, "FirstName", __firstname); - else if (res->seq == GG_SEQ_CHINFO) - DBDeleteContactSetting(NULL, GG_PROTO, "FirstName"); - - if (__lastname) - DBWriteContactSettingString(hContact, GG_PROTO, "LastName", __lastname); - else if (res->seq == GG_SEQ_CHINFO) - DBDeleteContactSetting(NULL, GG_PROTO, "LastName"); - - if (__familyname) - DBWriteContactSettingString(hContact, GG_PROTO, "FamilyName", __familyname); - else if (res->seq == GG_SEQ_CHINFO) - DBDeleteContactSetting(NULL, GG_PROTO, "FamilyName"); - - if (__origincity) - DBWriteContactSettingString(hContact, GG_PROTO, "CityOrigin", __origincity); - else if (res->seq == GG_SEQ_CHINFO) - DBDeleteContactSetting(NULL, GG_PROTO, "CityOrigin"); - - if (__birthyear) - { - time_t t = time(NULL); - struct tm *lt = localtime(&t); - int br = atoi(__birthyear); - if (br > 0) - { - DBWriteContactSettingWord(hContact, GG_PROTO, "Age", (WORD)(lt->tm_year + 1900 - br)); - DBWriteContactSettingWord(hContact, GG_PROTO, "BirthYear", (WORD)br); - } - } - else if (res->seq == GG_SEQ_CHINFO) - { - DBDeleteContactSetting(NULL, GG_PROTO, "Age"); - DBDeleteContactSetting(NULL, GG_PROTO, "BirthYear"); - } - - // Gadu-Gadu Male <-> Female - if (__gender) - { - if (res->seq == GG_SEQ_CHINFO) - DBWriteContactSettingByte(hContact, GG_PROTO, "Gender", - (BYTE)(!strcmp(__gender, GG_PUBDIR50_GENDER_SET_MALE) ? 'M' : - (!strcmp(__gender, GG_PUBDIR50_GENDER_SET_FEMALE) ? 'F' : '?'))); - else - DBWriteContactSettingByte(hContact, GG_PROTO, "Gender", - (BYTE)(!strcmp(__gender, GG_PUBDIR50_GENDER_MALE) ? 'M' : - (!strcmp(__gender, GG_PUBDIR50_GENDER_FEMALE) ? 'F' : '?'))); - } - else if (res->seq == GG_SEQ_CHINFO) - { - DBDeleteContactSetting(NULL, GG_PROTO, "Gender"); - } - - gg_netlog(gg, "gg_mainthread(%x): Setting user info for uin %d.", gg, uin); - ProtoBroadcastAck(GG_PROTO, hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE) 1, 0); - } - } - } - if (res->seq == GG_SEQ_SEARCH) - ProtoBroadcastAck(GG_PROTO, NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE) 1, 0); - break; - } - - // Status (deprecated) - case GG_EVENT_STATUS: - gg_changecontactstatus(gg, e->event.status.uin, e->event.status.status, e->event.status.descr, 0, 0, 0, 0); - break; - - // Status (version >= 6.0) - case GG_EVENT_STATUS60: - { - HANDLE hContact = gg_getcontact(gg, e->event.status60.uin, 0, 0, NULL); - int oldstatus = DBGetContactSettingWord(hContact, GG_PROTO, GG_KEY_STATUS, (WORD)ID_STATUS_OFFLINE); - uin_t uin = (uin_t)DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0); - - if (e->event.status60.uin == uin) - { - // Status was changed by the user simultaneously logged on using different Miranda account or IM client - int iStatus = status_gg2m(gg, e->event.status60.status); - CallProtoService(GG_PROTO, PS_SETAWAYMSG, iStatus, (LPARAM)e->event.status60.descr); - CallProtoService(GG_PROTO, PS_SETSTATUS, iStatus, 0); - } - - gg_changecontactstatus(gg, e->event.status60.uin, e->event.status60.status, e->event.status60.descr, - e->event.status60.time, e->event.status60.remote_ip, e->event.status60.remote_port, e->event.status60.version); - - if (oldstatus == ID_STATUS_OFFLINE && DBGetContactSettingWord(hContact, GG_PROTO, GG_KEY_STATUS, (WORD)ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE) - gg_requestavatar(gg, hContact, 0); - } - break; - - // Received userlist / or put info - case GG_EVENT_USERLIST: - switch (e->event.userlist.type) - { - case GG_USERLIST_GET_REPLY: - if(e->event.userlist.reply) - { - gg_parsecontacts(gg, e->event.userlist.reply); - MessageBox( - NULL, - Translate("List import successful."), - GG_PROTONAME, - MB_OK | MB_ICONINFORMATION - ); - } - break; - - case GG_USERLIST_PUT_REPLY: - if(gg->list_remove) - MessageBox( - NULL, - Translate("List remove successful."), - GG_PROTONAME, - MB_OK | MB_ICONINFORMATION - ); - else - MessageBox( - NULL, - Translate("List export successful."), - GG_PROTONAME, - MB_OK | MB_ICONINFORMATION - ); - - break; - } - break; - - // Received message - case GG_EVENT_MSG: - // This is CTCP request - if ((e->event.msg.msgclass & GG_CLASS_CTCP)) - { - gg_dccconnect(gg, e->event.msg.sender); - } - // Check if not conference and block - else if (!e->event.msg.recipients_count || gg->gc_enabled) - { - // Check if groupchat - if(e->event.msg.recipients_count && gg->gc_enabled && !DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF)) - { - char *chat = gg_gc_getchat(gg, e->event.msg.sender, e->event.msg.recipients, e->event.msg.recipients_count); - if(chat) - { - char id[32]; - GCDEST gcdest = {GG_PROTO, chat, GC_EVENT_MESSAGE}; - GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; - time_t t = time(NULL); - - UIN2ID(e->event.msg.sender, id); - - gcevent.pszUID = id; - gcevent.pszText = e->event.msg.message; - gcevent.pszNick = (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) gg_getcontact(gg, e->event.msg.sender, 1, 0, NULL), 0); - gcevent.time = (!(e->event.msg.msgclass & GG_CLASS_OFFLINE) || e->event.msg.time > (t - timeDeviation)) ? t : e->event.msg.time; - gcevent.dwFlags = GCEF_ADDTOLOG; - gg_netlog(gg, "gg_mainthread(%x): Conference message to room %s & id %s.", gg, chat, id); - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); - } - } - // Check if not empty message ( who needs it? ) - else if (!e->event.msg.recipients_count && e->event.msg.message && *e->event.msg.message && strcmp(e->event.msg.message, "\xA0\0")) - { - CCSDATA ccs = {0}; - PROTORECVEVENT pre = {0}; - time_t t = time(NULL); - ccs.szProtoService = PSR_MESSAGE; - ccs.hContact = gg_getcontact(gg, e->event.msg.sender, 1, 0, NULL); - ccs.lParam = (LPARAM)⪯ - pre.timestamp = (!(e->event.msg.msgclass & GG_CLASS_OFFLINE) || e->event.msg.time > (t - timeDeviation)) ? t : e->event.msg.time; - pre.szMessage = e->event.msg.message; - CallService(MS_PROTO_CHAINRECV, 0, (LPARAM) &ccs); - } - - // RichEdit format included (image) - if(e->event.msg.formats_length && - DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGRECEIVE, GG_KEYDEF_IMGRECEIVE) && - !(DBGetContactSettingDword(gg_getcontact(gg, e->event.msg.sender, 1, 0, NULL), "Ignore", "Mask1", 0) & IGNOREEVENT_MESSAGE)) - { - char *formats = e->event.msg.formats; - int len = 0, formats_len = e->event.msg.formats_length, add_ptr; - - while (len < formats_len) - { - add_ptr = sizeof(struct gg_msg_richtext_format); - if (((struct gg_msg_richtext_format*)formats)->font & GG_FONT_IMAGE) - { - struct gg_msg_richtext_image *image = (struct gg_msg_richtext_image *)(formats + add_ptr); - EnterCriticalSection(&gg->sess_mutex); - gg_image_request(gg->sess, e->event.msg.sender, image->size, image->crc32); - LeaveCriticalSection(&gg->sess_mutex); - - gg_netlog(gg, "gg_mainthread: image request sent!"); - add_ptr += sizeof(struct gg_msg_richtext_image); - } - if (((struct gg_msg_richtext_format*)formats)->font & GG_FONT_COLOR) - add_ptr += sizeof(struct gg_msg_richtext_color); - len += add_ptr; - formats += add_ptr; - } - } - } - break; - - // Message sent from concurrent user session - case GG_EVENT_MULTILOGON_MSG: - if (e->event.multilogon_msg.recipients_count && gg->gc_enabled && !DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF)) - { - char *chat = gg_gc_getchat(gg, e->event.multilogon_msg.sender, e->event.multilogon_msg.recipients, e->event.multilogon_msg.recipients_count); - if (chat) - { - char id[32]; - DBVARIANT dbv; - GCDEST gcdest = {GG_PROTO, chat, GC_EVENT_MESSAGE}; - GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; - - UIN2ID(DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0), id); - - gcevent.pszUID = id; - gcevent.pszText = e->event.multilogon_msg.message; - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_NICK, &dbv)) - gcevent.pszNick = dbv.pszVal; - else - gcevent.pszNick = Translate("Me"); - gcevent.time = e->event.multilogon_msg.time; - gcevent.bIsMe = 1; - gcevent.dwFlags = GCEF_ADDTOLOG; - gg_netlog(gg, "gg_mainthread(%x): Sent conference message to room %s.", gg, chat); - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); - if (gcevent.pszNick == dbv.pszVal) DBFreeVariant(&dbv); - } - } - else if (!e->event.multilogon_msg.recipients_count && e->event.multilogon_msg.message && *e->event.multilogon_msg.message - && strcmp(e->event.multilogon_msg.message, "\xA0\0")) - { - DBEVENTINFO dbei = {0}; - dbei.cbSize = sizeof(dbei); - dbei.szModule = GG_PROTO; - dbei.timestamp = (DWORD)e->event.multilogon_msg.time; - dbei.flags = DBEF_SENT; - dbei.eventType = EVENTTYPE_MESSAGE; - dbei.cbBlob = (DWORD)strlen(e->event.multilogon_msg.message) + 1; - dbei.pBlob = (PBYTE)e->event.multilogon_msg.message; - CallService(MS_DB_EVENT_ADD, (WPARAM)gg_getcontact(gg, e->event.multilogon_msg.sender, 1, 0, NULL), (LPARAM)&dbei); - } - break; - - // Information on active concurrent sessions - case GG_EVENT_MULTILOGON_INFO: - { - list_t l; - int* iIndexes = NULL, i; - gg_netlog(gg, "gg_mainthread(): Concurrent sessions count: %d.", e->event.multilogon_info.count); - if (e->event.multilogon_info.count > 0) - iIndexes = mir_calloc(e->event.multilogon_info.count * sizeof(int)); - EnterCriticalSection(&gg->sessions_mutex); - for (l = gg->sessions; l; l = l->next) - { - struct gg_multilogon_session* sess = (struct gg_multilogon_session*)l->data; - for (i = 0; i < e->event.multilogon_info.count; i++) - { - if (!memcmp(&sess->id, &e->event.multilogon_info.sessions[i].id, sizeof(gg_multilogon_id_t)) && iIndexes) - { - iIndexes[i]++; - break; - } - } - mir_free(sess->name); - mir_free(sess); - } - list_destroy(gg->sessions, 0); - gg->sessions = NULL; - for (i = 0; i < e->event.multilogon_info.count; i++) - { - struct gg_multilogon_session* sess = mir_alloc(sizeof(struct gg_multilogon_session)); - memcpy(sess, &e->event.multilogon_info.sessions[i], sizeof(struct gg_multilogon_session)); - sess->name = mir_strdup(*e->event.multilogon_info.sessions[i].name != '\0' - ? e->event.multilogon_info.sessions[i].name - : Translate("Unknown client")); - list_add(&gg->sessions, sess, 0); - } - LeaveCriticalSection(&gg->sessions_mutex); - gg_sessions_updatedlg(gg); - if (ServiceExists(MS_POPUP_ADDPOPUPCLASS)) - { - const char* szText = time(NULL) - logonTime > 3 - ? Translate("You have logged in at another location") - : Translate("You are logged in at another location"); - for (i = 0; i < e->event.multilogon_info.count; i++) - { - char szMsg[MAX_SECONDLINE]; - if (iIndexes && iIndexes[i]) continue; - mir_snprintf(szMsg, SIZEOF(szMsg), "%s (%s)", szText, - *e->event.multilogon_info.sessions[i].name != '\0' - ? e->event.multilogon_info.sessions[i].name - : Translate("Unknown client")); - gg_showpopup(gg, GG_PROTONAME, szMsg, GG_POPUP_MULTILOGON); - } - } - mir_free(iIndexes); - } - break; - - // Image reply sent - case GG_EVENT_IMAGE_REPLY: - // Get rid of empty image - if(e->event.image_reply.size && e->event.image_reply.image) - { - HANDLE hContact = gg_getcontact(gg, e->event.image_reply.sender, 1, 0, NULL); - void *img = (void *)gg_img_loadpicture(gg, e, 0); - - if (!img) - break; - - if(DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGMETHOD, GG_KEYDEF_IMGMETHOD) == 1 || gg_img_opened(gg, e->event.image_reply.sender)) - { - gg_img_display(gg, hContact, img); - } - else if(DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGMETHOD, GG_KEYDEF_IMGMETHOD) == 2) - { - gg_img_displayasmsg(gg, hContact, img); - } - else - { - CLISTEVENT cle = {0}; - char service[128]; - mir_snprintf(service, sizeof(service), GGS_RECVIMAGE, GG_PROTO); - - cle.cbSize = sizeof(cle); - cle.hContact = hContact; - cle.hIcon = LoadIconEx("image", FALSE); - cle.flags = CLEF_URGENT; - cle.hDbEvent = (HANDLE)"img"; - cle.lParam = (LPARAM)img; - cle.pszService = service; - cle.pszTooltip = Translate("Incoming image"); - CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&cle); - ReleaseIconEx("image", FALSE); - } - } - break; - - // Image send request - case GG_EVENT_IMAGE_REQUEST: - gg_img_sendonrequest(gg, e); - break; - - // Incoming direct connection - case GG_EVENT_DCC7_NEW: - { - struct gg_dcc7 *dcc7 = e->event.dcc7_new; - gg_netlog(gg, "gg_mainthread(%x): Incoming direct connection.", gg); - dcc7->contact = gg_getcontact(gg, dcc7->peer_uin, 0, 0, NULL); - - // Check if user is on the list and if it is my uin - if (!dcc7->contact || DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, -1) != dcc7->uin) { - gg_dcc7_free(dcc7); - e->event.dcc7_new = NULL; - break; - } - - // Add to waiting transfers - EnterCriticalSection(&gg->ft_mutex); - list_add(&gg->transfers, dcc7, 0); - LeaveCriticalSection(&gg->ft_mutex); - - ////////////////////////////////////////////////// - // Add file recv request - { - CCSDATA ccs; - PROTORECVEVENT pre; - char *szBlob; - char *szFilename = dcc7->filename; - char *szMsg = dcc7->filename; - gg_netlog(gg, "gg_mainthread(%x): Client: %d, File ack filename \"%s\" size %d.", gg, dcc7->peer_uin, - dcc7->filename, dcc7->size); - // Make new ggtransfer struct - szBlob = (char *)malloc(sizeof(DWORD) + strlen(szFilename) + strlen(szMsg) + 2); - // Store current dcc - *(PDWORD)szBlob = (DWORD)dcc7; - // Store filename - strcpy(szBlob + sizeof(DWORD), szFilename); - // Store description - strcpy(szBlob + sizeof(DWORD) + strlen(szFilename) + 1, szMsg); - ccs.szProtoService = PSR_FILE; - ccs.hContact = dcc7->contact; - ccs.wParam = 0; - ccs.lParam = (LPARAM)⪯ - pre.flags = 0; - pre.timestamp = time(NULL); - pre.szMessage = szBlob; - pre.lParam = 0; - CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); - free(szBlob); - } - e->event.dcc7_new = NULL; - } - break; - - // Direct connection rejected - case GG_EVENT_DCC7_REJECT: - { - struct gg_dcc7 *dcc7 = e->event.dcc7_reject.dcc7; - if (dcc7->type == GG_SESSION_DCC7_SEND) - { - gg_netlog(gg, "gg_mainthread(%x): File transfer denied by client %d (reason = %d).", gg, dcc7->peer_uin, e->event.dcc7_reject.reason); - ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DENIED, dcc7, 0); - - // Remove from watches and free - EnterCriticalSection(&gg->ft_mutex); - list_remove(&gg->watches, dcc7, 0); - LeaveCriticalSection(&gg->ft_mutex); - gg_dcc7_free(dcc7); - } - else - { - gg_netlog(gg, "gg_mainthread(%x): File transfer aborted by client %d.", gg, dcc7->peer_uin); - - // Remove transfer from waiting list - EnterCriticalSection(&gg->ft_mutex); - list_remove(&gg->transfers, dcc7, 0); - LeaveCriticalSection(&gg->ft_mutex); - } - } - break; - - // Direct connection error - case GG_EVENT_DCC7_ERROR: - { - struct gg_dcc7 *dcc7 = e->event.dcc7_error_ex.dcc7; - switch (e->event.dcc7_error) - { - case GG_ERROR_DCC7_HANDSHAKE: - gg_netlog(gg, "gg_mainthread(%x): Client: %d, Handshake error.", gg, dcc7 ? dcc7->peer_uin : 0); - break; - case GG_ERROR_DCC7_NET: - gg_netlog(gg, "gg_mainthread(%x): Client: %d, Network error.", gg, dcc7 ? dcc7->peer_uin : 0); - break; - case GG_ERROR_DCC7_FILE: - gg_netlog(gg, "gg_mainthread(%x): Client: %d, File read/write error.", gg, dcc7 ? dcc7->peer_uin : 0); - break; - case GG_ERROR_DCC7_EOF: - gg_netlog(gg, "gg_mainthread(%x): Client: %d, End of file/connection error.", gg, dcc7 ? dcc7->peer_uin : 0); - break; - case GG_ERROR_DCC7_REFUSED: - gg_netlog(gg, "gg_mainthread(%x): Client: %d, Connection refused error.", gg, dcc7 ? dcc7->peer_uin : 0); - break; - case GG_ERROR_DCC7_RELAY: - gg_netlog(gg, "gg_mainthread(%x): Client: %d, Relay connection error.", gg, dcc7 ? dcc7->peer_uin : 0); - break; - default: - gg_netlog(gg, "gg_mainthread(%x): Client: %d, Unknown error.", gg, dcc7 ? dcc7->peer_uin : 0); - } - if (!dcc7) break; - - // Remove from watches - list_remove(&gg->watches, dcc7, 0); - - // Close file & fail - if (dcc7->file_fd != -1) - { - _close(dcc7->file_fd); - dcc7->file_fd = -1; - } - - if (dcc7->contact) - ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); - - // Free dcc - gg_dcc7_free(dcc7); - } - break; - - case GG_EVENT_XML_ACTION: - if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) { - HXML hXml; - TCHAR *xmlAction; - TCHAR *tag; - - xmlAction = gg_a2t(e->event.xml_action.data); - tag = gg_a2t("events"); - hXml = xi.parseString(xmlAction, 0, tag); - - if (hXml != NULL) { - HXML node; - char *type, *sender; - - mir_free(tag); - tag = gg_a2t("event/type"); - node = xi.getChildByPath(hXml, tag, 0); - type = node != NULL ? gg_t2a(xi.getText(node)) : NULL; - - mir_free(tag); - tag = gg_a2t("event/sender"); - node = xi.getChildByPath(hXml, tag, 0); - sender = node != NULL ? gg_t2a(xi.getText(node)) : NULL; - gg_netlog(gg, "gg_mainthread(%x): XML Action type: %s.", gg, type != NULL ? type : "unknown"); - // Avatar change notify - if (type != NULL && !strcmp(type, "28")) { - gg_netlog(gg, "gg_mainthread(%x): Client %s changed his avatar.", gg, sender); - gg_requestavatar(gg, gg_getcontact(gg, atoi(sender), 0, 0, NULL), 0); - } - mir_free(type); - mir_free(sender); - xi.destroyNode(hXml); - } - mir_free(tag); - mir_free(xmlAction); - } - break; - - case GG_EVENT_TYPING_NOTIFICATION: - { - HANDLE hContact = gg_getcontact(gg, e->event.typing_notification.uin, 0, 0, NULL); -#ifdef DEBUGMODE - gg_netlog(gg, "gg_mainthread(%x): Typing notification from %d (%d).", gg, - e->event.typing_notification.uin, e->event.typing_notification.length); -#endif - CallService(MS_PROTO_CONTACTISTYPING, (WPARAM)hContact, - e->event.typing_notification.length > 0 ? 7 : PROTOTYPE_CONTACTTYPING_OFF); - } - break; - } - // Free event struct - gg_free_event(e); - } - - gg_broadcastnewstatus(gg, ID_STATUS_OFFLINE); - gg_setalloffline(gg); - DBWriteContactSettingDword(NULL, GG_PROTO, GG_KEY_LOGONTIME, 0); - - // If it was unwanted disconnection reconnect - if(gg->proto.m_iDesiredStatus != ID_STATUS_OFFLINE - && DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT)) - { - gg_netlog(gg, "gg_mainthread(%x): Unintentional disconnection detected. Going to reconnect...", gg); - hostnum = 0; - gg_broadcastnewstatus(gg, ID_STATUS_CONNECTING); - mir_free(p.status_descr); - goto retry; - } - - mir_free(p.password); - mir_free(p.status_descr); - - // Destroy concurrent sessions list - { - list_t l; - EnterCriticalSection(&gg->sessions_mutex); - for (l = gg->sessions; l; l = l->next) - { - struct gg_multilogon_session* sess = (struct gg_multilogon_session*)l->data; - mir_free(sess->name); - mir_free(sess); - } - list_destroy(gg->sessions, 0); - gg->sessions = NULL; - LeaveCriticalSection(&gg->sessions_mutex); - } - - // Stop dcc server - gg->pth_dcc.dwThreadId = 0; -#ifdef DEBUGMODE - gg_netlog(gg, "gg_mainthread(%x): Waiting until DCC Server Thread finished, if needed.", gg); -#endif - gg_threadwait(gg, &gg->pth_dcc); - - gg_netlog(gg, "gg_mainthread(%x): Server Thread Ending", gg); - return; -} - -//////////////////////////////////////////////////////////// -// Change status function -void gg_broadcastnewstatus(GGPROTO *gg, int newStatus) -{ - int oldStatus; - - EnterCriticalSection(&gg->modemsg_mutex); - oldStatus = gg->proto.m_iStatus; - if(oldStatus == newStatus) - { - LeaveCriticalSection(&gg->modemsg_mutex); - return; - } - gg->proto.m_iStatus = newStatus; - LeaveCriticalSection(&gg->modemsg_mutex); - - ProtoBroadcastAck(GG_PROTO, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldStatus, newStatus); - - gg_netlog(gg, "gg_broadcastnewstatus(): Broadcast new status: %d.", newStatus); -} - -//////////////////////////////////////////////////////////// -// When contact is deleted -int gg_contactdeleted(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - HANDLE hContact = (HANDLE) wParam; - uin_t uin; int type; - DBVARIANT dbv; - - uin = (uin_t)DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0); - type = DBGetContactSettingByte(hContact, GG_PROTO, "ChatRoom", 0); - - // Terminate conference if contact is deleted - if(type && !DBGetContactSetting(hContact, GG_PROTO, "ChatRoomID", &dbv) && gg->gc_enabled) - { - GCDEST gcdest = {GG_PROTO, dbv.pszVal, GC_EVENT_CONTROL}; - GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; - GGGC *chat = gg_gc_lookup(gg, dbv.pszVal); - - gg_netlog(gg, "gg_gc_event(): Terminating chat %x, id %s from contact list...", chat, dbv.pszVal); - if(chat) - { - // Destroy chat entry - free(chat->recipients); - list_remove(&gg->chats, chat, 1); - // Terminate chat window / shouldn't cascade entry is deleted - CallServiceSync(MS_GC_EVENT, SESSION_OFFLINE, (LPARAM)&gcevent); - CallServiceSync(MS_GC_EVENT, SESSION_TERMINATE, (LPARAM)&gcevent); - } - - DBFreeVariant(&dbv); - return 0; - } - - if(uin && gg_isonline(gg)) - { - EnterCriticalSection(&gg->sess_mutex); - gg_remove_notify_ex(gg->sess, uin, GG_USER_NORMAL); - LeaveCriticalSection(&gg->sess_mutex); - } - - return 0; -} - -//////////////////////////////////////////////////////////// -// When db settings changed -int gg_dbsettingchanged(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam; - HANDLE hContact = (HANDLE) wParam; - char *szProto = NULL; - - // Check if the contact is NULL or we are not online - if (!gg_isonline(gg)) - return 0; - - // If contact has been blocked - if (!strcmp(cws->szModule, GG_PROTO) && !strcmp(cws->szSetting, GG_KEY_BLOCK)) - { - gg_notifyuser(gg, hContact, 1); - return 0; - } - - // Contact is being renamed - if(gg->gc_enabled && !strcmp(cws->szModule, GG_PROTO) && !strcmp(cws->szSetting, GG_KEY_NICK) - && cws->value.pszVal) - { - // Groupchat window contact is being renamed - DBVARIANT dbv; - int type = DBGetContactSettingByte(hContact, GG_PROTO, "ChatRoom", 0); - if(type && !DBGetContactSetting(hContact, GG_PROTO, "ChatRoomID", &dbv)) - { - // Most important... check redundancy (fucking cascading) - static int cascade = 0; - if (!cascade && dbv.pszVal) - { - GCDEST gcdest = {GG_PROTO, dbv.pszVal, GC_EVENT_CHANGESESSIONAME}; - GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; - gcevent.pszText = cws->value.pszVal; - gg_netlog(gg, "gg_dbsettingchanged(): Conference %s was renamed to %s.", dbv.pszVal, cws->value.pszVal); - // Mark cascading - /* FIXME */ cascade = 1; - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); - /* FIXME */ cascade = 0; - } - DBFreeVariant(&dbv); - } - else - // Change contact name on all chats - gg_gc_changenick(gg, hContact, cws->value.pszVal); - } - - // Contact list changes - if (!strcmp(cws->szModule, "CList")) - { - // If name changed... change nick - if (!strcmp(cws->szSetting, "MyHandle") && cws->value.type == DBVT_ASCIIZ && cws->value.pszVal) - DBWriteContactSettingString(hContact, GG_PROTO, GG_KEY_NICK, cws->value.pszVal); - - // If not on list changed - if (!strcmp(cws->szSetting, "NotOnList")) - { - if(DBGetContactSettingByte(hContact, "CList", "Hidden", 0)) - return 0; - // Notify user normally this time if added to the list permanently - if(cws->value.type == DBVT_DELETED || (cws->value.type == DBVT_BYTE && cws->value.bVal == 0)) - gg_notifyuser(gg, hContact, 1); - } - } - return 0; -} - -//////////////////////////////////////////////////////////// -// All users set offline -void gg_setalloffline(GGPROTO *gg) -{ - HANDLE hContact; - char *szProto; - - gg_netlog(gg, "gg_setalloffline(): Setting buddies offline"); - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_STATUS, ID_STATUS_OFFLINE); - hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); - while (hContact) - { - szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if(szProto != NULL && !strcmp(szProto, GG_PROTO)) - { - DBWriteContactSettingWord(hContact, GG_PROTO, GG_KEY_STATUS, ID_STATUS_OFFLINE); - // Clear IP and port settings - DBDeleteContactSetting(hContact, GG_PROTO, GG_KEY_CLIENTIP); - DBDeleteContactSetting(hContact, GG_PROTO, GG_KEY_CLIENTPORT); - // Delete status descr - DBDeleteContactSetting(hContact, "CList", GG_KEY_STATUSDESCR); - } - hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); - } -#ifdef DEBUGMODE - gg_netlog(gg, "gg_setalloffline(): End"); -#endif -} - -//////////////////////////////////////////////////////////// -// All users set offline -void gg_notifyuser(GGPROTO *gg, HANDLE hContact, int refresh) -{ - uin_t uin; - if (!hContact) return; - if (gg_isonline(gg) && (uin = (uin_t)DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0))) - { - // Check if user should be invisible - // Or be blocked ? - if ((DBGetContactSettingWord(hContact, GG_PROTO, GG_KEY_APPARENT, (WORD) ID_STATUS_ONLINE) == ID_STATUS_OFFLINE) || - DBGetContactSettingByte(hContact, "CList", "NotOnList", 0)) - { - EnterCriticalSection(&gg->sess_mutex); - if (refresh) - { - gg_remove_notify_ex(gg->sess, uin, GG_USER_NORMAL); - gg_remove_notify_ex(gg->sess, uin, GG_USER_BLOCKED); - } - - gg_add_notify_ex(gg->sess, uin, GG_USER_OFFLINE); - LeaveCriticalSection(&gg->sess_mutex); - } - else if (DBGetContactSettingByte(hContact, GG_PROTO, GG_KEY_BLOCK, 0)) - { - EnterCriticalSection(&gg->sess_mutex); - if (refresh) - { - gg_remove_notify_ex(gg->sess, uin, GG_USER_OFFLINE); - } - - gg_add_notify_ex(gg->sess, uin, GG_USER_BLOCKED); - LeaveCriticalSection(&gg->sess_mutex); - } - else - { - EnterCriticalSection(&gg->sess_mutex); - if (refresh) - { - gg_remove_notify_ex(gg->sess, uin, GG_USER_BLOCKED); - } - - gg_add_notify_ex(gg->sess, uin, GG_USER_NORMAL); - LeaveCriticalSection(&gg->sess_mutex); - } - } -} -void gg_notifyall(GGPROTO *gg) -{ - HANDLE hContact; - char *szProto; - int count = 0, cc = 0; - uin_t *uins; - char *types; - - gg_netlog(gg, "gg_notifyall(): Subscribing notification to all users"); - // Readup count - hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); - while (hContact) - { - szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if (szProto != NULL && !strcmp(szProto, GG_PROTO)) count ++; - hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); - } - - // Readup list - /* FIXME: If we have nothing on the list but we omit gg_notify_ex we have problem with receiving any contacts */ - if (count == 0) - { - if(gg_isonline(gg)) - { - EnterCriticalSection(&gg->sess_mutex); - gg_notify_ex(gg->sess, NULL, NULL, 0); - LeaveCriticalSection(&gg->sess_mutex); - } - return; - } - uins = calloc(sizeof(uin_t), count); - types = calloc(sizeof(char), count); - - hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); - while (hContact && cc < count) - { - szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if (szProto != NULL && !strcmp(szProto, GG_PROTO) && (uins[cc] = DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0))) - { - if ((DBGetContactSettingWord(hContact, GG_PROTO, GG_KEY_APPARENT, (WORD) ID_STATUS_ONLINE) == ID_STATUS_OFFLINE) || - DBGetContactSettingByte(hContact, "CList", "NotOnList", 0)) - types[cc] = GG_USER_OFFLINE; - else if (DBGetContactSettingByte(hContact, GG_PROTO, GG_KEY_BLOCK, 0)) - types[cc] = GG_USER_BLOCKED; - else - types[cc] = GG_USER_NORMAL; - cc ++; - } - hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); - } - if (cc < count) count = cc; - - // Send notification - if (gg_isonline(gg)) - { - EnterCriticalSection(&gg->sess_mutex); - gg_notify_ex(gg->sess, uins, types, count); - LeaveCriticalSection(&gg->sess_mutex); - } - - // Free variables - free(uins); free(types); -} - -//////////////////////////////////////////////////////////// -// Get contact by uin -HANDLE gg_getcontact(GGPROTO *gg, uin_t uin, int create, int inlist, char *szNick) -{ - HANDLE hContact; - char *szProto; - - // Look for contact in DB - hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); - while (hContact) - { - szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if(szProto != NULL && !strcmp(szProto, GG_PROTO)) - { - if ((uin_t)DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0) == uin - && DBGetContactSettingByte(hContact, GG_PROTO, "ChatRoom", 0) == 0) - { - if(inlist) - { - DBDeleteContactSetting(hContact, "CList", "NotOnList"); - DBDeleteContactSetting(hContact, "CList", "Hidden"); - } - return hContact; - } - } - hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); - } - if (!create) return NULL; - - hContact = (HANDLE) CallService(MS_DB_CONTACT_ADD, 0, 0); - - if (!hContact) - { - gg_netlog(gg, "gg_getcontact(): Failed to create Gadu-Gadu contact %s", szNick); - return NULL; - } - - if(CallService(MS_PROTO_ADDTOCONTACT, (WPARAM) hContact, (LPARAM) GG_PROTO) != 0) - { - // For some reason we failed to register the protocol for this contact - CallService(MS_DB_CONTACT_DELETE, (WPARAM) hContact, 0); - gg_netlog(gg, "Failed to register GG contact %d", uin); - return NULL; - } - - gg_netlog(gg, "gg_getcontact(): Added buddy: %d", uin); - if (!inlist) - { - DBWriteContactSettingByte(hContact, "CList", "NotOnList", 1); - //DBWriteContactSettingByte(hContact, "CList", "Hidden", 1); - } - - DBWriteContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, (DWORD) uin); - DBWriteContactSettingWord(hContact, GG_PROTO, GG_KEY_STATUS, ID_STATUS_OFFLINE); - - // If nick specified use it - if(szNick) - DBWriteContactSettingString(hContact, GG_PROTO, GG_KEY_NICK, szNick); - else if(gg_isonline(gg)) - { - gg_pubdir50_t req; - - // Search for that nick - if(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH)) - { - // Add uin and search it - gg_pubdir50_add(req, GG_PUBDIR50_UIN, ditoa(uin)); - gg_pubdir50_seq_set(req, GG_SEQ_GETNICK); - EnterCriticalSection(&gg->sess_mutex); - gg_pubdir50(gg->sess, req); - LeaveCriticalSection(&gg->sess_mutex); - gg_pubdir50_free(req); - DBWriteContactSettingString(hContact, GG_PROTO, GG_KEY_NICK, ditoa(uin)); - gg_netlog(gg, "gg_getcontact(): Search for nick on uin: %d", uin); - } - } - - // Add to notify list and pull avatar for the new contact - if(gg_isonline(gg)) - { - PROTO_AVATAR_INFORMATIONT pai = {0}; - - EnterCriticalSection(&gg->sess_mutex); - gg_add_notify_ex(gg->sess, uin, (char)(inlist ? GG_USER_NORMAL : GG_USER_OFFLINE)); - LeaveCriticalSection(&gg->sess_mutex); - - pai.cbSize = sizeof(pai); - pai.hContact = hContact; - gg_getavatarinfo(gg, (WPARAM)GAIF_FORCE, (LPARAM)&pai); - - // Change status of the contact with our own UIN (if got yourself added to the contact list) - if (DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0) == uin) - { - char *szMsg; - EnterCriticalSection(&gg->modemsg_mutex); - szMsg = mir_strdup(gg_getstatusmsg(gg, gg->proto.m_iStatus)); - LeaveCriticalSection(&gg->modemsg_mutex); - gg_changecontactstatus(gg, uin, status_m2gg(gg, gg->proto.m_iStatus, szMsg != NULL), szMsg, 0, 0, 0, 0); - mir_free(szMsg); - } - } - - // TODO server side list & add buddy - return hContact; -} - -//////////////////////////////////////////////////////////// -// Status conversion -int status_m2gg(GGPROTO *gg, int status, int descr) -{ - // check frends only - int mask = DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_FRIENDSONLY, GG_KEYDEF_FRIENDSONLY) ? GG_STATUS_FRIENDS_MASK : 0; - - if(descr) - { - switch(status) - { - case ID_STATUS_OFFLINE: - return GG_STATUS_NOT_AVAIL_DESCR | mask; - - case ID_STATUS_ONLINE: - return GG_STATUS_AVAIL_DESCR | mask; - - case ID_STATUS_AWAY: - return GG_STATUS_BUSY_DESCR | mask; - - case ID_STATUS_DND: - return GG_STATUS_DND_DESCR | mask; - - case ID_STATUS_FREECHAT: - return GG_STATUS_FFC_DESCR | mask; - - case ID_STATUS_INVISIBLE: - return GG_STATUS_INVISIBLE_DESCR | mask; - - default: - return GG_STATUS_BUSY_DESCR | mask; - } - } - else - { - switch(status) - { - case ID_STATUS_OFFLINE: - return GG_STATUS_NOT_AVAIL | mask; - - case ID_STATUS_ONLINE: - return GG_STATUS_AVAIL | mask; - - case ID_STATUS_AWAY: - return GG_STATUS_BUSY | mask; - - case ID_STATUS_DND: - return GG_STATUS_DND | mask; - - case ID_STATUS_FREECHAT: - return GG_STATUS_FFC | mask; - - case ID_STATUS_INVISIBLE: - return GG_STATUS_INVISIBLE | mask; - - default: - return GG_STATUS_BUSY | mask; - } - } -} -int status_gg2m(GGPROTO *gg, int status) -{ - // ignore additional flags - status = GG_S(status); - - // when user has status description but is offline (show it invisible) - if(status == GG_STATUS_NOT_AVAIL_DESCR && DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWINVISIBLE, GG_KEYDEF_SHOWINVISIBLE)) - return ID_STATUS_INVISIBLE; - - // rest of cases - switch(status) - { - case GG_STATUS_NOT_AVAIL: - case GG_STATUS_NOT_AVAIL_DESCR: - return ID_STATUS_OFFLINE; - - case GG_STATUS_AVAIL: - case GG_STATUS_AVAIL_DESCR: - return ID_STATUS_ONLINE; - - case GG_STATUS_BUSY: - case GG_STATUS_BUSY_DESCR: - return ID_STATUS_AWAY; - - case GG_STATUS_DND: - case GG_STATUS_DND_DESCR: - return ID_STATUS_DND; - - case GG_STATUS_FFC: - case GG_STATUS_FFC_DESCR: - return ID_STATUS_FREECHAT; - - case GG_STATUS_INVISIBLE: - case GG_STATUS_INVISIBLE_DESCR: - return ID_STATUS_INVISIBLE; - - case GG_STATUS_BLOCKED: - return ID_STATUS_NA; - - default: - return ID_STATUS_OFFLINE; - } -} - -//////////////////////////////////////////////////////////// -// Called when contact status is changed -void gg_changecontactstatus(GGPROTO *gg, uin_t uin, int status, const char *idescr, int time, uint32_t remote_ip, uint16_t remote_port, uint32_t version) -{ - HANDLE hContact = gg_getcontact(gg, uin, 0, 0, NULL); - - // Check if contact is on list - if (!hContact) return; - - // Write contact status - DBWriteContactSettingWord(hContact, GG_PROTO, GG_KEY_STATUS, (WORD)status_gg2m(gg, status)); - - // Check if there's description and if it's not empty - if(idescr && *idescr) - { - gg_netlog(gg, "gg_changecontactstatus(): Saving for %d status descr \"%s\".", uin, idescr); - DBWriteContactSettingString(hContact, "CList", GG_KEY_STATUSDESCR, idescr); - } - else - // Remove status if there's nothing - DBDeleteContactSetting(hContact, "CList", GG_KEY_STATUSDESCR); - - // Store contact ip and port - if(remote_ip) DBWriteContactSettingDword(hContact, GG_PROTO, GG_KEY_CLIENTIP, (DWORD) swap32(remote_ip)); - if(remote_port) DBWriteContactSettingWord(hContact, GG_PROTO, GG_KEY_CLIENTPORT, (WORD) remote_port); - if(version) - { - char sversion[48]; - DBWriteContactSettingDword(hContact, GG_PROTO, GG_KEY_CLIENTVERSION, (DWORD) version); - mir_snprintf(sversion, sizeof(sversion), "%sGadu-Gadu %s", (version & 0x00ffffff) > 0x2b ? "Nowe " : "", gg_version2string(version)); - DBWriteContactSettingString(hContact, GG_PROTO, "MirVer", sversion); - } -} - -//////////////////////////////////////////////////////////// -// Returns GG client version string from packet version -const char *gg_version2string(int v) -{ - const char *pstr = "???"; - v &= 0x00ffffff; - switch(v) - { - case 0x2e: - pstr = "8.0 build 8283"; break; - case 0x2d: - pstr = "8.0 build 4881"; break; - case 0x2b: - pstr = "< 8.0"; break; - case 0x2a: - pstr = "7.7 build 3315"; break; - case 0x29: - pstr = "7.6 build 1688"; break; - case 0x28: - pstr = "7.5 build 2201"; break; - case 0x27: - pstr = "7.0 build 22"; break; - case 0x26: - pstr = "7.0 build 20"; break; - case 0x25: - pstr = "7.0 build 1"; break; - case 0x24: - pstr = "6.1 (155) / 7.6 (1359)"; break; - case 0x22: - pstr = "6.0 build 140"; break; - case 0x21: - pstr = "6.0 build 133"; break; - case 0x20: - pstr = "6.0b"; break; - case 0x1e: - pstr = "5.7b build 121"; break; - case 0x1c: - pstr = "5.7b"; break; - case 0x1b: - pstr = "5.0.5"; break; - case 0x19: - pstr = "5.0.3"; break; - case 0x18: - pstr = "5.0.0-1"; break; - case 0x17: - pstr = "4.9.2"; break; - case 0x16: - pstr = "4.9.1"; break; - case 0x15: - pstr = "4.8.9"; break; - case 0x14: - pstr = "4.8.1-3"; break; - case 0x11: - pstr = "4.6.1-10"; break; - case 0x10: - pstr = "4.5.15-22"; break; - case 0x0f: - pstr = "4.5.12"; break; - case 0x0b: - pstr = "4.0.25-30"; break; - default: - if (v < 0x0b) - pstr = "< 4.0.25"; - else if (v > 0x2e) - pstr = ">= 8.0"; - break; - } - return pstr; -} diff --git a/protocols/Gadu-Gadu/core.cpp b/protocols/Gadu-Gadu/core.cpp new file mode 100644 index 0000000000..735b3536bc --- /dev/null +++ b/protocols/Gadu-Gadu/core.cpp @@ -0,0 +1,1765 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// Copyright (c) 2009-2012 Bartosz Białek +// +// 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 "gg.h" +#include +#include + +//////////////////////////////////////////////////////////// +// Swap bits in DWORD +uint32_t swap32(uint32_t x) +{ + return (uint32_t) + (((x & (uint32_t) 0x000000ffU) << 24) | + ((x & (uint32_t) 0x0000ff00U) << 8) | + ((x & (uint32_t) 0x00ff0000U) >> 8) | + ((x & (uint32_t) 0xff000000U) >> 24)); +} + +//////////////////////////////////////////////////////////// +// Is online function + +int GGPROTO::isonline() +{ + mir_cslock lck(sess_mutex); + return (sess != NULL); +} + +//////////////////////////////////////////////////////////// +// Send disconnect request and wait for server thread to die +void GGPROTO::disconnect() +{ + // If main loop then send disconnect request + if (isonline()) + { + // Fetch proper status msg + char *szMsg = NULL; + + // Loadup status + if (db_get_b(NULL, m_szModuleName, GG_KEY_LEAVESTATUSMSG, GG_KEYDEF_LEAVESTATUSMSG)) + { + DBVARIANT dbv; + switch (db_get_w(NULL, m_szModuleName, GG_KEY_LEAVESTATUS, GG_KEYDEF_LEAVESTATUS)) { + case ID_STATUS_ONLINE: + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(modemsg.online); + LeaveCriticalSection(&modemsg_mutex); + if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_ONLINE, "Default"), &dbv, DBVT_ASCIIZ)) { + if (dbv.pszVal && *(dbv.pszVal)) + szMsg = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + break; + case ID_STATUS_AWAY: + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(modemsg.away); + LeaveCriticalSection(&modemsg_mutex); + if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_AWAY, "Default"), &dbv, DBVT_ASCIIZ)) { + if (dbv.pszVal && *(dbv.pszVal)) + szMsg = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + break; + case ID_STATUS_DND: + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(modemsg.dnd); + LeaveCriticalSection(&modemsg_mutex); + if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_DND, "Default"), &dbv, DBVT_ASCIIZ)) { + if (dbv.pszVal && *(dbv.pszVal)) + szMsg = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + break; + case ID_STATUS_FREECHAT: + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(modemsg.freechat); + LeaveCriticalSection(&modemsg_mutex); + if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_FREECHAT, "Default"), &dbv, DBVT_ASCIIZ)) { + if (dbv.pszVal && *(dbv.pszVal)) + szMsg = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + break; + case ID_STATUS_INVISIBLE: + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(modemsg.invisible); + LeaveCriticalSection(&modemsg_mutex); + if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_INVISIBLE, "Default"), &dbv, DBVT_ASCIIZ)) { + if (dbv.pszVal && *(dbv.pszVal)) + szMsg = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + break; + default: + // Set last status + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(getstatusmsg(m_iStatus)); + LeaveCriticalSection(&modemsg_mutex); + } + } + + EnterCriticalSection(&sess_mutex); + // Check if it has message + if (szMsg) + { + gg_change_status_descr(sess, GG_STATUS_NOT_AVAIL_DESCR, szMsg); + mir_free(szMsg); + // Wait for disconnection acknowledge + } + else + { + gg_change_status(sess, GG_STATUS_NOT_AVAIL); + // Send logoff immediately + gg_logoff(sess); + } + LeaveCriticalSection(&sess_mutex); + } + // Else cancel connection attempt + else if (sock) + closesocket(sock); +} + +//////////////////////////////////////////////////////////// +// DNS lookup function +uint32_t gg_dnslookup(GGPROTO *gg, char *host) +{ + uint32_t ip; + struct hostent *he; + + ip = inet_addr(host); + if (ip != INADDR_NONE) + { +#ifdef DEBUGMODE + gg->netlog("gg_dnslookup(): Parameter \"%s\" is already IP number.", host); +#endif + return ip; + } + he = gethostbyname(host); + if (he) + { + ip = *(uint32_t *) he->h_addr_list[0]; +#ifdef DEBUGMODE + gg->netlog("gg_dnslookup(): Parameter \"%s\" was resolved to %d.%d.%d.%d.", host, + LOBYTE(LOWORD(ip)), HIBYTE(LOWORD(ip)), LOBYTE(HIWORD(ip)), HIBYTE(HIWORD(ip))); +#endif + return ip; + } + gg->netlog("gg_dnslookup(): Cannot resolve hostname \"%s\".", host); + return 0; +} + +//////////////////////////////////////////////////////////// +// Host list decoder +typedef struct +{ + char hostname[128]; + int port; +} GGHOST; +#define ISHOSTALPHA(a) (((a) >= '0' && (a) <= '9') || ((a) >= 'a' && (a) <= 'z') || (a) == '.' || (a) == '-') +int gg_decodehosts(char *var, GGHOST *hosts, int max) +{ + int hp = 0; + char *hostname = NULL; + char *portname = NULL; + + while(var && *var && hp < max) + { + if (ISHOSTALPHA(*var)) + { + hostname = var; + + while(var && *var && ISHOSTALPHA(*var)) var ++; + + if (var && *var == ':' && var++ && *var && isdigit(*var)) + { + *(var - 1) = 0; + portname = var; + while(var && *var && isdigit(*var)) var++; + if (*var) { *var = 0; var ++; } + } + else + if (*var) { *var = 0; var ++; } + + // Insert new item + hosts[hp].hostname[127] = 0; + strncpy(hosts[hp].hostname, hostname, 127); + hosts[hp].port = portname ? atoi(portname) : 443; + hp ++; + + // Zero the names + hostname = NULL; + portname = NULL; + } + else + var ++; + } + return hp; +} + +//////////////////////////////////////////////////////////// +// Main connection session thread +void __cdecl GGPROTO::mainthread(void *) +{ + // Miranda variables + NETLIBUSERSETTINGS nlus = {0}; + DBVARIANT dbv; + // Gadu-Gadu variables + gg_login_params p = {0}; + gg_event *e; + // Host cycling variables + int hostnum = 0, hostcount = 0; + GGHOST hosts[64]; + // Gadu-gadu login errors + static const struct tagReason { int type; TCHAR *str; } reason[] = { + { GG_FAILURE_RESOLVING, LPGENT("Miranda was unable to resolve the name of the Gadu-Gadu server to its numeric address.") }, + { GG_FAILURE_CONNECTING, LPGENT("Miranda was unable to make a connection with a server. It is likely that the server is down, in which case you should wait for a while and try again later.") }, + { GG_FAILURE_INVALID, LPGENT("Received invalid server response.") }, + { GG_FAILURE_READING, LPGENT("The connection with the server was abortively closed during the connection attempt. You may have lost your local network connection.") }, + { GG_FAILURE_WRITING, LPGENT("The connection with the server was abortively closed during the connection attempt. You may have lost your local network connection.") }, + { GG_FAILURE_PASSWORD, LPGENT("Your Gadu-Gadu number and password combination was rejected by the Gadu-Gadu server. Please check login details at M->Options->Network->Gadu-Gadu and try again.") }, + { GG_FAILURE_404, LPGENT("Connecting to Gadu-Gadu hub failed.") }, + { GG_FAILURE_TLS, LPGENT("Cannot establish secure connection.") }, + { GG_FAILURE_NEED_EMAIL, LPGENT("Server disconnected asking you for changing your e-mail.") }, + { GG_FAILURE_INTRUDER, LPGENT("Too many login attempts with invalid password.") }, + { GG_FAILURE_UNAVAILABLE, LPGENT("Gadu-Gadu servers are now down. Try again later.") }, + { 0, LPGENT("Unknown") } + }; + time_t logonTime = 0; + time_t timeDeviation = db_get_w(NULL, m_szModuleName, GG_KEY_TIMEDEVIATION, GG_KEYDEF_TIMEDEVIATION); + int gg_failno = 0; + + netlog("gg_mainthread(%x): Server Thread Starting", this); +#ifdef DEBUGMODE + gg_debug_level = GG_DEBUG_NET | GG_DEBUG_TRAFFIC | GG_DEBUG_FUNCTION | GG_DEBUG_MISC; +#else + gg_debug_level = 0; +#endif + + // Broadcast that service is connecting + broadcastnewstatus(ID_STATUS_CONNECTING); + + // Client version and misc settings + p.client_version = GG_DEFAULT_CLIENT_VERSION; + p.protocol_version = GG_DEFAULT_PROTOCOL_VERSION; + p.protocol_features = GG_FEATURE_DND_FFC | GG_FEATURE_UNKNOWN_100 | GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | GG_FEATURE_TYPING_NOTIFICATION | GG_FEATURE_MULTILOGON; + p.encoding = GG_ENCODING_CP1250; + p.status_flags = GG_STATUS_FLAG_UNKNOWN; + if (db_get_b(NULL, m_szModuleName, GG_KEY_SHOWLINKS, GG_KEYDEF_SHOWLINKS)) + p.status_flags |= GG_STATUS_FLAG_SPAM; + + // Use audio + /* p.has_audio = 1; */ + + // Use async connections + /* p.async = 1; */ + + // Send Era Omnix info if set + p.era_omnix = db_get_b(NULL, m_szModuleName, "EraOmnix", 0); + + // Setup proxy + nlus.cbSize = sizeof(nlus); + if (CallService(MS_NETLIB_GETUSERSETTINGS, (WPARAM)netlib, (LPARAM)&nlus)) + { + if (nlus.useProxy) + netlog("gg_mainthread(%x): Using proxy %s:%d.", this, nlus.szProxyServer, nlus.wProxyPort); + gg_proxy_enabled = nlus.useProxy; + gg_proxy_host = nlus.szProxyServer; + gg_proxy_port = nlus.wProxyPort; + if (nlus.useProxyAuth) + { + gg_proxy_username = nlus.szProxyAuthUser; + gg_proxy_password = nlus.szProxyAuthPassword; + } + else + gg_proxy_username = gg_proxy_password = NULL; + } + else + { + netlog("gg_mainthread(%x): Failed loading proxy settings.", this); + gg_proxy_enabled = 0; + } + + // Check out manual host setting + if (db_get_b(NULL, m_szModuleName, GG_KEY_MANUALHOST, GG_KEYDEF_MANUALHOST)) + { + if (!db_get_s(NULL, m_szModuleName, GG_KEY_SERVERHOSTS, &dbv, DBVT_ASCIIZ)) + { + hostcount = gg_decodehosts(dbv.pszVal, hosts, 64); + DBFreeVariant(&dbv); + } + } + + // Readup password + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) + { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + p.password = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + else + { + netlog("gg_mainthread(%x): No password specified. Exiting.", this); + broadcastnewstatus(ID_STATUS_OFFLINE); + return; + } + + // Readup number + if (!(p.uin = db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0))) + { + netlog("gg_mainthread(%x): No Gadu-Gadu number specified. Exiting.", this); + broadcastnewstatus(ID_STATUS_OFFLINE); + mir_free(p.password); + return; + } + + // Readup SSL/TLS setting + if (p.tls = db_get_b(NULL, m_szModuleName, GG_KEY_SSLCONN, GG_KEYDEF_SSLCONN)) + netlog("gg_mainthread(%x): Using TLS/SSL for connections.", this); + + // Gadu-Gadu accepts image sizes upto 255 + p.image_size = 255; + + ////////////////////////////// DCC STARTUP ///////////////////////////// + // Uin is ok so startup dcc if not started already + if (!dcc) + { + hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + dccstart(); + + // Wait for DCC +#ifdef DEBUGMODE + netlog("gg_mainthread(%x): Waiting DCC service to start...", this); +#endif + while (WaitForSingleObjectEx(hEvent, INFINITE, TRUE) != WAIT_OBJECT_0); + CloseHandle(hEvent); hEvent = NULL; + } + // Check if dcc is running and setup forwarding port + if (dcc && db_get_b(NULL, m_szModuleName, GG_KEY_FORWARDING, GG_KEYDEF_FORWARDING)) + { + if (!db_get_s(NULL, m_szModuleName, GG_KEY_FORWARDHOST, &dbv, DBVT_ASCIIZ)) + { + if (!(p.external_addr = gg_dnslookup(this, dbv.pszVal))) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("External direct connections hostname %S is invalid. Disabling external host forwarding."), dbv.pszVal); + showpopup(m_tszUserName, error, GG_POPUP_WARNING | GG_POPUP_ALLOW_MSGBOX); + } + else + netlog("gg_mainthread(%x): Loading forwarding host %s and port %d.", dbv.pszVal, p.external_port, this); + if (p.external_addr) p.external_port = db_get_w(NULL, m_szModuleName, GG_KEY_FORWARDPORT, GG_KEYDEF_FORWARDPORT); + DBFreeVariant(&dbv); + } + } + // Setup client port + if (dcc) p.client_port = dcc->port; + +retry: + // Loadup startup status & description + EnterCriticalSection(&modemsg_mutex); + p.status_descr = mir_strdup(getstatusmsg(m_iDesiredStatus)); + p.status = status_m2gg(m_iDesiredStatus, p.status_descr != NULL); + + netlog("gg_mainthread(%x): Connecting with number %d, status %d and description \"%s\".", this, p.uin, m_iDesiredStatus, + p.status_descr ? p.status_descr : ""); + LeaveCriticalSection(&modemsg_mutex); + + // Check manual hosts + if (hostnum < hostcount) + { + if (!(p.server_addr = gg_dnslookup(this, hosts[hostnum].hostname))) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("Server hostname %S is invalid. Using default hostname provided by the network."), hosts[hostnum].hostname); + showpopup(m_tszUserName, error, GG_POPUP_WARNING | GG_POPUP_ALLOW_MSGBOX); + } + else + { + p.server_port = hosts[hostnum].port; + netlog("gg_mainthread(%x): Connecting to manually specified host %s (%d.%d.%d.%d) and port %d.", this, + hosts[hostnum].hostname, LOBYTE(LOWORD(p.server_addr)), HIBYTE(LOWORD(p.server_addr)), + LOBYTE(HIWORD(p.server_addr)), HIBYTE(HIWORD(p.server_addr)), p.server_port); + } + } + else + p.server_port = p.server_addr = 0; + + // Send login request + if (!(sess = gg_login(&p, &sock, &gg_failno))) + { + broadcastnewstatus(ID_STATUS_OFFLINE); + // Check if connection attempt wasn't cancelled by the user + if (m_iDesiredStatus != ID_STATUS_OFFLINE) + { + TCHAR error[128], *perror = NULL; + // Lookup for error desciption + if (errno == EACCES) { + for (int i = 0; reason[i].type; i++) if (reason[i].type == gg_failno) { + perror = TranslateTS(reason[i].str); + break; + } + } + if (!perror) { + mir_sntprintf(error, SIZEOF(error), TranslateT("Connection cannot be established because of error:\n\t%s"), _tcserror(errno)); + perror = error; + } + netlog("gg_mainthread(%x): %s", this, perror); + if (db_get_b(NULL, m_szModuleName, GG_KEY_SHOWCERRORS, GG_KEYDEF_SHOWCERRORS)) + showpopup(m_tszUserName, perror, GG_POPUP_ERROR | GG_POPUP_ALLOW_MSGBOX | GG_POPUP_ONCE); + + // Check if we should reconnect + if ((gg_failno >= GG_FAILURE_RESOLVING && gg_failno != GG_FAILURE_PASSWORD && gg_failno != GG_FAILURE_INTRUDER && gg_failno != GG_FAILURE_UNAVAILABLE) + && errno == EACCES + && (db_get_b(NULL, m_szModuleName, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT) || (hostnum < hostcount - 1))) + { + DWORD dwInterval = db_get_b(NULL, m_szModuleName, GG_KEY_RECONNINTERVAL, GG_KEYDEF_RECONNINTERVAL), dwResult; + BOOL bRetry = TRUE; + + hConnStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + dwResult = WaitForSingleObjectEx(hConnStopEvent, dwInterval, TRUE); + if ((dwResult == WAIT_OBJECT_0 && m_iDesiredStatus == ID_STATUS_OFFLINE) + || (dwResult == WAIT_IO_COMPLETION && Miranda_Terminated())) + bRetry = FALSE; + CloseHandle(hConnStopEvent); + hConnStopEvent = NULL; + + // Reconnect to the next server on the list + if (bRetry) + { + if (hostnum < hostcount - 1) hostnum++; + mir_free(p.status_descr); + broadcastnewstatus(ID_STATUS_CONNECTING); + goto retry; + } + } + // We cannot do more about this + EnterCriticalSection(&modemsg_mutex); + m_iDesiredStatus = ID_STATUS_OFFLINE; + LeaveCriticalSection(&modemsg_mutex); + } + else + netlog("gg_mainthread(%x)): Connection attempt cancelled by the user.", this); + } + else + { + // Successfully connected + logonTime = time(NULL); + db_set_w(NULL, m_szModuleName, GG_KEY_LOGONTIME, logonTime); + EnterCriticalSection(&sess_mutex); + sess = sess; + LeaveCriticalSection(&sess_mutex); + // Subscribe users status notifications + notifyall(); + // Set startup status + if (m_iDesiredStatus != status_gg2m(p.status)) + refreshstatus(m_iDesiredStatus); + else + { + broadcastnewstatus(m_iDesiredStatus); + // Change status of the contact with our own UIN (if got yourself added to the contact list) + changecontactstatus(p.uin, p.status, p.status_descr, 0, 0, 0, 0); + } + if (check_first_conn) // First connection to the account + { + // Start search for user data + GetInfo(NULL, 0); + // Fetch user avatar + getUserAvatar(); + check_first_conn = 0; + } + } + + ////////////////////////////////////////////////////////////////////////////////// + // Main loop + while(isonline()) + { + // Connection broken/closed + if (!(e = gg_watch_fd(sess))) + { + netlog("gg_mainthread(%x): Connection closed.", this); + EnterCriticalSection(&sess_mutex); + gg_free_session(sess); + sess = NULL; + LeaveCriticalSection(&sess_mutex); + break; + } + else + netlog("gg_mainthread(%x): Event: %s", this, ggdebug_eventtype(e)); + + switch(e->type) + { + // Client connected + case GG_EVENT_CONN_SUCCESS: + // Nada + break; + + // Client disconnected or connection failure + case GG_EVENT_CONN_FAILED: + case GG_EVENT_DISCONNECT: + EnterCriticalSection(&sess_mutex); + gg_free_session(sess); + sess = NULL; + LeaveCriticalSection(&sess_mutex); + break; + + // Client allowed to disconnect + case GG_EVENT_DISCONNECT_ACK: + // Send logoff + gg_logoff(sess); + break; + + // Received ackowledge + case GG_EVENT_ACK: + if (e->event.ack.seq && e->event.ack.recipient) + { + ProtoBroadcastAck(m_szModuleName, getcontact((DWORD)e->event.ack.recipient, 0, 0, NULL), + ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE) e->event.ack.seq, 0); + } + break; + + // Statuslist notify (deprecated) + case GG_EVENT_NOTIFY: + case GG_EVENT_NOTIFY_DESCR: + { + struct gg_notify_reply *n; + + n = (e->type == GG_EVENT_NOTIFY) ? e->event.notify : e->event.notify_descr.notify; + + for (; n->uin; n++) + { + char *descr = (e->type == GG_EVENT_NOTIFY_DESCR) ? e->event.notify_descr.descr : NULL; + changecontactstatus(n->uin, n->status, descr, 0, n->remote_ip, n->remote_port, n->version); + } + break; + } + // Statuslist notify (version >= 6.0) + case GG_EVENT_NOTIFY60: + { + uin_t uin = (uin_t)db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0); + int i; + for(i = 0; e->event.notify60[i].uin; i++) { + if (e->event.notify60[i].uin == uin) continue; + changecontactstatus(e->event.notify60[i].uin, e->event.notify60[i].status, e->event.notify60[i].descr, + e->event.notify60[i].time, e->event.notify60[i].remote_ip, e->event.notify60[i].remote_port, + e->event.notify60[i].version); + requestAvatar(getcontact(e->event.notify60[i].uin, 0, 0, NULL), 0); + } + break; + } + + // Pubdir search reply && read own data reply + case GG_EVENT_PUBDIR50_SEARCH_REPLY: + case GG_EVENT_PUBDIR50_READ: + case GG_EVENT_PUBDIR50_WRITE: + { + gg_pubdir50_t res = e->event.pubdir50; + int i, count; + + if (e->type == GG_EVENT_PUBDIR50_SEARCH_REPLY) + { + netlog("gg_mainthread(%x): Got user info.", this); + // Store next search UIN + if (res->seq == GG_SEQ_SEARCH) + next_uin = gg_pubdir50_next(res); + } + else if (e->type == GG_EVENT_PUBDIR50_READ) + { + netlog("gg_mainthread(%x): Got owner info.", this); + } + else if (e->type == GG_EVENT_PUBDIR50_WRITE) + { + netlog("gg_mainthread(%x): Public directory save succesful.", this); + // Update user details + GetInfo(NULL, 0); + } + + if ((count = gg_pubdir50_count(res)) > 0) + { + for (i = 0; i < count; i++) + { + // Loadup fields + const char *__fmnumber = gg_pubdir50_get(res, i, GG_PUBDIR50_UIN); + const char *__nick = gg_pubdir50_get(res, i, GG_PUBDIR50_NICKNAME); + const char *__firstname = gg_pubdir50_get(res, i, GG_PUBDIR50_FIRSTNAME); + const char *__lastname = gg_pubdir50_get(res, i, GG_PUBDIR50_LASTNAME); + const char *__familyname = gg_pubdir50_get(res, i, GG_PUBDIR50_FAMILYNAME); + const char *__birthyear = gg_pubdir50_get(res, i, GG_PUBDIR50_BIRTHYEAR); + const char *__city = gg_pubdir50_get(res, i, GG_PUBDIR50_CITY); + const char *__origincity = gg_pubdir50_get(res, i, GG_PUBDIR50_FAMILYCITY); + const char *__gender = gg_pubdir50_get(res, i, GG_PUBDIR50_GENDER); + const char *__status = gg_pubdir50_get(res, i, GG_PUBDIR50_STATUS); + uin_t uin = __fmnumber ? atoi(__fmnumber) : 0; + + HANDLE hContact = (res->seq == GG_SEQ_CHINFO) ? NULL : getcontact(uin, 0, 0, NULL); + netlog("gg_mainthread(%x): Search result for uin %d, seq %d.", this, uin, res->seq); + if (res->seq == GG_SEQ_SEARCH) + { + char strFmt1[64]; + char strFmt2[64]; + + mir_snprintf(strFmt2, sizeof(strFmt2), "%s", (char *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, status_gg2m(atoi(__status)), 0)); + if (__city) + { + mir_snprintf(strFmt1, sizeof(strFmt1), ", %s %s", Translate("City:"), __city); + strncat(strFmt2, strFmt1, sizeof(strFmt2) - strlen(strFmt2)); + } + if (__birthyear) + { + time_t t = time(NULL); + struct tm *lt = localtime(&t); + int br = atoi(__birthyear); + + if (br < (lt->tm_year + 1900) && br > 1900) + { + mir_snprintf(strFmt1, sizeof(strFmt1), ", %s %d", Translate("Age:"), (lt->tm_year + 1900) - br); + strncat(strFmt2, strFmt1, sizeof(strFmt2) - strlen(strFmt2)); + } + } + + GGSEARCHRESULT sr; + memset(&sr, 0, sizeof(sr)); + sr.cbSize = sizeof(sr); + sr.nick = mir_a2t(__nick); + sr.firstName = mir_a2t(__firstname); + sr.lastName = mir_a2t(__lastname); + sr.email = mir_a2t(strFmt2); + sr.id = mir_a2t(_ultoa(uin, strFmt1, 10)); + sr.uin = uin; + ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE) 1, (LPARAM)&sr); + mir_free(sr.nick); + mir_free(sr.firstName); + mir_free(sr.lastName); + mir_free(sr.email); + mir_free(sr.id); + } + + if (((res->seq == GG_SEQ_INFO || res->seq == GG_SEQ_GETNICK) && hContact != NULL) + || res->seq == GG_SEQ_CHINFO) + { + // Change nickname if it's not present + if (__nick && (res->seq == GG_SEQ_GETNICK || res->seq == GG_SEQ_CHINFO)) + db_set_s(hContact, m_szModuleName, GG_KEY_NICK, __nick); + + if (__nick) + db_set_s(hContact, m_szModuleName, "NickName", __nick); + else if (res->seq == GG_SEQ_CHINFO) + db_unset(NULL, m_szModuleName, "NickName"); + + // Change other info + if (__city) + db_set_s(hContact, m_szModuleName, "City", __city); + else if (res->seq == GG_SEQ_CHINFO) + db_unset(NULL, m_szModuleName, "City"); + + if (__firstname) + db_set_s(hContact, m_szModuleName, "FirstName", __firstname); + else if (res->seq == GG_SEQ_CHINFO) + db_unset(NULL, m_szModuleName, "FirstName"); + + if (__lastname) + db_set_s(hContact, m_szModuleName, "LastName", __lastname); + else if (res->seq == GG_SEQ_CHINFO) + db_unset(NULL, m_szModuleName, "LastName"); + + if (__familyname) + db_set_s(hContact, m_szModuleName, "FamilyName", __familyname); + else if (res->seq == GG_SEQ_CHINFO) + db_unset(NULL, m_szModuleName, "FamilyName"); + + if (__origincity) + db_set_s(hContact, m_szModuleName, "CityOrigin", __origincity); + else if (res->seq == GG_SEQ_CHINFO) + db_unset(NULL, m_szModuleName, "CityOrigin"); + + if (__birthyear) + { + time_t t = time(NULL); + struct tm *lt = localtime(&t); + int br = atoi(__birthyear); + if (br > 0) + { + db_set_w(hContact, m_szModuleName, "Age", (WORD)(lt->tm_year + 1900 - br)); + db_set_w(hContact, m_szModuleName, "BirthYear", (WORD)br); + } + } + else if (res->seq == GG_SEQ_CHINFO) + { + db_unset(NULL, m_szModuleName, "Age"); + db_unset(NULL, m_szModuleName, "BirthYear"); + } + + // Gadu-Gadu Male <-> Female + if (__gender) + { + if (res->seq == GG_SEQ_CHINFO) + db_set_b(hContact, m_szModuleName, "Gender", + (BYTE)(!strcmp(__gender, GG_PUBDIR50_GENDER_SET_MALE) ? 'M' : + (!strcmp(__gender, GG_PUBDIR50_GENDER_SET_FEMALE) ? 'F' : '?'))); + else + db_set_b(hContact, m_szModuleName, "Gender", + (BYTE)(!strcmp(__gender, GG_PUBDIR50_GENDER_MALE) ? 'M' : + (!strcmp(__gender, GG_PUBDIR50_GENDER_FEMALE) ? 'F' : '?'))); + } + else if (res->seq == GG_SEQ_CHINFO) + { + db_unset(NULL, m_szModuleName, "Gender"); + } + + netlog("gg_mainthread(%x): Setting user info for uin %d.", this, uin); + ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE) 1, 0); + } + } + } + if (res->seq == GG_SEQ_SEARCH) + ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE) 1, 0); + break; + } + + // Status (deprecated) + case GG_EVENT_STATUS: + changecontactstatus(e->event.status.uin, e->event.status.status, e->event.status.descr, 0, 0, 0, 0); + break; + + // Status (version >= 6.0) + case GG_EVENT_STATUS60: + { + HANDLE hContact = getcontact(e->event.status60.uin, 0, 0, NULL); + int oldstatus = db_get_w(hContact, m_szModuleName, GG_KEY_STATUS, (WORD)ID_STATUS_OFFLINE); + uin_t uin = (uin_t)db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0); + + if (e->event.status60.uin == uin) + { + // Status was changed by the user simultaneously logged on using different Miranda account or IM client + int iStatus = status_gg2m(e->event.status60.status); + CallProtoService(m_szModuleName, PS_SETAWAYMSG, iStatus, (LPARAM)e->event.status60.descr); + CallProtoService(m_szModuleName, PS_SETSTATUS, iStatus, 0); + } + + changecontactstatus(e->event.status60.uin, e->event.status60.status, e->event.status60.descr, + e->event.status60.time, e->event.status60.remote_ip, e->event.status60.remote_port, e->event.status60.version); + + if (oldstatus == ID_STATUS_OFFLINE && db_get_w(hContact, m_szModuleName, GG_KEY_STATUS, (WORD)ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE) + requestAvatar(hContact, 0); + } + break; + + // Received userlist / or put info + case GG_EVENT_USERLIST: + switch (e->event.userlist.type) { + case GG_USERLIST_GET_REPLY: + if (e->event.userlist.reply) { + parsecontacts(e->event.userlist.reply); + MessageBox(NULL, TranslateT("List import successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + break; + + case GG_USERLIST_PUT_REPLY: + if (is_list_remove) + MessageBox(NULL, TranslateT("List remove successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); + else + MessageBox(NULL, TranslateT("List export successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); + break; + } + break; + + // Received message + case GG_EVENT_MSG: + // This is CTCP request + if ((e->event.msg.msgclass & GG_CLASS_CTCP)) + { + dccconnect(e->event.msg.sender); + } + // Check if not conference and block + else if (!e->event.msg.recipients_count || gc_enabled) + { + // Check if groupchat + if (e->event.msg.recipients_count && gc_enabled && !db_get_b(NULL, m_szModuleName, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF)) + { + char *chat = gc_getchat(e->event.msg.sender, e->event.msg.recipients, e->event.msg.recipients_count); + if (chat) + { + char id[32]; + GCDEST gcdest = {m_szModuleName, chat, GC_EVENT_MESSAGE}; + GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; + time_t t = time(NULL); + + UIN2ID(e->event.msg.sender, id); + + gcevent.pszUID = id; + gcevent.pszText = e->event.msg.message; + gcevent.pszNick = (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) getcontact(e->event.msg.sender, 1, 0, NULL), 0); + gcevent.time = (!(e->event.msg.msgclass & GG_CLASS_OFFLINE) || e->event.msg.time > (t - timeDeviation)) ? t : e->event.msg.time; + gcevent.dwFlags = GCEF_ADDTOLOG; + netlog("gg_mainthread(%x): Conference message to room %s & id %s.", this, chat, id); + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); + } + } + // Check if not empty message ( who needs it? ) + else if (!e->event.msg.recipients_count && e->event.msg.message && *e->event.msg.message && strcmp(e->event.msg.message, "\xA0\0")) + { + CCSDATA ccs = {0}; + PROTORECVEVENT pre = {0}; + time_t t = time(NULL); + ccs.szProtoService = PSR_MESSAGE; + ccs.hContact = getcontact(e->event.msg.sender, 1, 0, NULL); + ccs.lParam = (LPARAM)⪯ + pre.timestamp = (!(e->event.msg.msgclass & GG_CLASS_OFFLINE) || e->event.msg.time > (t - timeDeviation)) ? t : e->event.msg.time; + pre.szMessage = e->event.msg.message; + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM) &ccs); + } + + // RichEdit format included (image) + if (e->event.msg.formats_length && + db_get_b(NULL, m_szModuleName, GG_KEY_IMGRECEIVE, GG_KEYDEF_IMGRECEIVE) && + !(db_get_b(getcontact(e->event.msg.sender, 1, 0, NULL), "Ignore", "Mask1", 0) & IGNOREEVENT_MESSAGE)) + { + char *formats = (char*)e->event.msg.formats; + int len = 0, formats_len = e->event.msg.formats_length, add_ptr; + + while (len < formats_len) + { + add_ptr = sizeof(struct gg_msg_richtext_format); + if (((struct gg_msg_richtext_format*)formats)->font & GG_FONT_IMAGE) + { + struct gg_msg_richtext_image *image = (struct gg_msg_richtext_image *)(formats + add_ptr); + EnterCriticalSection(&sess_mutex); + gg_image_request(sess, e->event.msg.sender, image->size, image->crc32); + LeaveCriticalSection(&sess_mutex); + + netlog("gg_mainthread: image request sent!"); + add_ptr += sizeof(struct gg_msg_richtext_image); + } + if (((struct gg_msg_richtext_format*)formats)->font & GG_FONT_COLOR) + add_ptr += sizeof(struct gg_msg_richtext_color); + len += add_ptr; + formats += add_ptr; + } + } + } + break; + + // Message sent from concurrent user session + case GG_EVENT_MULTILOGON_MSG: + if (e->event.multilogon_msg.recipients_count && gc_enabled && !db_get_b(NULL, m_szModuleName, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF)) + { + char *chat = gc_getchat(e->event.multilogon_msg.sender, e->event.multilogon_msg.recipients, e->event.multilogon_msg.recipients_count); + if (chat) + { + char id[32]; + DBVARIANT dbv; + GCDEST gcdest = {m_szModuleName, chat, GC_EVENT_MESSAGE}; + GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; + + UIN2ID(db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0), id); + + gcevent.pszUID = id; + gcevent.pszText = e->event.multilogon_msg.message; + if (!db_get_s(NULL, m_szModuleName, GG_KEY_NICK, &dbv, DBVT_ASCIIZ)) + gcevent.pszNick = dbv.pszVal; + else + gcevent.pszNick = Translate("Me"); + gcevent.time = e->event.multilogon_msg.time; + gcevent.bIsMe = 1; + gcevent.dwFlags = GCEF_ADDTOLOG; + netlog("gg_mainthread(%x): Sent conference message to room %s.", this, chat); + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); + if (gcevent.pszNick == dbv.pszVal) DBFreeVariant(&dbv); + } + } + else if (!e->event.multilogon_msg.recipients_count && e->event.multilogon_msg.message && *e->event.multilogon_msg.message + && strcmp(e->event.multilogon_msg.message, "\xA0\0")) + { + DBEVENTINFO dbei = {0}; + dbei.cbSize = sizeof(dbei); + dbei.szModule = m_szModuleName; + dbei.timestamp = (DWORD)e->event.multilogon_msg.time; + dbei.flags = DBEF_SENT; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.cbBlob = (DWORD)strlen(e->event.multilogon_msg.message) + 1; + dbei.pBlob = (PBYTE)e->event.multilogon_msg.message; + CallService(MS_DB_EVENT_ADD, (WPARAM)getcontact(e->event.multilogon_msg.sender, 1, 0, NULL), (LPARAM)&dbei); + } + break; + + // Information on active concurrent sessions + case GG_EVENT_MULTILOGON_INFO: + { + list_t l; + int* iIndexes = NULL, i; + netlog("gg_mainthread(): Concurrent sessions count: %d.", e->event.multilogon_info.count); + if (e->event.multilogon_info.count > 0) + iIndexes = (int*)mir_calloc(e->event.multilogon_info.count * sizeof(int)); + EnterCriticalSection(&sessions_mutex); + for (l = sessions; l; l = l->next) + { + struct gg_multilogon_session* sess = (struct gg_multilogon_session*)l->data; + for (i = 0; i < e->event.multilogon_info.count; i++) + { + if (!memcmp(&sess->id, &e->event.multilogon_info.sessions[i].id, sizeof(gg_multilogon_id_t)) && iIndexes) + { + iIndexes[i]++; + break; + } + } + mir_free(sess->name); + mir_free(sess); + } + list_destroy(sessions, 0); + sessions = NULL; + for (i = 0; i < e->event.multilogon_info.count; i++) + { + gg_multilogon_session* sess = (gg_multilogon_session*)mir_alloc(sizeof(struct gg_multilogon_session)); + memcpy(sess, &e->event.multilogon_info.sessions[i], sizeof(struct gg_multilogon_session)); + sess->name = mir_strdup(*e->event.multilogon_info.sessions[i].name != '\0' + ? e->event.multilogon_info.sessions[i].name + : Translate("Unknown client")); + list_add(&sessions, sess, 0); + } + LeaveCriticalSection(&sessions_mutex); + sessions_updatedlg(); + if (ServiceExists(MS_POPUP_ADDPOPUPCLASS)) + { + const TCHAR* szText = time(NULL) - logonTime > 3 + ? TranslateT("You have logged in at another location") + : TranslateT("You are logged in at another location"); + for (i = 0; i < e->event.multilogon_info.count; i++) + { + TCHAR szMsg[MAX_SECONDLINE]; + if (iIndexes && iIndexes[i]) + continue; + + mir_sntprintf(szMsg, SIZEOF(szMsg), _T("%s (%s)"), szText, + *e->event.multilogon_info.sessions[i].name != '\0' ? + _A2T(e->event.multilogon_info.sessions[i].name) : TranslateT("Unknown client")); + showpopup(m_tszUserName, szMsg, GG_POPUP_MULTILOGON); + } + } + mir_free(iIndexes); + } + break; + + // Image reply sent + case GG_EVENT_IMAGE_REPLY: + // Get rid of empty image + if (e->event.image_reply.size && e->event.image_reply.image) + { + HANDLE hContact = getcontact(e->event.image_reply.sender, 1, 0, NULL); + void *img = (void *)img_loadpicture(e, 0); + + if (!img) + break; + + if (db_get_b(NULL, m_szModuleName, GG_KEY_IMGMETHOD, GG_KEYDEF_IMGMETHOD) == 1 || img_opened(e->event.image_reply.sender)) + { + img_display(hContact, img); + } + else if (db_get_b(NULL, m_szModuleName, GG_KEY_IMGMETHOD, GG_KEYDEF_IMGMETHOD) == 2) + { + img_displayasmsg(hContact, img); + } + else + { + CLISTEVENT cle = {0}; + char service[128]; + mir_snprintf(service, sizeof(service), GGS_RECVIMAGE, m_szModuleName); + + cle.cbSize = sizeof(cle); + cle.hContact = hContact; + cle.hIcon = LoadIconEx("image", FALSE); + cle.flags = CLEF_URGENT; + cle.hDbEvent = (HANDLE)"img"; + cle.lParam = (LPARAM)img; + cle.pszService = service; + cle.pszTooltip = Translate("Incoming image"); + CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&cle); + ReleaseIconEx("image", FALSE); + } + } + break; + + // Image send request + case GG_EVENT_IMAGE_REQUEST: + img_sendonrequest(e); + break; + + // Incoming direct connection + case GG_EVENT_DCC7_NEW: + { + struct gg_dcc7 *dcc7 = e->event.dcc7_new; + netlog("gg_mainthread(%x): Incoming direct connection.", this); + dcc7->contact = getcontact(dcc7->peer_uin, 0, 0, NULL); + + // Check if user is on the list and if it is my uin + if (!dcc7->contact || db_get_b(NULL, m_szModuleName, GG_KEY_UIN, -1) != dcc7->uin) { + gg_dcc7_free(dcc7); + e->event.dcc7_new = NULL; + break; + } + + // Add to waiting transfers + EnterCriticalSection(&ft_mutex); + list_add(&transfers, dcc7, 0); + LeaveCriticalSection(&ft_mutex); + + ////////////////////////////////////////////////// + // Add file recv request + { + CCSDATA ccs; + PROTORECVEVENT pre; + char *szBlob; + char *szFilename = (char*)dcc7->filename; + char *szMsg = (char*)dcc7->filename; + netlog("gg_mainthread(%x): Client: %d, File ack filename \"%s\" size %d.", this, dcc7->peer_uin, + dcc7->filename, dcc7->size); + // Make new ggtransfer struct + szBlob = (char *)malloc(sizeof(DWORD) + strlen(szFilename) + strlen(szMsg) + 2); + // Store current dcc + *(PDWORD)szBlob = (DWORD)dcc7; + // Store filename + strcpy(szBlob + sizeof(DWORD), szFilename); + // Store description + strcpy(szBlob + sizeof(DWORD) + strlen(szFilename) + 1, szMsg); + ccs.szProtoService = PSR_FILE; + ccs.hContact = dcc7->contact; + ccs.wParam = 0; + ccs.lParam = (LPARAM)⪯ + pre.flags = 0; + pre.timestamp = time(NULL); + pre.szMessage = szBlob; + pre.lParam = 0; + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); + free(szBlob); + } + e->event.dcc7_new = NULL; + } + break; + + // Direct connection rejected + case GG_EVENT_DCC7_REJECT: + { + struct gg_dcc7 *dcc7 = e->event.dcc7_reject.dcc7; + if (dcc7->type == GG_SESSION_DCC7_SEND) + { + netlog("gg_mainthread(%x): File transfer denied by client %d (reason = %d).", this, dcc7->peer_uin, e->event.dcc7_reject.reason); + ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DENIED, dcc7, 0); + + // Remove from watches and free + EnterCriticalSection(&ft_mutex); + list_remove(&watches, dcc7, 0); + LeaveCriticalSection(&ft_mutex); + gg_dcc7_free(dcc7); + } + else + { + netlog("gg_mainthread(%x): File transfer aborted by client %d.", this, dcc7->peer_uin); + + // Remove transfer from waiting list + EnterCriticalSection(&ft_mutex); + list_remove(&transfers, dcc7, 0); + LeaveCriticalSection(&ft_mutex); + } + } + break; + + // Direct connection error + case GG_EVENT_DCC7_ERROR: + { + struct gg_dcc7 *dcc7 = e->event.dcc7_error_ex.dcc7; + switch (e->event.dcc7_error) + { + case GG_ERROR_DCC7_HANDSHAKE: + netlog("gg_mainthread(%x): Client: %d, Handshake error.", this, dcc7 ? dcc7->peer_uin : 0); + break; + case GG_ERROR_DCC7_NET: + netlog("gg_mainthread(%x): Client: %d, Network error.", this, dcc7 ? dcc7->peer_uin : 0); + break; + case GG_ERROR_DCC7_FILE: + netlog("gg_mainthread(%x): Client: %d, File read/write error.", this, dcc7 ? dcc7->peer_uin : 0); + break; + case GG_ERROR_DCC7_EOF: + netlog("gg_mainthread(%x): Client: %d, End of file/connection error.", this, dcc7 ? dcc7->peer_uin : 0); + break; + case GG_ERROR_DCC7_REFUSED: + netlog("gg_mainthread(%x): Client: %d, Connection refused error.", this, dcc7 ? dcc7->peer_uin : 0); + break; + case GG_ERROR_DCC7_RELAY: + netlog("gg_mainthread(%x): Client: %d, Relay connection error.", this, dcc7 ? dcc7->peer_uin : 0); + break; + default: + netlog("gg_mainthread(%x): Client: %d, Unknown error.", this, dcc7 ? dcc7->peer_uin : 0); + } + if (!dcc7) break; + + // Remove from watches + list_remove(&watches, dcc7, 0); + + // Close file & fail + if (dcc7->file_fd != -1) + { + _close(dcc7->file_fd); + dcc7->file_fd = -1; + } + + if (dcc7->contact) + ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); + + // Free dcc + gg_dcc7_free(dcc7); + } + break; + + case GG_EVENT_XML_ACTION: + if (db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) { + HXML hXml; + TCHAR *xmlAction; + TCHAR *tag; + + xmlAction = mir_a2t(e->event.xml_action.data); + tag = mir_a2t("events"); + hXml = xi.parseString(xmlAction, 0, tag); + + if (hXml != NULL) { + HXML node; + char *type, *sender; + + mir_free(tag); + tag = mir_a2t("event/type"); + node = xi.getChildByPath(hXml, tag, 0); + type = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + + mir_free(tag); + tag = mir_a2t("event/sender"); + node = xi.getChildByPath(hXml, tag, 0); + sender = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + netlog("gg_mainthread(%x): XML Action type: %s.", this, type != NULL ? type : "unknown"); + // Avatar change notify + if (type != NULL && !strcmp(type, "28")) { + netlog("gg_mainthread(%x): Client %s changed his avatar.", this, sender); + requestAvatar(getcontact(atoi(sender), 0, 0, NULL), 0); + } + mir_free(type); + mir_free(sender); + xi.destroyNode(hXml); + } + mir_free(tag); + mir_free(xmlAction); + } + break; + + case GG_EVENT_TYPING_NOTIFICATION: + { + HANDLE hContact = getcontact(e->event.typing_notification.uin, 0, 0, NULL); +#ifdef DEBUGMODE + netlog("gg_mainthread(%x): Typing notification from %d (%d).", this, + e->event.typing_notification.uin, e->event.typing_notification.length); +#endif + CallService(MS_PROTO_CONTACTISTYPING, (WPARAM)hContact, + e->event.typing_notification.length > 0 ? 7 : PROTOTYPE_CONTACTTYPING_OFF); + } + break; + } + // Free event struct + gg_free_event(e); + } + + broadcastnewstatus(ID_STATUS_OFFLINE); + setalloffline(); + db_set_w(NULL, m_szModuleName, GG_KEY_LOGONTIME, 0); + + // If it was unwanted disconnection reconnect + if (m_iDesiredStatus != ID_STATUS_OFFLINE + && db_get_b(NULL, m_szModuleName, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT)) + { + netlog("gg_mainthread(%x): Unintentional disconnection detected. Going to reconnect...", this); + hostnum = 0; + broadcastnewstatus(ID_STATUS_CONNECTING); + mir_free(p.status_descr); + goto retry; + } + + mir_free(p.password); + mir_free(p.status_descr); + + // Destroy concurrent sessions list + { + list_t l; + EnterCriticalSection(&sessions_mutex); + for (l = sessions; l; l = l->next) + { + struct gg_multilogon_session* sess = (struct gg_multilogon_session*)l->data; + mir_free(sess->name); + mir_free(sess); + } + list_destroy(sessions, 0); + sessions = NULL; + LeaveCriticalSection(&sessions_mutex); + } + + // Stop dcc server + pth_dcc.dwThreadId = 0; +#ifdef DEBUGMODE + netlog("gg_mainthread(%x): Waiting until DCC Server Thread finished, if needed.", this); +#endif + threadwait(&pth_dcc); + + netlog("gg_mainthread(%x): Server Thread Ending", this); + return; +} + +//////////////////////////////////////////////////////////// +// Change status function +void GGPROTO::broadcastnewstatus(int newStatus) +{ + int oldStatus; + + EnterCriticalSection(&modemsg_mutex); + oldStatus = m_iStatus; + if (oldStatus == newStatus) + { + LeaveCriticalSection(&modemsg_mutex); + return; + } + m_iStatus = newStatus; + LeaveCriticalSection(&modemsg_mutex); + + ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldStatus, newStatus); + + netlog("gg_broadcastnewstatus(): Broadcast new status: %d.", newStatus); +} + +//////////////////////////////////////////////////////////// +// When contact is deleted +int GGPROTO::contactdeleted(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE) wParam; + uin_t uin; int type; + DBVARIANT dbv; + + uin = (uin_t)db_get_b(hContact, m_szModuleName, GG_KEY_UIN, 0); + type = db_get_b(hContact, m_szModuleName, "ChatRoom", 0); + + // Terminate conference if contact is deleted + if (type && !db_get_s(hContact, m_szModuleName, "ChatRoomID", &dbv, DBVT_ASCIIZ) && gc_enabled) + { + GCDEST gcdest = {m_szModuleName, dbv.pszVal, GC_EVENT_CONTROL}; + GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; + GGGC *chat = gc_lookup(dbv.pszVal); + + netlog("gg_gc_event(): Terminating chat %x, id %s from contact list...", chat, dbv.pszVal); + if (chat) + { + // Destroy chat entry + free(chat->recipients); + list_remove(&chats, chat, 1); + // Terminate chat window / shouldn't cascade entry is deleted + CallServiceSync(MS_GC_EVENT, SESSION_OFFLINE, (LPARAM)&gcevent); + CallServiceSync(MS_GC_EVENT, SESSION_TERMINATE, (LPARAM)&gcevent); + } + + DBFreeVariant(&dbv); + return 0; + } + + if (uin && isonline()) + { + EnterCriticalSection(&sess_mutex); + gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); + LeaveCriticalSection(&sess_mutex); + } + + return 0; +} + +//////////////////////////////////////////////////////////// +// When db settings changed + +int GGPROTO::dbsettingchanged(WPARAM wParam, LPARAM lParam) +{ + DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam; + HANDLE hContact = (HANDLE) wParam; + char *szProto = NULL; + + // Check if the contact is NULL or we are not online + if (!isonline()) + return 0; + + // If contact has been blocked + if (!strcmp(cws->szModule, m_szModuleName) && !strcmp(cws->szSetting, GG_KEY_BLOCK)) + { + notifyuser(hContact, 1); + return 0; + } + + // Contact is being renamed + if (gc_enabled && !strcmp(cws->szModule, m_szModuleName) && !strcmp(cws->szSetting, GG_KEY_NICK) + && cws->value.pszVal) + { + // Groupchat window contact is being renamed + DBVARIANT dbv; + int type = db_get_b(hContact, m_szModuleName, "ChatRoom", 0); + if (type && !db_get_s(hContact, m_szModuleName, "ChatRoomID", &dbv, DBVT_ASCIIZ)) + { + // Most important... check redundancy (fucking cascading) + static int cascade = 0; + if (!cascade && dbv.pszVal) + { + GCDEST gcdest = {m_szModuleName, dbv.pszVal, GC_EVENT_CHANGESESSIONAME}; + GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; + gcevent.pszText = cws->value.pszVal; + netlog("gg_dbsettingchanged(): Conference %s was renamed to %s.", dbv.pszVal, cws->value.pszVal); + // Mark cascading + /* FIXME */ cascade = 1; + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); + /* FIXME */ cascade = 0; + } + DBFreeVariant(&dbv); + } + else + // Change contact name on all chats + gc_changenick(hContact, cws->value.pszVal); + } + + // Contact list changes + if (!strcmp(cws->szModule, "CList")) + { + // If name changed... change nick + if (!strcmp(cws->szSetting, "MyHandle") && cws->value.type == DBVT_ASCIIZ && cws->value.pszVal) + db_set_s(hContact, m_szModuleName, GG_KEY_NICK, cws->value.pszVal); + + // If not on list changed + if (!strcmp(cws->szSetting, "NotOnList")) + { + if (db_get_b(hContact, "CList", "Hidden", 0)) + return 0; + // Notify user normally this time if added to the list permanently + if (cws->value.type == DBVT_DELETED || (cws->value.type == DBVT_BYTE && cws->value.bVal == 0)) + notifyuser(hContact, 1); + } + } + return 0; +} + +//////////////////////////////////////////////////////////// +// All users set offline + +void GGPROTO::setalloffline() +{ + netlog("gg_setalloffline(): Setting buddies offline"); + db_set_w(NULL, m_szModuleName, GG_KEY_STATUS, ID_STATUS_OFFLINE); + HANDLE hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while (hContact) + { + char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && !strcmp(szProto, m_szModuleName)) + { + db_set_w(hContact, m_szModuleName, GG_KEY_STATUS, ID_STATUS_OFFLINE); + // Clear IP and port settings + db_unset(hContact, m_szModuleName, GG_KEY_CLIENTIP); + db_unset(hContact, m_szModuleName, GG_KEY_CLIENTPORT); + // Delete status descr + db_unset(hContact, "CList", GG_KEY_STATUSDESCR); + } + hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); + } +#ifdef DEBUGMODE + netlog("gg_setalloffline(): End"); +#endif +} + +//////////////////////////////////////////////////////////// +// All users set offline + +void GGPROTO::notifyuser(HANDLE hContact, int refresh) +{ + uin_t uin; + if (!hContact) return; + if (isonline() && (uin = (uin_t)db_get_b(hContact, m_szModuleName, GG_KEY_UIN, 0))) + { + // Check if user should be invisible + // Or be blocked ? + if ((db_get_w(hContact, m_szModuleName, GG_KEY_APPARENT, (WORD) ID_STATUS_ONLINE) == ID_STATUS_OFFLINE) || + db_get_b(hContact, "CList", "NotOnList", 0)) + { + mir_cslock l(sess_mutex); + if (refresh) { + gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); + gg_remove_notify_ex(sess, uin, GG_USER_BLOCKED); + } + + gg_add_notify_ex(sess, uin, GG_USER_OFFLINE); + } + else if (db_get_b(hContact, m_szModuleName, GG_KEY_BLOCK, 0)) + { + mir_cslock l(sess_mutex); + if (refresh) + gg_remove_notify_ex(sess, uin, GG_USER_OFFLINE); + + gg_add_notify_ex(sess, uin, GG_USER_BLOCKED); + } + else { + mir_cslock l(sess_mutex); + if (refresh) + gg_remove_notify_ex(sess, uin, GG_USER_BLOCKED); + + gg_add_notify_ex(sess, uin, GG_USER_NORMAL); + } + } +} + +void GGPROTO::notifyall() +{ + HANDLE hContact; + char *szProto; + int count = 0, cc = 0; + uin_t *uins; + char *types; + + netlog("gg_notifyall(): Subscribing notification to all users"); + // Readup count + hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while (hContact) + { + szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && !strcmp(szProto, m_szModuleName)) count ++; + hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); + } + + // Readup list + /* FIXME: If we have nothing on the list but we omit gg_notify_ex we have problem with receiving any contacts */ + if (count == 0) + { + if (isonline()) + { + EnterCriticalSection(&sess_mutex); + gg_notify_ex(sess, NULL, NULL, 0); + LeaveCriticalSection(&sess_mutex); + } + return; + } + uins = (uin_t*)calloc(sizeof(uin_t), count); + types = (char*)calloc(sizeof(char), count); + + hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while (hContact && cc < count) + { + szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && !strcmp(szProto, m_szModuleName) && (uins[cc] = db_get_b(hContact, m_szModuleName, GG_KEY_UIN, 0))) + { + if ((db_get_w(hContact, m_szModuleName, GG_KEY_APPARENT, (WORD) ID_STATUS_ONLINE) == ID_STATUS_OFFLINE) || + db_get_b(hContact, "CList", "NotOnList", 0)) + types[cc] = GG_USER_OFFLINE; + else if (db_get_b(hContact, m_szModuleName, GG_KEY_BLOCK, 0)) + types[cc] = GG_USER_BLOCKED; + else + types[cc] = GG_USER_NORMAL; + cc ++; + } + hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); + } + if (cc < count) count = cc; + + // Send notification + if (isonline()) + { + EnterCriticalSection(&sess_mutex); + gg_notify_ex(sess, uins, types, count); + LeaveCriticalSection(&sess_mutex); + } + + // Free variables + free(uins); free(types); +} + +//////////////////////////////////////////////////////////// +// Get contact by uin + +HANDLE GGPROTO::getcontact(uin_t uin, int create, int inlist, TCHAR *szNick) +{ + // Look for contact in DB + HANDLE hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while (hContact) { + char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && !strcmp(szProto, m_szModuleName)) { + if ((uin_t)db_get_b(hContact, m_szModuleName, GG_KEY_UIN, 0) == uin + && db_get_b(hContact, m_szModuleName, "ChatRoom", 0) == 0) + { + if (inlist) { + db_unset(hContact, "CList", "NotOnList"); + db_unset(hContact, "CList", "Hidden"); + } + return hContact; + } + } + hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); + } + if (!create) return NULL; + + hContact = (HANDLE) CallService(MS_DB_CONTACT_ADD, 0, 0); + if (!hContact) { + netlog("gg_getcontact(): Failed to create Gadu-Gadu contact %s", szNick); + return NULL; + } + + if (CallService(MS_PROTO_ADDTOCONTACT, (WPARAM) hContact, (LPARAM) m_szModuleName) != 0) { + // For some reason we failed to register the protocol for this contact + CallService(MS_DB_CONTACT_DELETE, (WPARAM) hContact, 0); + netlog("Failed to register GG contact %d", uin); + return NULL; + } + + netlog("gg_getcontact(): Added buddy: %d", uin); + if (!inlist) + db_set_b(hContact, "CList", "NotOnList", 1); + + db_set_w(hContact, m_szModuleName, GG_KEY_UIN, (DWORD) uin); + db_set_w(hContact, m_szModuleName, GG_KEY_STATUS, ID_STATUS_OFFLINE); + + // If nick specified use it + if (szNick) + db_set_ts(hContact, m_szModuleName, GG_KEY_NICK, szNick); + else if (isonline()) { + gg_pubdir50_t req; + + // Search for that nick + if (req = gg_pubdir50_new(GG_PUBDIR50_SEARCH)) { + // Add uin and search it + gg_pubdir50_add(req, GG_PUBDIR50_UIN, ditoa(uin)); + gg_pubdir50_seq_set(req, GG_SEQ_GETNICK); + EnterCriticalSection(&sess_mutex); + gg_pubdir50(sess, req); + LeaveCriticalSection(&sess_mutex); + gg_pubdir50_free(req); + db_set_s(hContact, m_szModuleName, GG_KEY_NICK, ditoa(uin)); + netlog("gg_getcontact(): Search for nick on uin: %d", uin); + } + } + + // Add to notify list and pull avatar for the new contact + if (isonline()) + { + PROTO_AVATAR_INFORMATIONT pai = {0}; + + EnterCriticalSection(&sess_mutex); + gg_add_notify_ex(sess, uin, (char)(inlist ? GG_USER_NORMAL : GG_USER_OFFLINE)); + LeaveCriticalSection(&sess_mutex); + + pai.cbSize = sizeof(pai); + pai.hContact = hContact; + getavatarinfo((WPARAM)GAIF_FORCE, (LPARAM)&pai); + + // Change status of the contact with our own UIN (if got yourself added to the contact list) + if (db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0) == uin) { + char *szMsg; + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(getstatusmsg(m_iStatus)); + LeaveCriticalSection(&modemsg_mutex); + changecontactstatus(uin, status_m2gg(m_iStatus, szMsg != NULL), szMsg, 0, 0, 0, 0); + mir_free(szMsg); + } + } + + // TODO server side list & add buddy + return hContact; +} + +//////////////////////////////////////////////////////////// +// Status conversion + +int GGPROTO::status_m2gg(int status, int descr) +{ + // check frends only + int mask = db_get_b(NULL, m_szModuleName, GG_KEY_FRIENDSONLY, GG_KEYDEF_FRIENDSONLY) ? GG_STATUS_FRIENDS_MASK : 0; + + if (descr) + { + switch(status) + { + case ID_STATUS_OFFLINE: return GG_STATUS_NOT_AVAIL_DESCR | mask; + case ID_STATUS_ONLINE: return GG_STATUS_AVAIL_DESCR | mask; + case ID_STATUS_AWAY: return GG_STATUS_BUSY_DESCR | mask; + case ID_STATUS_DND: return GG_STATUS_DND_DESCR | mask; + case ID_STATUS_FREECHAT: return GG_STATUS_FFC_DESCR | mask; + case ID_STATUS_INVISIBLE: return GG_STATUS_INVISIBLE_DESCR | mask; + default: return GG_STATUS_BUSY_DESCR | mask; + } + } + else + { + switch(status) + { + case ID_STATUS_OFFLINE: return GG_STATUS_NOT_AVAIL | mask; + case ID_STATUS_ONLINE: return GG_STATUS_AVAIL | mask; + case ID_STATUS_AWAY: return GG_STATUS_BUSY | mask; + case ID_STATUS_DND: return GG_STATUS_DND | mask; + case ID_STATUS_FREECHAT: return GG_STATUS_FFC | mask; + case ID_STATUS_INVISIBLE: return GG_STATUS_INVISIBLE | mask; + default: return GG_STATUS_BUSY | mask; + } + } +} + +int GGPROTO::status_gg2m(int status) +{ + // ignore additional flags + status = GG_S(status); + + // when user has status description but is offline (show it invisible) + if (status == GG_STATUS_NOT_AVAIL_DESCR && db_get_b(NULL, m_szModuleName, GG_KEY_SHOWINVISIBLE, GG_KEYDEF_SHOWINVISIBLE)) + return ID_STATUS_INVISIBLE; + + // rest of cases + switch(status) + { + case GG_STATUS_NOT_AVAIL: + case GG_STATUS_NOT_AVAIL_DESCR: + return ID_STATUS_OFFLINE; + + case GG_STATUS_AVAIL: + case GG_STATUS_AVAIL_DESCR: + return ID_STATUS_ONLINE; + + case GG_STATUS_BUSY: + case GG_STATUS_BUSY_DESCR: + return ID_STATUS_AWAY; + + case GG_STATUS_DND: + case GG_STATUS_DND_DESCR: + return ID_STATUS_DND; + + case GG_STATUS_FFC: + case GG_STATUS_FFC_DESCR: + return ID_STATUS_FREECHAT; + + case GG_STATUS_INVISIBLE: + case GG_STATUS_INVISIBLE_DESCR: + return ID_STATUS_INVISIBLE; + + case GG_STATUS_BLOCKED: + return ID_STATUS_NA; + + default: + return ID_STATUS_OFFLINE; + } +} + +//////////////////////////////////////////////////////////// +// Called when contact status is changed + +void GGPROTO::changecontactstatus(uin_t uin, int status, const char *idescr, int time, uint32_t remote_ip, uint16_t remote_port, uint32_t version) +{ + HANDLE hContact = getcontact(uin, 0, 0, NULL); + + // Check if contact is on list + if (!hContact) return; + + // Write contact status + db_set_w(hContact, m_szModuleName, GG_KEY_STATUS, (WORD)status_gg2m(status)); + + // Check if there's description and if it's not empty + if (idescr && *idescr) + { + netlog("gg_changecontactstatus(): Saving for %d status descr \"%s\".", uin, idescr); + db_set_s(hContact, "CList", GG_KEY_STATUSDESCR, idescr); + } + else + // Remove status if there's nothing + db_unset(hContact, "CList", GG_KEY_STATUSDESCR); + + // Store contact ip and port + if (remote_ip) db_set_w(hContact, m_szModuleName, GG_KEY_CLIENTIP, (DWORD) swap32(remote_ip)); + if (remote_port) db_set_w(hContact, m_szModuleName, GG_KEY_CLIENTPORT, (WORD) remote_port); + if (version) + { + char sversion[48]; + db_set_w(hContact, m_szModuleName, GG_KEY_CLIENTVERSION, (DWORD) version); + mir_snprintf(sversion, sizeof(sversion), "%sGadu-Gadu %s", (version & 0x00ffffff) > 0x2b ? "Nowe " : "", gg_version2string(version)); + db_set_s(hContact, m_szModuleName, "MirVer", sversion); + } +} + +//////////////////////////////////////////////////////////// +// Returns GG client version string from packet version +const char *gg_version2string(int v) +{ + const char *pstr = "???"; + v &= 0x00ffffff; + switch(v) + { + case 0x2e: + pstr = "8.0 build 8283"; break; + case 0x2d: + pstr = "8.0 build 4881"; break; + case 0x2b: + pstr = "< 8.0"; break; + case 0x2a: + pstr = "7.7 build 3315"; break; + case 0x29: + pstr = "7.6 build 1688"; break; + case 0x28: + pstr = "7.5 build 2201"; break; + case 0x27: + pstr = "7.0 build 22"; break; + case 0x26: + pstr = "7.0 build 20"; break; + case 0x25: + pstr = "7.0 build 1"; break; + case 0x24: + pstr = "6.1 (155) / 7.6 (1359)"; break; + case 0x22: + pstr = "6.0 build 140"; break; + case 0x21: + pstr = "6.0 build 133"; break; + case 0x20: + pstr = "6.0b"; break; + case 0x1e: + pstr = "5.7b build 121"; break; + case 0x1c: + pstr = "5.7b"; break; + case 0x1b: + pstr = "5.0.5"; break; + case 0x19: + pstr = "5.0.3"; break; + case 0x18: + pstr = "5.0.0-1"; break; + case 0x17: + pstr = "4.9.2"; break; + case 0x16: + pstr = "4.9.1"; break; + case 0x15: + pstr = "4.8.9"; break; + case 0x14: + pstr = "4.8.1-3"; break; + case 0x11: + pstr = "4.6.1-10"; break; + case 0x10: + pstr = "4.5.15-22"; break; + case 0x0f: + pstr = "4.5.12"; break; + case 0x0b: + pstr = "4.0.25-30"; break; + default: + if (v < 0x0b) + pstr = "< 4.0.25"; + else if (v > 0x2e) + pstr = ">= 8.0"; + break; + } + return pstr; +} diff --git a/protocols/Gadu-Gadu/dialogs.c b/protocols/Gadu-Gadu/dialogs.c deleted file mode 100644 index 5582d15ffb..0000000000 --- a/protocols/Gadu-Gadu/dialogs.c +++ /dev/null @@ -1,1042 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 Adam Strzelecki -// -// 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 "gg.h" - -static INT_PTR CALLBACK gg_genoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); -static INT_PTR CALLBACK gg_confoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); -static INT_PTR CALLBACK gg_advoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); -extern INT_PTR CALLBACK gg_userutildlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); - -//////////////////////////////////////////////////////////////////////////////// -// SetValue -#define SVS_NORMAL 0 -#define SVS_GENDER 1 -#define SVS_ZEROISUNSPEC 2 -#define SVS_IP 3 -#define SVS_COUNTRY 4 -#define SVS_MONTH 5 -#define SVS_SIGNED 6 -#define SVS_TIMEZONE 7 -#define SVS_GGVERSION 9 - -static void SetValue(HWND hwndDlg, int idCtrl, HANDLE hContact, char *szModule, char *szSetting, int special, int disableIfUndef) -{ - DBVARIANT dbv = {0}; - char str[80], *pstr = NULL; - int unspecified = 0; - - dbv.type = DBVT_DELETED; - if (szModule == NULL) unspecified = 1; - else unspecified = DBGetContactSettingW(hContact, szModule, szSetting, &dbv); - if (!unspecified) { - switch (dbv.type) { - case DBVT_BYTE: - if (special == SVS_GENDER) { - if (dbv.cVal == 'M') pstr = Translate("Male"); - else if (dbv.cVal == 'F') pstr = Translate("Female"); - else unspecified = 1; - } - else if (special == SVS_MONTH) { - if (dbv.bVal > 0 && dbv.bVal <= 12) { - pstr = str; - GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1 - 1 + dbv.bVal, str, SIZEOF(str)); - } - else unspecified = 1; - } - else if (special == SVS_TIMEZONE) { - if (dbv.cVal == -100) unspecified = 1; - else { - pstr = str; - mir_snprintf(str, SIZEOF(str), dbv.cVal ? "GMT%+d:%02d" : "GMT", -dbv.cVal / 2, (dbv.cVal & 1) * 30); - } - } - else { - unspecified = (special == SVS_ZEROISUNSPEC && dbv.bVal == 0); - pstr = _itoa(special == SVS_SIGNED ? dbv.cVal : dbv.bVal, str, 10); - } - break; - case DBVT_WORD: - if (special == SVS_COUNTRY) { - pstr = (char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, dbv.wVal, 0); - unspecified = pstr == NULL; - } - else { - unspecified = (special == SVS_ZEROISUNSPEC && dbv.wVal == 0); - pstr = _itoa(special == SVS_SIGNED ? dbv.sVal : dbv.wVal, str, 10); - } - break; - case DBVT_DWORD: - unspecified = (special == SVS_ZEROISUNSPEC && dbv.dVal == 0); - if (special == SVS_IP) { - struct in_addr ia; - ia.S_un.S_addr = htonl(dbv.dVal); - pstr = inet_ntoa(ia); - if (dbv.dVal == 0) unspecified = 1; - } - else if (special == SVS_GGVERSION) - pstr = (char *)gg_version2string(dbv.dVal); - else - pstr = _itoa(special == SVS_SIGNED ? dbv.lVal : dbv.dVal, str, 10); - break; - case DBVT_ASCIIZ: - unspecified = (special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0'); - pstr = dbv.pszVal; - break; - default: pstr = str; lstrcpy(str, "???"); break; - } - } - - if (disableIfUndef) - { - EnableWindow(GetDlgItem(hwndDlg, idCtrl), !unspecified); - if (unspecified) - SetDlgItemText(hwndDlg, idCtrl, Translate("")); - else - SetDlgItemText(hwndDlg, idCtrl, pstr); - } - else - { - EnableWindow(GetDlgItem(hwndDlg, idCtrl), TRUE); - if (!unspecified) - SetDlgItemText(hwndDlg, idCtrl, pstr); - } - DBFreeVariant(&dbv); -} - -//////////////////////////////////////////////////////////////////////////////// -// Options Page : Init -int gg_options_init(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - OPTIONSDIALOGPAGE odp = { 0 }; - odp.cbSize = sizeof(odp); - odp.position = 1003000; - odp.hInstance = hInstance; - odp.pszGroup = LPGEN("Network"); - odp.pszTitle = GG_PROTONAME; - odp.dwInitParam = (LPARAM)gg; - - odp.pszTab = LPGEN("General"); - odp.pszTemplate = MAKEINTRESOURCE(IDD_OPT_GG_GENERAL); - odp.pfnDlgProc = gg_genoptsdlgproc; - odp.flags = ODPF_BOLDGROUPS | ODPF_DONTTRANSLATE; - Options_AddPage(wParam, &odp); - - odp.pszTab = LPGEN("Conference"); - odp.pszTemplate = MAKEINTRESOURCE(IDD_OPT_GG_CONFERENCE); - odp.pfnDlgProc = gg_confoptsdlgproc; - Options_AddPage(wParam, &odp); - - odp.pszTab = LPGEN("Advanced"); - odp.pszTemplate = MAKEINTRESOURCE(IDD_OPT_GG_ADVANCED); - odp.pfnDlgProc = gg_advoptsdlgproc; - odp.flags |= ODPF_EXPERTONLY; - Options_AddPage(wParam, &odp); - - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -// Check if new user data has been filled in for specified account -void gg_checknewuser(GGPROTO* gg, uin_t uin, const char* passwd) -{ - char oldpasswd[128]; - DBVARIANT dbv; - uin_t olduin = (uin_t)DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0); - - oldpasswd[0] = '\0'; - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) - { - if (dbv.pszVal) strcpy(oldpasswd, dbv.pszVal); - DBFreeVariant(&dbv); - } - - if (uin > 0 && strlen(passwd) > 0 && (uin != olduin || strcmp(oldpasswd, passwd))) - gg->check_first_conn = 1; -} - -//////////////////////////////////////////////////////////////////////////////// -// Options Page : Proc -static void gg_optsdlgcheck(HWND hwndDlg) -{ - char text[128]; - GetDlgItemText(hwndDlg, IDC_UIN, text, sizeof(text)); - if(strlen(text)) - { - GetDlgItemText(hwndDlg, IDC_EMAIL, text, sizeof(text)); - if(strlen(text)) - ShowWindow(GetDlgItem(hwndDlg, IDC_CHEMAIL), SW_SHOW); - else - ShowWindow(GetDlgItem(hwndDlg, IDC_CHEMAIL), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_CHPASS), SW_SHOW); - ShowWindow(GetDlgItem(hwndDlg, IDC_LOSTPASS), SW_SHOW); - ShowWindow(GetDlgItem(hwndDlg, IDC_REMOVEACCOUNT), SW_SHOW); - ShowWindow(GetDlgItem(hwndDlg, IDC_CREATEACCOUNT), SW_HIDE); - } - else - { - ShowWindow(GetDlgItem(hwndDlg, IDC_REMOVEACCOUNT), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_LOSTPASS), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_CHPASS), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_CHEMAIL), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_CREATEACCOUNT), SW_SHOW); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////// -// Proc: General options dialog -static INT_PTR CALLBACK gg_genoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: - { - DBVARIANT dbv; - DWORD num; - GGPROTO *gg = (GGPROTO *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); - - TranslateDialogDefault(hwndDlg); - if (num = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0)) - { - SetDlgItemText(hwndDlg, IDC_UIN, ditoa(num)); - ShowWindow(GetDlgItem(hwndDlg, IDC_CREATEACCOUNT), SW_HIDE); - } - else - { - ShowWindow(GetDlgItem(hwndDlg, IDC_CHPASS), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_REMOVEACCOUNT), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_LOSTPASS), SW_HIDE); - } - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - SetDlgItemText(hwndDlg, IDC_PASSWORD, dbv.pszVal); - DBFreeVariant(&dbv); - } - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, &dbv)) { - SetDlgItemText(hwndDlg, IDC_EMAIL, dbv.pszVal); - DBFreeVariant(&dbv); - } - else - { - ShowWindow(GetDlgItem(hwndDlg, IDC_LOSTPASS), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_CHPASS), SW_HIDE); - } - - CheckDlgButton(hwndDlg, IDC_FRIENDSONLY, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_FRIENDSONLY, GG_KEYDEF_FRIENDSONLY)); - CheckDlgButton(hwndDlg, IDC_SHOWINVISIBLE, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWINVISIBLE, GG_KEYDEF_SHOWINVISIBLE)); - CheckDlgButton(hwndDlg, IDC_LEAVESTATUSMSG, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_LEAVESTATUSMSG, GG_KEYDEF_LEAVESTATUSMSG)); - if(gg->gc_enabled) - CheckDlgButton(hwndDlg, IDC_IGNORECONF, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF)); - else - { - EnableWindow(GetDlgItem(hwndDlg, IDC_IGNORECONF), FALSE); - CheckDlgButton(hwndDlg, IDC_IGNORECONF, TRUE); - } - CheckDlgButton(hwndDlg, IDC_IMGRECEIVE, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGRECEIVE, GG_KEYDEF_IMGRECEIVE)); - CheckDlgButton(hwndDlg, IDC_SHOWLINKS, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWLINKS, GG_KEYDEF_SHOWLINKS)); - CheckDlgButton(hwndDlg, IDC_ENABLEAVATARS, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)); - - EnableWindow(GetDlgItem(hwndDlg, IDC_LEAVESTATUS), IsDlgButtonChecked(hwndDlg, IDC_LEAVESTATUSMSG)); - EnableWindow(GetDlgItem(hwndDlg, IDC_IMGMETHOD), IsDlgButtonChecked(hwndDlg, IDC_IMGRECEIVE)); - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)Translate("")); // 0 - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)Translate("Online")); // ID_STATUS_ONLINE - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)Translate("Away")); // ID_STATUS_AWAY - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)Translate("DND")); // ID_STATUS_DND - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)Translate("Free for chat")); // ID_STATUS_FREECHAT - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)Translate("Invisible")); // ID_STATUS_INVISIBLE - switch(DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, GG_KEYDEF_LEAVESTATUS)) - { - case ID_STATUS_ONLINE: - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 1, 0); - break; - case ID_STATUS_AWAY: - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 2, 0); - break; - case ID_STATUS_DND: - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 3, 0); - break; - case ID_STATUS_FREECHAT: - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 4, 0); - break; - case ID_STATUS_INVISIBLE: - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 5, 0); - break; - default: - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 0, 0); - } - - SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_ADDSTRING, 0, (LPARAM)Translate("System tray icon")); - SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_ADDSTRING, 0, (LPARAM)Translate("Popup window")); - SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_ADDSTRING, 0, (LPARAM)Translate("Message with [img] BBCode")); - SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_SETCURSEL, - DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGMETHOD, GG_KEYDEF_IMGMETHOD), 0); - break; - } - case WM_COMMAND: - { - if ((LOWORD(wParam) == IDC_UIN || LOWORD(wParam) == IDC_PASSWORD || LOWORD(wParam) == IDC_EMAIL) - && (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())) - return 0; - switch (LOWORD(wParam)) { - case IDC_EMAIL: - case IDC_UIN: - { - gg_optsdlgcheck(hwndDlg); - break; - } - case IDC_LEAVESTATUSMSG: - { - EnableWindow(GetDlgItem(hwndDlg, IDC_LEAVESTATUS), IsDlgButtonChecked(hwndDlg, IDC_LEAVESTATUSMSG)); - break; - } - case IDC_IMGRECEIVE: - { - EnableWindow(GetDlgItem(hwndDlg, IDC_IMGMETHOD), IsDlgButtonChecked(hwndDlg, IDC_IMGRECEIVE)); - break; - } - case IDC_LOSTPASS: - { - char email[128]; - uin_t uin; - GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - GetDlgItemText(hwndDlg, IDC_UIN, email, sizeof(email)); - uin = atoi(email); - GetDlgItemText(hwndDlg, IDC_EMAIL, email, sizeof(email)); - if (!strlen(email)) - MessageBox( - NULL, - Translate("You need to specify your registration e-mail first."), - GG_PROTONAME, - MB_OK | MB_ICONEXCLAMATION); - else if(MessageBox( - NULL, - Translate("Your password will be sent to your registration e-mail.\nDo you want to continue ?"), - GG_PROTONAME, - MB_OKCANCEL | MB_ICONQUESTION) == IDOK) - gg_remindpassword(gg, uin, email); - return FALSE; - } - case IDC_CREATEACCOUNT: - case IDC_REMOVEACCOUNT: - if(gg_isonline((GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA))) - { - if(MessageBox( - NULL, - Translate("You should disconnect before making any permanent changes with your account.\nDo you want to disconnect now ?"), - ((GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA))->proto.m_szModuleName, - MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL) - break; - else - gg_disconnect((GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA)); - } - case IDC_CHPASS: - case IDC_CHEMAIL: - { - // Readup data - GGUSERUTILDLGDATA dat; - int ret; - char pass[128], email[128]; - GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - GetDlgItemText(hwndDlg, IDC_UIN, pass, sizeof(pass)); - dat.uin = atoi(pass); - GetDlgItemText(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); - GetDlgItemText(hwndDlg, IDC_EMAIL, email, sizeof(email)); - dat.pass = pass; - dat.email = email; - dat.gg = gg; - if(LOWORD(wParam) == IDC_CREATEACCOUNT) - { - dat.mode = GG_USERUTIL_CREATE; - ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CREATEACCOUNT), hwndDlg, gg_userutildlgproc, (LPARAM)&dat); - } - else if(LOWORD(wParam) == IDC_CHPASS) - { - dat.mode = GG_USERUTIL_PASS; - ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CHPASS), hwndDlg, gg_userutildlgproc, (LPARAM)&dat); - } - else if(LOWORD(wParam) == IDC_CHEMAIL) - { - dat.mode = GG_USERUTIL_EMAIL; - ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CHEMAIL), hwndDlg, gg_userutildlgproc, (LPARAM)&dat); - } - else - { - dat.mode = GG_USERUTIL_REMOVE; - ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_REMOVEACCOUNT), hwndDlg, gg_userutildlgproc, (LPARAM)&dat); - } - - if(ret == IDOK) - { - DBVARIANT dbv; - DWORD num; - GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - // Show reload required window - ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW); - - // Update uin - if (num = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0)) - SetDlgItemText(hwndDlg, IDC_UIN, ditoa(num)); - else - SetDlgItemText(hwndDlg, IDC_UIN, ""); - - // Update password - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - SetDlgItemText(hwndDlg, IDC_PASSWORD, dbv.pszVal); - DBFreeVariant(&dbv); - } - else - SetDlgItemText(hwndDlg, IDC_PASSWORD, ""); - - // Update e-mail - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, &dbv)) { - SetDlgItemText(hwndDlg, IDC_EMAIL, dbv.pszVal); - DBFreeVariant(&dbv); - } - else - SetDlgItemText(hwndDlg, IDC_EMAIL, ""); - - // Update links - gg_optsdlgcheck(hwndDlg); - - // Remove details - if(LOWORD(wParam) != IDC_CHPASS && LOWORD(wParam) != IDC_CHEMAIL) - { - DBDeleteContactSetting(NULL, GG_PROTO, GG_KEY_NICK); - DBDeleteContactSetting(NULL, GG_PROTO, "NickName"); - DBDeleteContactSetting(NULL, GG_PROTO, "City"); - DBDeleteContactSetting(NULL, GG_PROTO, "FirstName"); - DBDeleteContactSetting(NULL, GG_PROTO, "LastName"); - DBDeleteContactSetting(NULL, GG_PROTO, "FamilyName"); - DBDeleteContactSetting(NULL, GG_PROTO, "CityOrigin"); - DBDeleteContactSetting(NULL, GG_PROTO, "Age"); - DBDeleteContactSetting(NULL, GG_PROTO, "BirthYear"); - DBDeleteContactSetting(NULL, GG_PROTO, "Gender"); - } - } - } - break; - } - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - } - case WM_NOTIFY: - { - switch (((LPNMHDR) lParam)->code) { - case PSN_APPLY: - { - GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - int status_flags = GG_STATUS_FLAG_UNKNOWN; - char str[128]; - uin_t uin; - - // Write Gadu-Gadu number & password - GetDlgItemText(hwndDlg, IDC_UIN, str, sizeof(str)); - uin = atoi(str); - GetDlgItemText(hwndDlg, IDC_PASSWORD, str, sizeof(str)); - CallService(MS_DB_CRYPT_ENCODESTRING, sizeof(str), (LPARAM) str); - gg_checknewuser(gg, uin, str); - DBWriteContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, uin); - DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, str); - - // Write Gadu-Gadu email - GetDlgItemText(hwndDlg, IDC_EMAIL, str, sizeof(str)); - DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, str); - - // Write checkboxes - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_FRIENDSONLY, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_FRIENDSONLY)); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWINVISIBLE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWINVISIBLE)); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_LEAVESTATUSMSG, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_LEAVESTATUSMSG)); - if (gg->gc_enabled) - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_IGNORECONF, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_IGNORECONF)); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGRECEIVE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_IMGRECEIVE)); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWLINKS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWLINKS)); - if (IsDlgButtonChecked(hwndDlg, IDC_SHOWLINKS)) - status_flags |= GG_STATUS_FLAG_SPAM; - EnterCriticalSection(&gg->sess_mutex); - gg_change_status_flags(gg->sess, status_flags); - LeaveCriticalSection(&gg->sess_mutex); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ENABLEAVATARS)); - - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGMETHOD, - (BYTE)SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_GETCURSEL, 0, 0)); - - // Write leave status - switch(SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_GETCURSEL, 0, 0)) - { - case 1: - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, ID_STATUS_ONLINE); - break; - case 2: - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, ID_STATUS_AWAY); - break; - case 3: - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, ID_STATUS_DND); - break; - case 4: - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, ID_STATUS_FREECHAT); - break; - case 5: - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, ID_STATUS_INVISIBLE); - break; - default: - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, GG_KEYDEF_LEAVESTATUS); - } - break; - } - } - break; - } - } - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////////////////////// -// Proc: Conference options dialog -static INT_PTR CALLBACK gg_confoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: - { - DWORD num; - GGPROTO *gg = (GGPROTO *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); - - TranslateDialogDefault(hwndDlg); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_ADDSTRING, 0, (LPARAM)Translate("Allow")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_ADDSTRING, 0, (LPARAM)Translate("Ask")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_ADDSTRING, 0, (LPARAM)Translate("Ignore")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_SETCURSEL, - DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL), 0); - - if (num = DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) - SetDlgItemText(hwndDlg, IDC_GC_COUNT_TOTAL, ditoa(num)); - - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)Translate("Allow")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)Translate("Ask")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)Translate("Ignore")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_SETCURSEL, - DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN), 0); - - if (num = DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN)) - SetDlgItemText(hwndDlg, IDC_GC_COUNT_UNKNOWN, ditoa(num)); - - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)Translate("Allow")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)Translate("Ask")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)Translate("Ignore")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_SETCURSEL, - DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_DEFAULT, GG_KEYDEF_GC_POLICY_DEFAULT), 0); - break; - } - case WM_COMMAND: - { - if ((LOWORD(wParam) == IDC_GC_COUNT_TOTAL || LOWORD(wParam) == IDC_GC_COUNT_UNKNOWN) - && (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())) - return 0; - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - } - case WM_NOTIFY: - { - switch (((LPNMHDR) lParam)->code) { - case PSN_APPLY: - { - char str[128]; - GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - // Write groupchat policy - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_TOTAL, - (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_GETCURSEL, 0, 0)); - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_UNKNOWN, - (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_GETCURSEL, 0, 0)); - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_DEFAULT, - (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_GETCURSEL, 0, 0)); - - GetDlgItemText(hwndDlg, IDC_GC_COUNT_TOTAL, str, sizeof(str)); - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_TOTAL, (WORD)atoi(str)); - GetDlgItemText(hwndDlg, IDC_GC_COUNT_UNKNOWN, str, sizeof(str)); - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_UNKNOWN, (WORD)atoi(str)); - - break; - } - } - break; - } - } - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////////////////////// -// Proc: Advanced options dialog -static INT_PTR CALLBACK gg_advoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: - { - DBVARIANT dbv; - DWORD num; - GGPROTO *gg = (GGPROTO *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); - - TranslateDialogDefault(hwndDlg); - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_SERVERHOSTS, &dbv)) { - SetDlgItemText(hwndDlg, IDC_HOST, dbv.pszVal); - DBFreeVariant(&dbv); - } - else - SetDlgItemText(hwndDlg, IDC_HOST, GG_KEYDEF_SERVERHOSTS); - - CheckDlgButton(hwndDlg, IDC_KEEPALIVE, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_KEEPALIVE, GG_KEYDEF_KEEPALIVE)); - CheckDlgButton(hwndDlg, IDC_SHOWCERRORS, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWCERRORS, GG_KEYDEF_SHOWCERRORS)); - CheckDlgButton(hwndDlg, IDC_ARECONNECT, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT)); - CheckDlgButton(hwndDlg, IDC_MSGACK, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_MSGACK, GG_KEYDEF_MSGACK)); - CheckDlgButton(hwndDlg, IDC_MANUALHOST, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_MANUALHOST, GG_KEYDEF_MANUALHOST)); - CheckDlgButton(hwndDlg, IDC_SSLCONN, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SSLCONN, GG_KEYDEF_SSLCONN)); - - EnableWindow(GetDlgItem(hwndDlg, IDC_HOST), IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST)); - EnableWindow(GetDlgItem(hwndDlg, IDC_PORT), IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST)); - - CheckDlgButton(hwndDlg, IDC_DIRECTCONNS, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_DIRECTCONNS, GG_KEYDEF_DIRECTCONNS)); - if (num = DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_DIRECTPORT, GG_KEYDEF_DIRECTPORT)) - SetDlgItemText(hwndDlg, IDC_DIRECTPORT, ditoa(num)); - CheckDlgButton(hwndDlg, IDC_FORWARDING, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_FORWARDING, GG_KEYDEF_FORWARDING)); - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_FORWARDHOST, &dbv)) { - SetDlgItemText(hwndDlg, IDC_FORWARDHOST, dbv.pszVal); - DBFreeVariant(&dbv); - } - if (num = DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_FORWARDPORT, GG_KEYDEF_FORWARDPORT)) - SetDlgItemText(hwndDlg, IDC_FORWARDPORT, ditoa(num)); - - EnableWindow(GetDlgItem(hwndDlg, IDC_DIRECTPORT), IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); - EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDING), IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); - EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDPORT), IsDlgButtonChecked(hwndDlg, IDC_FORWARDING) && IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); - EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDHOST), IsDlgButtonChecked(hwndDlg, IDC_FORWARDING) && IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); - break; - } - case WM_COMMAND: - { - if ((LOWORD(wParam) == IDC_DIRECTPORT || LOWORD(wParam) == IDC_FORWARDHOST || LOWORD(wParam) == IDC_FORWARDPORT) - && (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())) - return 0; - switch (LOWORD(wParam)) { - case IDC_MANUALHOST: - { - EnableWindow(GetDlgItem(hwndDlg, IDC_HOST), IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST)); - EnableWindow(GetDlgItem(hwndDlg, IDC_PORT), IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST)); - ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW); - break; - } - case IDC_DIRECTCONNS: - case IDC_FORWARDING: - { - EnableWindow(GetDlgItem(hwndDlg, IDC_DIRECTPORT), IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); - EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDING), IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); - EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDPORT), IsDlgButtonChecked(hwndDlg, IDC_FORWARDING) && IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); - EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDHOST), IsDlgButtonChecked(hwndDlg, IDC_FORWARDING) && IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); - ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW); - break; - } - } - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - } - case WM_NOTIFY: - { - switch (((LPNMHDR) lParam)->code) { - case PSN_APPLY: - { - char str[512]; - GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_KEEPALIVE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_KEEPALIVE)); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWCERRORS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWCERRORS)); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_ARECONNECT, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ARECONNECT)); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_MSGACK, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_MSGACK)); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_MANUALHOST, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST)); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_SSLCONN, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SSLCONN)); - - // Transfer settings - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_DIRECTCONNS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); - DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_FORWARDING, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_FORWARDING)); - - // Write custom servers - GetDlgItemText(hwndDlg, IDC_HOST, str, sizeof(str)); - DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_SERVERHOSTS, str); - - // Write direct port - GetDlgItemText(hwndDlg, IDC_DIRECTPORT, str, sizeof(str)); - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_DIRECTPORT, (WORD)atoi(str)); - // Write forwarding host - GetDlgItemText(hwndDlg, IDC_FORWARDHOST, str, sizeof(str)); - DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_FORWARDHOST, str); - GetDlgItemText(hwndDlg, IDC_FORWARDPORT, str, sizeof(str)); - DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_FORWARDPORT, (WORD)atoi(str)); - break; - } - } - break; - } - } - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////////// -// Info Page : Data -struct GGDETAILSDLGDATA -{ - GGPROTO *gg; - HANDLE hContact; - int disableUpdate; - int updating; -}; - -//////////////////////////////////////////////////////////////////////////////// -// Info Page : Proc -static INT_PTR CALLBACK gg_detailsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - struct GGDETAILSDLGDATA *dat = (struct GGDETAILSDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch(msg) - { - case WM_INITDIALOG: - { - TranslateDialogDefault(hwndDlg); - dat = (struct GGDETAILSDLGDATA *)mir_alloc(sizeof(struct GGDETAILSDLGDATA)); - dat->hContact=(HANDLE)lParam; - dat->disableUpdate = FALSE; - dat->updating = FALSE; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); - // Add genders - if (!dat->hContact) - { - SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)_T("")); // 0 - SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)Translate("Female")); // 1 - SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)Translate("Male")); // 2 - } - break; - } - - case WM_NOTIFY: - switch (((LPNMHDR)lParam)->idFrom) - { - case 0: - switch (((LPNMHDR)lParam)->code) - { - case PSN_PARAMCHANGED: - { - dat->gg = (GGPROTO *)((LPPSHNOTIFY)lParam)->lParam; - break; - } - case PSN_INFOCHANGED: - { - char *szProto; - HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lParam)->lParam; - GGPROTO *gg = dat->gg; - - // Show updated message - if(dat && dat->updating) - { - MessageBox( - NULL, - Translate("Your details has been uploaded to the public directory."), - GG_PROTONAME, - MB_OK | MB_ICONINFORMATION - ); - dat->updating = FALSE; - break; - } - - if (hContact == NULL) - szProto = GG_PROTO; - else - szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); - if (szProto == NULL) - break; - - // Disable when updating - if(dat) dat->disableUpdate = TRUE; - - SetValue(hwndDlg, IDC_UIN, hContact, szProto, GG_KEY_UIN, 0, hContact != NULL); - SetValue(hwndDlg, IDC_REALIP, hContact, szProto, GG_KEY_CLIENTIP, SVS_IP, hContact != NULL); - SetValue(hwndDlg, IDC_PORT, hContact, szProto, GG_KEY_CLIENTPORT, SVS_ZEROISUNSPEC, hContact != NULL); - SetValue(hwndDlg, IDC_VERSION, hContact, szProto, GG_KEY_CLIENTVERSION, SVS_GGVERSION, hContact != NULL); - - SetValue(hwndDlg, IDC_FIRSTNAME, hContact, szProto, "FirstName", SVS_NORMAL, hContact != NULL); - SetValue(hwndDlg, IDC_LASTNAME, hContact, szProto, "LastName", SVS_NORMAL, hContact != NULL); - SetValue(hwndDlg, IDC_NICKNAME, hContact, szProto, "NickName", SVS_NORMAL, hContact != NULL); - SetValue(hwndDlg, IDC_BIRTHYEAR, hContact, szProto, "BirthYear", SVS_ZEROISUNSPEC, hContact != NULL); - SetValue(hwndDlg, IDC_CITY, hContact, szProto, "City", SVS_NORMAL, hContact != NULL); - SetValue(hwndDlg, IDC_FAMILYNAME, hContact, szProto, "FamilyName", SVS_NORMAL, hContact != NULL); - SetValue(hwndDlg, IDC_CITYORIGIN, hContact, szProto, "CityOrigin", SVS_NORMAL, hContact != NULL); - - if (hContact) - { - SetValue(hwndDlg, IDC_GENDER, hContact, szProto, "Gender", SVS_GENDER, hContact != NULL); - SetValue(hwndDlg, IDC_STATUSDESCR, hContact, "CList", GG_KEY_STATUSDESCR, SVS_NORMAL, hContact != NULL); - } - else switch((char)DBGetContactSettingByte(hContact, GG_PROTO, "Gender", (BYTE)'?')) - { - case 'F': - SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_SETCURSEL, 1, 0); - break; - case 'M': - SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_SETCURSEL, 2, 0); - break; - default: - SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_SETCURSEL, 0, 0); - } - - // Disable when updating - if(dat) dat->disableUpdate = FALSE; - break; - } - } - break; - } - break; - case WM_COMMAND: - if (dat && !dat->hContact && LOWORD(wParam) == IDC_SAVE && HIWORD(wParam) == BN_CLICKED) - { - // Save user data - char text[256]; - gg_pubdir50_t req; - GGPROTO *gg = dat->gg; - - if (!gg_isonline(gg)) - { - MessageBox(NULL, - Translate("You have to be logged in before you can change your details."), - GG_PROTONAME, MB_OK | MB_ICONSTOP - ); - break; - } - - EnableWindow(GetDlgItem(hwndDlg, IDC_SAVE), FALSE); - - req = gg_pubdir50_new(GG_PUBDIR50_WRITE); - - GetDlgItemText(hwndDlg, IDC_FIRSTNAME, text, sizeof(text)); - if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, text); - - GetDlgItemText(hwndDlg, IDC_LASTNAME, text, sizeof(text)); - if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, text); - - GetDlgItemText(hwndDlg, IDC_NICKNAME, text, sizeof(text)); - if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, text); - - GetDlgItemText(hwndDlg, IDC_CITY, text, sizeof(text)); - if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_CITY, text); - - // Gadu-Gadu Female <-> Male - switch(SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_GETCURSEL, 0, 0)) - { - case 1: - gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_FEMALE); - break; - case 2: - gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_MALE); - break; - default: - gg_pubdir50_add(req, GG_PUBDIR50_GENDER, ""); - } - - GetDlgItemText(hwndDlg, IDC_BIRTHYEAR, text, sizeof(text)); - if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, text); - - GetDlgItemText(hwndDlg, IDC_FAMILYNAME, text, sizeof(text)); - if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_FAMILYNAME, text); - - GetDlgItemText(hwndDlg, IDC_CITYORIGIN, text, sizeof(text)); - if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_FAMILYCITY, text); - - // Run update - gg_pubdir50_seq_set(req, GG_SEQ_CHINFO); - EnterCriticalSection(&gg->sess_mutex); - gg_pubdir50(gg->sess, req); - LeaveCriticalSection(&gg->sess_mutex); - dat->updating = TRUE; - - gg_pubdir50_free(req); - } - - if(dat && !dat->hContact && !dat->disableUpdate && (HIWORD(wParam) == EN_CHANGE && ( - LOWORD(wParam) == IDC_NICKNAME || LOWORD(wParam) == IDC_FIRSTNAME || LOWORD(wParam) == IDC_LASTNAME || LOWORD(wParam) == IDC_FAMILYNAME || - LOWORD(wParam) == IDC_CITY || LOWORD(wParam) == IDC_CITYORIGIN || LOWORD(wParam) == IDC_BIRTHYEAR) || - HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_GENDER)) - EnableWindow(GetDlgItem(hwndDlg, IDC_SAVE), TRUE); - - switch(LOWORD(wParam)) - { - case IDCANCEL: - SendMessage(GetParent(hwndDlg),msg,wParam,lParam); - break; - } - break; - case WM_DESTROY: - if(dat) mir_free(dat); - break; - } - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////////// -// Info Page : Init -int gg_details_init(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, lParam, 0); - if ((szProto == NULL || strcmp(szProto, GG_PROTO)) && lParam || lParam && DBGetContactSettingByte((HANDLE)lParam, GG_PROTO, "ChatRoom", 0)) - return 0; - - // Here goes init - { - OPTIONSDIALOGPAGE odp = {0}; - - odp.cbSize = sizeof(odp); - odp.flags = ODPF_DONTTRANSLATE; - odp.hInstance = hInstance; - odp.pfnDlgProc = gg_detailsdlgproc; - odp.position = -1900000000; - odp.pszTemplate = ((HANDLE)lParam != NULL) ? MAKEINTRESOURCE(IDD_INFO_GG) : MAKEINTRESOURCE(IDD_CHINFO_GG); - odp.ptszTitle = GG_PROTONAME; - odp.dwInitParam = (LPARAM)gg; - UserInfo_AddPage(wParam, &odp); - } - - // Start search for user data - if ((HANDLE)lParam == NULL) - gg_getinfo((PROTO_INTERFACE *)gg, NULL, 0); - - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////////// -// Proc: Account manager options dialog -INT_PTR CALLBACK gg_acc_mgr_guidlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -//////////////////////////////////////////////////////////////////////////////////////////// -{ - switch (msg) { - case WM_INITDIALOG: - { - DBVARIANT dbv; - DWORD num; - GGPROTO *gg = (GGPROTO *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); - - TranslateDialogDefault(hwndDlg); - if (num = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0)) - SetDlgItemText(hwndDlg, IDC_UIN, ditoa(num)); - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - SetDlgItemText(hwndDlg, IDC_PASSWORD, dbv.pszVal); - DBFreeVariant(&dbv); - } - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, &dbv)) { - SetDlgItemText(hwndDlg, IDC_EMAIL, dbv.pszVal); - DBFreeVariant(&dbv); - } - break; - } - case WM_COMMAND: - { - switch (LOWORD(wParam)) { - case IDC_CREATEACCOUNT: - { - // Readup data - GGUSERUTILDLGDATA dat; - int ret; - char pass[128], email[128]; - GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - GetDlgItemText(hwndDlg, IDC_UIN, pass, sizeof(pass)); - dat.uin = atoi(pass); - GetDlgItemText(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); - GetDlgItemText(hwndDlg, IDC_EMAIL, email, sizeof(email)); - dat.pass = pass; - dat.email = email; - dat.gg = gg; - dat.mode = GG_USERUTIL_CREATE; - ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CREATEACCOUNT), hwndDlg, gg_userutildlgproc, (LPARAM)&dat); - - if(ret == IDOK) - { - DBVARIANT dbv; - DWORD num; - GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - // Show reload required window - ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW); - - // Update uin - if (num = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0)) - SetDlgItemText(hwndDlg, IDC_UIN, ditoa(num)); - else - SetDlgItemText(hwndDlg, IDC_UIN, ""); - - // Update password - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - SetDlgItemText(hwndDlg, IDC_PASSWORD, dbv.pszVal); - DBFreeVariant(&dbv); - } - else - SetDlgItemText(hwndDlg, IDC_PASSWORD, ""); - - // Update e-mail - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, &dbv)) { - SetDlgItemText(hwndDlg, IDC_EMAIL, dbv.pszVal); - DBFreeVariant(&dbv); - } - else - SetDlgItemText(hwndDlg, IDC_EMAIL, ""); - } - } - - } - break; - } - case WM_NOTIFY: - { - switch(((LPNMHDR)lParam)->idFrom) - { - case 0: - switch (((LPNMHDR) lParam)->code) { - case PSN_APPLY: - { - GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - char str[128]; - uin_t uin; - - // Write Gadu-Gadu number & password - GetDlgItemText(hwndDlg, IDC_UIN, str, sizeof(str)); - uin = atoi(str); - GetDlgItemText(hwndDlg, IDC_PASSWORD, str, sizeof(str)); - CallService(MS_DB_CRYPT_ENCODESTRING, sizeof(str), (LPARAM) str); - gg_checknewuser(gg, uin, str); - DBWriteContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, uin); - DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, str); - - // Write Gadu-Gadu email - GetDlgItemText(hwndDlg, IDC_EMAIL, str, sizeof(str)); - DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, str); - } - } - } - break; - } - } - return FALSE; -} diff --git a/protocols/Gadu-Gadu/dialogs.cpp b/protocols/Gadu-Gadu/dialogs.cpp new file mode 100644 index 0000000000..6a626f2d55 --- /dev/null +++ b/protocols/Gadu-Gadu/dialogs.cpp @@ -0,0 +1,1016 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 Adam Strzelecki +// +// 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 "gg.h" + +static INT_PTR CALLBACK gg_genoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); +static INT_PTR CALLBACK gg_confoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); +static INT_PTR CALLBACK gg_advoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); +extern INT_PTR CALLBACK gg_userutildlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); + +//////////////////////////////////////////////////////////////////////////////// +// SetValue + +#define SVS_NORMAL 0 +#define SVS_GENDER 1 +#define SVS_ZEROISUNSPEC 2 +#define SVS_IP 3 +#define SVS_COUNTRY 4 +#define SVS_MONTH 5 +#define SVS_SIGNED 6 +#define SVS_TIMEZONE 7 +#define SVS_GGVERSION 9 + +static void SetValue(HWND hwndDlg, int idCtrl, HANDLE hContact, char *szModule, char *szSetting, int special, int disableIfUndef) +{ + DBVARIANT dbv = {0}; + char str[80], *pstr = NULL; + int unspecified = 0; + + dbv.type = DBVT_DELETED; + if (szModule == NULL) unspecified = 1; + else unspecified = DBGetContactSettingW(hContact, szModule, szSetting, &dbv); + if (!unspecified) { + switch (dbv.type) { + case DBVT_BYTE: + if (special == SVS_GENDER) { + if (dbv.cVal == 'M') pstr = Translate("Male"); + else if (dbv.cVal == 'F') pstr = Translate("Female"); + else unspecified = 1; + } + else if (special == SVS_MONTH) { + if (dbv.bVal > 0 && dbv.bVal <= 12) { + pstr = str; + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1 - 1 + dbv.bVal, str, SIZEOF(str)); + } + else unspecified = 1; + } + else if (special == SVS_TIMEZONE) { + if (dbv.cVal == -100) unspecified = 1; + else { + pstr = str; + mir_snprintf(str, SIZEOF(str), dbv.cVal ? "GMT%+d:%02d" : "GMT", -dbv.cVal / 2, (dbv.cVal & 1) * 30); + } + } + else { + unspecified = (special == SVS_ZEROISUNSPEC && dbv.bVal == 0); + pstr = _itoa(special == SVS_SIGNED ? dbv.cVal : dbv.bVal, str, 10); + } + break; + case DBVT_WORD: + if (special == SVS_COUNTRY) { + pstr = (char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, dbv.wVal, 0); + unspecified = pstr == NULL; + } + else { + unspecified = (special == SVS_ZEROISUNSPEC && dbv.wVal == 0); + pstr = _itoa(special == SVS_SIGNED ? dbv.sVal : dbv.wVal, str, 10); + } + break; + case DBVT_DWORD: + unspecified = (special == SVS_ZEROISUNSPEC && dbv.dVal == 0); + if (special == SVS_IP) { + struct in_addr ia; + ia.S_un.S_addr = htonl(dbv.dVal); + pstr = inet_ntoa(ia); + if (dbv.dVal == 0) unspecified = 1; + } + else if (special == SVS_GGVERSION) + pstr = (char *)gg_version2string(dbv.dVal); + else + pstr = _itoa(special == SVS_SIGNED ? dbv.lVal : dbv.dVal, str, 10); + break; + case DBVT_ASCIIZ: + unspecified = (special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0'); + pstr = dbv.pszVal; + break; + default: pstr = str; lstrcpyA(str, "???"); break; + } + } + + if (disableIfUndef) { + EnableWindow(GetDlgItem(hwndDlg, idCtrl), !unspecified); + if (unspecified) + SetDlgItemText(hwndDlg, idCtrl, TranslateT("")); + else + SetDlgItemTextA(hwndDlg, idCtrl, pstr); + } + else { + EnableWindow(GetDlgItem(hwndDlg, idCtrl), TRUE); + if (!unspecified) + SetDlgItemTextA(hwndDlg, idCtrl, pstr); + } + DBFreeVariant(&dbv); +} + +//////////////////////////////////////////////////////////////////////////////// +// Options Page : Init + +int GGPROTO::options_init(WPARAM wParam, LPARAM lParam) +{ + OPTIONSDIALOGPAGE odp = { 0 }; + odp.cbSize = sizeof(odp); + odp.flags = ODPF_TCHAR; + odp.position = 1003000; + odp.hInstance = hInstance; + odp.ptszGroup = LPGENT("Network"); + odp.ptszTitle = m_tszUserName; + odp.dwInitParam = (LPARAM)this; + + odp.ptszTab = LPGENT("General"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_GG_GENERAL); + odp.pfnDlgProc = gg_genoptsdlgproc; + odp.flags = ODPF_TCHAR | ODPF_BOLDGROUPS | ODPF_DONTTRANSLATE; + Options_AddPage(wParam, &odp); + + odp.ptszTab = LPGENT("Conference"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_GG_CONFERENCE); + odp.pfnDlgProc = gg_confoptsdlgproc; + Options_AddPage(wParam, &odp); + + odp.ptszTab = LPGENT("Advanced"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_GG_ADVANCED); + odp.pfnDlgProc = gg_advoptsdlgproc; + odp.flags |= ODPF_EXPERTONLY; + Options_AddPage(wParam, &odp); + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// Check if new user data has been filled in for specified account +void GGPROTO::checknewuser(uin_t uin, const char* passwd) +{ + char oldpasswd[128]; + DBVARIANT dbv; + uin_t olduin = (uin_t)db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0); + + oldpasswd[0] = '\0'; + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) + { + if (dbv.pszVal) strcpy(oldpasswd, dbv.pszVal); + DBFreeVariant(&dbv); + } + + if (uin > 0 && strlen(passwd) > 0 && (uin != olduin || strcmp(oldpasswd, passwd))) + check_first_conn = 1; +} + +//////////////////////////////////////////////////////////////////////////////// +// Options Page : Proc + +static void gg_optsdlgcheck(HWND hwndDlg) +{ + TCHAR text[128]; + GetDlgItemText(hwndDlg, IDC_UIN, text, SIZEOF(text)); + if (text[0]) { + GetDlgItemText(hwndDlg, IDC_EMAIL, text, SIZEOF(text)); + if (text[0]) + ShowWindow(GetDlgItem(hwndDlg, IDC_CHEMAIL), SW_SHOW); + else + ShowWindow(GetDlgItem(hwndDlg, IDC_CHEMAIL), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_CHPASS), SW_SHOW); + ShowWindow(GetDlgItem(hwndDlg, IDC_LOSTPASS), SW_SHOW); + ShowWindow(GetDlgItem(hwndDlg, IDC_REMOVEACCOUNT), SW_SHOW); + ShowWindow(GetDlgItem(hwndDlg, IDC_CREATEACCOUNT), SW_HIDE); + } + else { + ShowWindow(GetDlgItem(hwndDlg, IDC_REMOVEACCOUNT), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_LOSTPASS), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_CHPASS), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_CHEMAIL), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_CREATEACCOUNT), SW_SHOW); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// Proc: General options dialog +static INT_PTR CALLBACK gg_genoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch (msg) { + case WM_INITDIALOG: + { + DBVARIANT dbv; + DWORD num; + GGPROTO *gg = (GGPROTO *)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); + + TranslateDialogDefault(hwndDlg); + if (num = db_get_b(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) + { + SetDlgItemTextA(hwndDlg, IDC_UIN, ditoa(num)); + ShowWindow(GetDlgItem(hwndDlg, IDC_CREATEACCOUNT), SW_HIDE); + } + else + { + ShowWindow(GetDlgItem(hwndDlg, IDC_CHPASS), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_REMOVEACCOUNT), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_LOSTPASS), SW_HIDE); + } + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + SetDlgItemTextA(hwndDlg, IDC_PASSWORD, dbv.pszVal); + DBFreeVariant(&dbv); + } + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv, DBVT_ASCIIZ)) { + SetDlgItemTextA(hwndDlg, IDC_EMAIL, dbv.pszVal); + DBFreeVariant(&dbv); + } + else + { + ShowWindow(GetDlgItem(hwndDlg, IDC_LOSTPASS), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_CHPASS), SW_HIDE); + } + + CheckDlgButton(hwndDlg, IDC_FRIENDSONLY, db_get_b(NULL, gg->m_szModuleName, GG_KEY_FRIENDSONLY, GG_KEYDEF_FRIENDSONLY)); + CheckDlgButton(hwndDlg, IDC_SHOWINVISIBLE, db_get_b(NULL, gg->m_szModuleName, GG_KEY_SHOWINVISIBLE, GG_KEYDEF_SHOWINVISIBLE)); + CheckDlgButton(hwndDlg, IDC_LEAVESTATUSMSG, db_get_b(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUSMSG, GG_KEYDEF_LEAVESTATUSMSG)); + if (gg->gc_enabled) + CheckDlgButton(hwndDlg, IDC_IGNORECONF, db_get_b(NULL, gg->m_szModuleName, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF)); + else + { + EnableWindow(GetDlgItem(hwndDlg, IDC_IGNORECONF), FALSE); + CheckDlgButton(hwndDlg, IDC_IGNORECONF, TRUE); + } + CheckDlgButton(hwndDlg, IDC_IMGRECEIVE, db_get_b(NULL, gg->m_szModuleName, GG_KEY_IMGRECEIVE, GG_KEYDEF_IMGRECEIVE)); + CheckDlgButton(hwndDlg, IDC_SHOWLINKS, db_get_b(NULL, gg->m_szModuleName, GG_KEY_SHOWLINKS, GG_KEYDEF_SHOWLINKS)); + CheckDlgButton(hwndDlg, IDC_ENABLEAVATARS, db_get_b(NULL, gg->m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)); + + EnableWindow(GetDlgItem(hwndDlg, IDC_LEAVESTATUS), IsDlgButtonChecked(hwndDlg, IDC_LEAVESTATUSMSG)); + EnableWindow(GetDlgItem(hwndDlg, IDC_IMGMETHOD), IsDlgButtonChecked(hwndDlg, IDC_IMGRECEIVE)); + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("")); // 0 + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Online")); // ID_STATUS_ONLINE + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Away")); // ID_STATUS_AWAY + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("DND")); // ID_STATUS_DND + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Free for chat")); // ID_STATUS_FREECHAT + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Invisible")); // ID_STATUS_INVISIBLE + switch(db_get_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, GG_KEYDEF_LEAVESTATUS)) { + case ID_STATUS_ONLINE: + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 1, 0); + break; + case ID_STATUS_AWAY: + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 2, 0); + break; + case ID_STATUS_DND: + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 3, 0); + break; + case ID_STATUS_FREECHAT: + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 4, 0); + break; + case ID_STATUS_INVISIBLE: + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 5, 0); + break; + default: + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 0, 0); + } + + SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_ADDSTRING, 0, (LPARAM)TranslateT("System tray icon")); + SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_ADDSTRING, 0, (LPARAM)TranslateT("Popup window")); + SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_ADDSTRING, 0, (LPARAM)TranslateT("Message with [img] BBCode")); + SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_SETCURSEL, + db_get_b(NULL, gg->m_szModuleName, GG_KEY_IMGMETHOD, GG_KEYDEF_IMGMETHOD), 0); + break; + } + case WM_COMMAND: + { + if ((LOWORD(wParam) == IDC_UIN || LOWORD(wParam) == IDC_PASSWORD || LOWORD(wParam) == IDC_EMAIL) + && (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())) + return 0; + + switch (LOWORD(wParam)) { + case IDC_EMAIL: + case IDC_UIN: + gg_optsdlgcheck(hwndDlg); + break; + + case IDC_LEAVESTATUSMSG: + EnableWindow(GetDlgItem(hwndDlg, IDC_LEAVESTATUS), IsDlgButtonChecked(hwndDlg, IDC_LEAVESTATUSMSG)); + break; + + case IDC_IMGRECEIVE: + EnableWindow(GetDlgItem(hwndDlg, IDC_IMGMETHOD), IsDlgButtonChecked(hwndDlg, IDC_IMGRECEIVE)); + break; + + case IDC_LOSTPASS: + { + char email[128]; + uin_t uin; + GetDlgItemTextA(hwndDlg, IDC_UIN, email, sizeof(email)); + uin = atoi(email); + GetDlgItemTextA(hwndDlg, IDC_EMAIL, email, sizeof(email)); + if (!strlen(email)) + MessageBox(NULL, TranslateT("You need to specify your registration e-mail first."), + gg->m_tszUserName, MB_OK | MB_ICONEXCLAMATION); + else if (MessageBox(NULL, + TranslateT("Your password will be sent to your registration e-mail.\nDo you want to continue ?"), + gg->m_tszUserName, + MB_OKCANCEL | MB_ICONQUESTION) == IDOK) + gg->remindpassword(uin, email); + return FALSE; + } + case IDC_CREATEACCOUNT: + case IDC_REMOVEACCOUNT: + if (gg->isonline()) + { + if (MessageBox( + NULL, + TranslateT("You should disconnect before making any permanent changes with your account.\nDo you want to disconnect now ?"), + gg->m_tszUserName, + MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL) + break; + else + gg->disconnect(); + } + case IDC_CHPASS: + case IDC_CHEMAIL: + { + // Readup data + GGUSERUTILDLGDATA dat; + int ret; + char pass[128], email[128]; + GetDlgItemTextA(hwndDlg, IDC_UIN, pass, sizeof(pass)); + dat.uin = atoi(pass); + GetDlgItemTextA(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); + GetDlgItemTextA(hwndDlg, IDC_EMAIL, email, sizeof(email)); + dat.pass = pass; + dat.email = email; + dat.gg = gg; + if (LOWORD(wParam) == IDC_CREATEACCOUNT) + { + dat.mode = GG_USERUTIL_CREATE; + ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CREATEACCOUNT), hwndDlg, gg_userutildlgproc, (LPARAM)&dat); + } + else if (LOWORD(wParam) == IDC_CHPASS) + { + dat.mode = GG_USERUTIL_PASS; + ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CHPASS), hwndDlg, gg_userutildlgproc, (LPARAM)&dat); + } + else if (LOWORD(wParam) == IDC_CHEMAIL) + { + dat.mode = GG_USERUTIL_EMAIL; + ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CHEMAIL), hwndDlg, gg_userutildlgproc, (LPARAM)&dat); + } + else + { + dat.mode = GG_USERUTIL_REMOVE; + ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_REMOVEACCOUNT), hwndDlg, gg_userutildlgproc, (LPARAM)&dat); + } + + if (ret == IDOK) + { + DBVARIANT dbv; + DWORD num; + // Show reload required window + ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW); + + // Update uin + if (num = db_get_b(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) + SetDlgItemTextA(hwndDlg, IDC_UIN, ditoa(num)); + else + SetDlgItemTextA(hwndDlg, IDC_UIN, ""); + + // Update password + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + SetDlgItemTextA(hwndDlg, IDC_PASSWORD, dbv.pszVal); + DBFreeVariant(&dbv); + } + else SetDlgItemTextA(hwndDlg, IDC_PASSWORD, ""); + + // Update e-mail + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv, DBVT_ASCIIZ)) { + SetDlgItemTextA(hwndDlg, IDC_EMAIL, dbv.pszVal); + DBFreeVariant(&dbv); + } + else SetDlgItemTextA(hwndDlg, IDC_EMAIL, ""); + + // Update links + gg_optsdlgcheck(hwndDlg); + + // Remove details + if (LOWORD(wParam) != IDC_CHPASS && LOWORD(wParam) != IDC_CHEMAIL) + { + db_unset(NULL, gg->m_szModuleName, GG_KEY_NICK); + db_unset(NULL, gg->m_szModuleName, "NickName"); + db_unset(NULL, gg->m_szModuleName, "City"); + db_unset(NULL, gg->m_szModuleName, "FirstName"); + db_unset(NULL, gg->m_szModuleName, "LastName"); + db_unset(NULL, gg->m_szModuleName, "FamilyName"); + db_unset(NULL, gg->m_szModuleName, "CityOrigin"); + db_unset(NULL, gg->m_szModuleName, "Age"); + db_unset(NULL, gg->m_szModuleName, "BirthYear"); + db_unset(NULL, gg->m_szModuleName, "Gender"); + } + } + } + break; + } + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + } + case WM_NOTIFY: + { + switch (((LPNMHDR) lParam)->code) { + case PSN_APPLY: + { + int status_flags = GG_STATUS_FLAG_UNKNOWN; + char str[128]; + uin_t uin; + + // Write Gadu-Gadu number & password + GetDlgItemTextA(hwndDlg, IDC_UIN, str, sizeof(str)); + uin = atoi(str); + GetDlgItemTextA(hwndDlg, IDC_PASSWORD, str, sizeof(str)); + CallService(MS_DB_CRYPT_ENCODESTRING, sizeof(str), (LPARAM) str); + gg->checknewuser(uin, str); + db_set_w(NULL, gg->m_szModuleName, GG_KEY_UIN, uin); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, str); + + // Write Gadu-Gadu email + GetDlgItemTextA(hwndDlg, IDC_EMAIL, str, sizeof(str)); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, str); + + // Write checkboxes + db_set_b(NULL, gg->m_szModuleName, GG_KEY_FRIENDSONLY, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_FRIENDSONLY)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_SHOWINVISIBLE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWINVISIBLE)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUSMSG, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_LEAVESTATUSMSG)); + if (gg->gc_enabled) + db_set_b(NULL, gg->m_szModuleName, GG_KEY_IGNORECONF, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_IGNORECONF)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_IMGRECEIVE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_IMGRECEIVE)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_SHOWLINKS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWLINKS)); + if (IsDlgButtonChecked(hwndDlg, IDC_SHOWLINKS)) + status_flags |= GG_STATUS_FLAG_SPAM; + EnterCriticalSection(&gg->sess_mutex); + gg_change_status_flags(gg->sess, status_flags); + LeaveCriticalSection(&gg->sess_mutex); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_ENABLEAVATARS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ENABLEAVATARS)); + + db_set_b(NULL, gg->m_szModuleName, GG_KEY_IMGMETHOD, + (BYTE)SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_GETCURSEL, 0, 0)); + + // Write leave status + switch(SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_GETCURSEL, 0, 0)) + { + case 1: + db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_ONLINE); + break; + case 2: + db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_AWAY); + break; + case 3: + db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_DND); + break; + case 4: + db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_FREECHAT); + break; + case 5: + db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_INVISIBLE); + break; + default: + db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, GG_KEYDEF_LEAVESTATUS); + } + break; + } + } + break; + } + } + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// Proc: Conference options dialog + +static INT_PTR CALLBACK gg_confoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch (msg) { + case WM_INITDIALOG: + { + DWORD num; + GGPROTO *gg = (GGPROTO *)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); + + TranslateDialogDefault(hwndDlg); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_ADDSTRING, 0, (LPARAM)TranslateT("Allow")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ask")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ignore")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_SETCURSEL, + db_get_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL), 0); + + if (num = db_get_w(NULL, gg->m_szModuleName, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) + SetDlgItemTextA(hwndDlg, IDC_GC_COUNT_TOTAL, ditoa(num)); + + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)TranslateT("Allow")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ask")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ignore")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_SETCURSEL, + db_get_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN), 0); + + if (num = db_get_w(NULL, gg->m_szModuleName, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN)) + SetDlgItemTextA(hwndDlg, IDC_GC_COUNT_UNKNOWN, ditoa(num)); + + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)TranslateT("Allow")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ask")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ignore")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_SETCURSEL, + db_get_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_DEFAULT, GG_KEYDEF_GC_POLICY_DEFAULT), 0); + break; + } + case WM_COMMAND: + { + if ((LOWORD(wParam) == IDC_GC_COUNT_TOTAL || LOWORD(wParam) == IDC_GC_COUNT_UNKNOWN) + && (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())) + return 0; + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + } + case WM_NOTIFY: + { + switch (((LPNMHDR) lParam)->code) { + case PSN_APPLY: + { + char str[128]; + + // Write groupchat policy + db_set_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_TOTAL, + (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_GETCURSEL, 0, 0)); + db_set_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_UNKNOWN, + (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_GETCURSEL, 0, 0)); + db_set_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_DEFAULT, + (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_GETCURSEL, 0, 0)); + + GetDlgItemTextA(hwndDlg, IDC_GC_COUNT_TOTAL, str, sizeof(str)); + db_set_w(NULL, gg->m_szModuleName, GG_KEY_GC_COUNT_TOTAL, (WORD)atoi(str)); + GetDlgItemTextA(hwndDlg, IDC_GC_COUNT_UNKNOWN, str, sizeof(str)); + db_set_w(NULL, gg->m_szModuleName, GG_KEY_GC_COUNT_UNKNOWN, (WORD)atoi(str)); + + break; + } + } + break; + } + } + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// Proc: Advanced options dialog +static INT_PTR CALLBACK gg_advoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch (msg) { + case WM_INITDIALOG: + { + DBVARIANT dbv; + DWORD num; + GGPROTO *gg = (GGPROTO *)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); + + TranslateDialogDefault(hwndDlg); + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_SERVERHOSTS, &dbv, DBVT_ASCIIZ)) { + SetDlgItemTextA(hwndDlg, IDC_HOST, dbv.pszVal); + DBFreeVariant(&dbv); + } + else SetDlgItemTextA(hwndDlg, IDC_HOST, GG_KEYDEF_SERVERHOSTS); + + CheckDlgButton(hwndDlg, IDC_KEEPALIVE, db_get_b(NULL, gg->m_szModuleName, GG_KEY_KEEPALIVE, GG_KEYDEF_KEEPALIVE)); + CheckDlgButton(hwndDlg, IDC_SHOWCERRORS, db_get_b(NULL, gg->m_szModuleName, GG_KEY_SHOWCERRORS, GG_KEYDEF_SHOWCERRORS)); + CheckDlgButton(hwndDlg, IDC_ARECONNECT, db_get_b(NULL, gg->m_szModuleName, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT)); + CheckDlgButton(hwndDlg, IDC_MSGACK, db_get_b(NULL, gg->m_szModuleName, GG_KEY_MSGACK, GG_KEYDEF_MSGACK)); + CheckDlgButton(hwndDlg, IDC_MANUALHOST, db_get_b(NULL, gg->m_szModuleName, GG_KEY_MANUALHOST, GG_KEYDEF_MANUALHOST)); + CheckDlgButton(hwndDlg, IDC_SSLCONN, db_get_b(NULL, gg->m_szModuleName, GG_KEY_SSLCONN, GG_KEYDEF_SSLCONN)); + + EnableWindow(GetDlgItem(hwndDlg, IDC_HOST), IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST)); + EnableWindow(GetDlgItem(hwndDlg, IDC_PORT), IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST)); + + CheckDlgButton(hwndDlg, IDC_DIRECTCONNS, db_get_b(NULL, gg->m_szModuleName, GG_KEY_DIRECTCONNS, GG_KEYDEF_DIRECTCONNS)); + if (num = db_get_w(NULL, gg->m_szModuleName, GG_KEY_DIRECTPORT, GG_KEYDEF_DIRECTPORT)) + SetDlgItemTextA(hwndDlg, IDC_DIRECTPORT, ditoa(num)); + CheckDlgButton(hwndDlg, IDC_FORWARDING, db_get_b(NULL, gg->m_szModuleName, GG_KEY_FORWARDING, GG_KEYDEF_FORWARDING)); + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_FORWARDHOST, &dbv, DBVT_ASCIIZ)) { + SetDlgItemTextA(hwndDlg, IDC_FORWARDHOST, dbv.pszVal); + DBFreeVariant(&dbv); + } + if (num = db_get_w(NULL, gg->m_szModuleName, GG_KEY_FORWARDPORT, GG_KEYDEF_FORWARDPORT)) + SetDlgItemTextA(hwndDlg, IDC_FORWARDPORT, ditoa(num)); + + EnableWindow(GetDlgItem(hwndDlg, IDC_DIRECTPORT), IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); + EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDING), IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); + EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDPORT), IsDlgButtonChecked(hwndDlg, IDC_FORWARDING) && IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); + EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDHOST), IsDlgButtonChecked(hwndDlg, IDC_FORWARDING) && IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); + break; + } + case WM_COMMAND: + { + if ((LOWORD(wParam) == IDC_DIRECTPORT || LOWORD(wParam) == IDC_FORWARDHOST || LOWORD(wParam) == IDC_FORWARDPORT) + && (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())) + return 0; + switch (LOWORD(wParam)) { + case IDC_MANUALHOST: + { + EnableWindow(GetDlgItem(hwndDlg, IDC_HOST), IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST)); + EnableWindow(GetDlgItem(hwndDlg, IDC_PORT), IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST)); + ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW); + break; + } + case IDC_DIRECTCONNS: + case IDC_FORWARDING: + { + EnableWindow(GetDlgItem(hwndDlg, IDC_DIRECTPORT), IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); + EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDING), IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); + EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDPORT), IsDlgButtonChecked(hwndDlg, IDC_FORWARDING) && IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); + EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDHOST), IsDlgButtonChecked(hwndDlg, IDC_FORWARDING) && IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); + ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW); + break; + } + } + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + } + case WM_NOTIFY: + { + switch (((LPNMHDR) lParam)->code) { + case PSN_APPLY: + { + char str[512]; + db_set_b(NULL, gg->m_szModuleName, GG_KEY_KEEPALIVE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_KEEPALIVE)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_SHOWCERRORS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWCERRORS)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_ARECONNECT, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ARECONNECT)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_MSGACK, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_MSGACK)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_MANUALHOST, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_SSLCONN, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SSLCONN)); + + // Transfer settings + db_set_b(NULL, gg->m_szModuleName, GG_KEY_DIRECTCONNS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_FORWARDING, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_FORWARDING)); + + // Write custom servers + GetDlgItemTextA(hwndDlg, IDC_HOST, str, sizeof(str)); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_SERVERHOSTS, str); + + // Write direct port + GetDlgItemTextA(hwndDlg, IDC_DIRECTPORT, str, sizeof(str)); + db_set_w(NULL, gg->m_szModuleName, GG_KEY_DIRECTPORT, (WORD)atoi(str)); + // Write forwarding host + GetDlgItemTextA(hwndDlg, IDC_FORWARDHOST, str, sizeof(str)); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_FORWARDHOST, str); + GetDlgItemTextA(hwndDlg, IDC_FORWARDPORT, str, sizeof(str)); + db_set_w(NULL, gg->m_szModuleName, GG_KEY_FORWARDPORT, (WORD)atoi(str)); + break; + } + } + break; + } + } + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////////// +// Info Page : Data +struct GGDETAILSDLGDATA +{ + GGPROTO *gg; + HANDLE hContact; + int disableUpdate; + int updating; +}; + +//////////////////////////////////////////////////////////////////////////////// +// Info Page : Proc +static INT_PTR CALLBACK gg_detailsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct GGDETAILSDLGDATA *dat = (struct GGDETAILSDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch(msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + + dat = (struct GGDETAILSDLGDATA *)mir_alloc(sizeof(struct GGDETAILSDLGDATA)); + dat->hContact=(HANDLE)lParam; + dat->disableUpdate = FALSE; + dat->updating = FALSE; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); + // Add genders + if (!dat->hContact) + { + SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)_T("")); // 0 + SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)TranslateT("Female")); // 1 + SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)TranslateT("Male")); // 2 + } + break; + + case WM_NOTIFY: + switch (((LPNMHDR)lParam)->idFrom) { + case 0: + switch (((LPNMHDR)lParam)->code) { + case PSN_PARAMCHANGED: + dat->gg = (GGPROTO *)((LPPSHNOTIFY)lParam)->lParam; + break; + + case PSN_INFOCHANGED: + { + char *szProto; + HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lParam)->lParam; + GGPROTO *gg = dat->gg; + + // Show updated message + if (dat && dat->updating) + { + MessageBox(NULL, TranslateT("Your details has been uploaded to the public directory."), + gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); + dat->updating = FALSE; + break; + } + + if (hContact == NULL) + szProto = gg->m_szModuleName; + else + szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + if (szProto == NULL) + break; + + // Disable when updating + if (dat) dat->disableUpdate = TRUE; + + SetValue(hwndDlg, IDC_UIN, hContact, szProto, GG_KEY_UIN, 0, hContact != NULL); + SetValue(hwndDlg, IDC_REALIP, hContact, szProto, GG_KEY_CLIENTIP, SVS_IP, hContact != NULL); + SetValue(hwndDlg, IDC_PORT, hContact, szProto, GG_KEY_CLIENTPORT, SVS_ZEROISUNSPEC, hContact != NULL); + SetValue(hwndDlg, IDC_VERSION, hContact, szProto, GG_KEY_CLIENTVERSION, SVS_GGVERSION, hContact != NULL); + + SetValue(hwndDlg, IDC_FIRSTNAME, hContact, szProto, "FirstName", SVS_NORMAL, hContact != NULL); + SetValue(hwndDlg, IDC_LASTNAME, hContact, szProto, "LastName", SVS_NORMAL, hContact != NULL); + SetValue(hwndDlg, IDC_NICKNAME, hContact, szProto, "NickName", SVS_NORMAL, hContact != NULL); + SetValue(hwndDlg, IDC_BIRTHYEAR, hContact, szProto, "BirthYear", SVS_ZEROISUNSPEC, hContact != NULL); + SetValue(hwndDlg, IDC_CITY, hContact, szProto, "City", SVS_NORMAL, hContact != NULL); + SetValue(hwndDlg, IDC_FAMILYNAME, hContact, szProto, "FamilyName", SVS_NORMAL, hContact != NULL); + SetValue(hwndDlg, IDC_CITYORIGIN, hContact, szProto, "CityOrigin", SVS_NORMAL, hContact != NULL); + + if (hContact) + { + SetValue(hwndDlg, IDC_GENDER, hContact, szProto, "Gender", SVS_GENDER, hContact != NULL); + SetValue(hwndDlg, IDC_STATUSDESCR, hContact, "CList", GG_KEY_STATUSDESCR, SVS_NORMAL, hContact != NULL); + } + else switch((char)db_get_b(hContact, gg->m_szModuleName, "Gender", (BYTE)'?')) + { + case 'F': + SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_SETCURSEL, 1, 0); + break; + case 'M': + SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_SETCURSEL, 2, 0); + break; + default: + SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_SETCURSEL, 0, 0); + } + + // Disable when updating + if (dat) dat->disableUpdate = FALSE; + break; + } + } + break; + } + break; + case WM_COMMAND: + if (dat && !dat->hContact && LOWORD(wParam) == IDC_SAVE && HIWORD(wParam) == BN_CLICKED) + { + // Save user data + char text[256]; + gg_pubdir50_t req; + GGPROTO *gg = dat->gg; + + if (!gg->isonline()) + { + MessageBox(NULL, + TranslateT("You have to be logged in before you can change your details."), + gg->m_tszUserName, MB_OK | MB_ICONSTOP); + break; + } + + EnableWindow(GetDlgItem(hwndDlg, IDC_SAVE), FALSE); + + req = gg_pubdir50_new(GG_PUBDIR50_WRITE); + + GetDlgItemTextA(hwndDlg, IDC_FIRSTNAME, text, sizeof(text)); + if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, text); + + GetDlgItemTextA(hwndDlg, IDC_LASTNAME, text, sizeof(text)); + if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, text); + + GetDlgItemTextA(hwndDlg, IDC_NICKNAME, text, sizeof(text)); + if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, text); + + GetDlgItemTextA(hwndDlg, IDC_CITY, text, sizeof(text)); + if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_CITY, text); + + // Gadu-Gadu Female <-> Male + switch(SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_GETCURSEL, 0, 0)) + { + case 1: + gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_FEMALE); + break; + case 2: + gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_MALE); + break; + default: + gg_pubdir50_add(req, GG_PUBDIR50_GENDER, ""); + } + + GetDlgItemTextA(hwndDlg, IDC_BIRTHYEAR, text, sizeof(text)); + if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, text); + + GetDlgItemTextA(hwndDlg, IDC_FAMILYNAME, text, sizeof(text)); + if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_FAMILYNAME, text); + + GetDlgItemTextA(hwndDlg, IDC_CITYORIGIN, text, sizeof(text)); + if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_FAMILYCITY, text); + + // Run update + gg_pubdir50_seq_set(req, GG_SEQ_CHINFO); + EnterCriticalSection(&gg->sess_mutex); + gg_pubdir50(gg->sess, req); + LeaveCriticalSection(&gg->sess_mutex); + dat->updating = TRUE; + + gg_pubdir50_free(req); + } + + if (dat && !dat->hContact && !dat->disableUpdate && (HIWORD(wParam) == EN_CHANGE && ( + LOWORD(wParam) == IDC_NICKNAME || LOWORD(wParam) == IDC_FIRSTNAME || LOWORD(wParam) == IDC_LASTNAME || LOWORD(wParam) == IDC_FAMILYNAME || + LOWORD(wParam) == IDC_CITY || LOWORD(wParam) == IDC_CITYORIGIN || LOWORD(wParam) == IDC_BIRTHYEAR) || + HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_GENDER)) + EnableWindow(GetDlgItem(hwndDlg, IDC_SAVE), TRUE); + + switch(LOWORD(wParam)) { + case IDCANCEL: + SendMessage(GetParent(hwndDlg),msg,wParam,lParam); + break; + } + break; + + case WM_DESTROY: + if (dat) mir_free(dat); + break; + } + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////////// +// Info Page : Init + +int GGPROTO::details_init(WPARAM wParam, LPARAM lParam) +{ + char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, lParam, 0); + if ((szProto == NULL || strcmp(szProto, m_szModuleName)) && lParam || lParam && db_get_b((HANDLE)lParam, m_szModuleName, "ChatRoom", 0)) + return 0; + + // Here goes init + { + OPTIONSDIALOGPAGE odp = {0}; + + odp.cbSize = sizeof(odp); + odp.flags = ODPF_DONTTRANSLATE | ODPF_TCHAR; + odp.hInstance = hInstance; + odp.pfnDlgProc = gg_detailsdlgproc; + odp.position = -1900000000; + odp.pszTemplate = ((HANDLE)lParam != NULL) ? MAKEINTRESOURCEA(IDD_INFO_GG) : MAKEINTRESOURCEA(IDD_CHINFO_GG); + odp.ptszTitle = m_tszUserName; + odp.dwInitParam = (LPARAM)this; + UserInfo_AddPage(wParam, &odp); + } + + // Start search for user data + if ((HANDLE)lParam == NULL) + GetInfo(NULL, 0); + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// Proc: Account manager options dialog +INT_PTR CALLBACK gg_acc_mgr_guidlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +//////////////////////////////////////////////////////////////////////////////////////////// +{ + GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch (msg) { + case WM_INITDIALOG: + { + DBVARIANT dbv; + DWORD num; + GGPROTO *gg = (GGPROTO *)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); + + TranslateDialogDefault(hwndDlg); + if (num = db_get_b(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) + SetDlgItemTextA(hwndDlg, IDC_UIN, ditoa(num)); + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + SetDlgItemTextA(hwndDlg, IDC_PASSWORD, dbv.pszVal); + DBFreeVariant(&dbv); + } + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv, DBVT_ASCIIZ)) { + SetDlgItemTextA(hwndDlg, IDC_EMAIL, dbv.pszVal); + DBFreeVariant(&dbv); + } + break; + } + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_CREATEACCOUNT: + { + // Readup data + GGUSERUTILDLGDATA dat; + int ret; + char pass[128], email[128]; + GetDlgItemTextA(hwndDlg, IDC_UIN, pass, sizeof(pass)); + dat.uin = atoi(pass); + GetDlgItemTextA(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); + GetDlgItemTextA(hwndDlg, IDC_EMAIL, email, sizeof(email)); + dat.pass = pass; + dat.email = email; + dat.gg = gg; + dat.mode = GG_USERUTIL_CREATE; + ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CREATEACCOUNT), hwndDlg, gg_userutildlgproc, (LPARAM)&dat); + + if (ret == IDOK) + { + DBVARIANT dbv; + DWORD num; + // Show reload required window + ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW); + + // Update uin + if (num = db_get_b(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) + SetDlgItemTextA(hwndDlg, IDC_UIN, ditoa(num)); + else + SetDlgItemTextA(hwndDlg, IDC_UIN, ""); + + // Update password + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + SetDlgItemTextA(hwndDlg, IDC_PASSWORD, dbv.pszVal); + DBFreeVariant(&dbv); + } + else SetDlgItemTextA(hwndDlg, IDC_PASSWORD, ""); + + // Update e-mail + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv, DBVT_ASCIIZ)) { + SetDlgItemTextA(hwndDlg, IDC_EMAIL, dbv.pszVal); + DBFreeVariant(&dbv); + } + else SetDlgItemTextA(hwndDlg, IDC_EMAIL, ""); + } + } + } + break; + + case WM_NOTIFY: + switch(((LPNMHDR)lParam)->idFrom) { + case 0: + switch (((LPNMHDR) lParam)->code) { + case PSN_APPLY: + { + char str[128]; + uin_t uin; + + // Write Gadu-Gadu number & password + GetDlgItemTextA(hwndDlg, IDC_UIN, str, sizeof(str)); + uin = atoi(str); + GetDlgItemTextA(hwndDlg, IDC_PASSWORD, str, sizeof(str)); + CallService(MS_DB_CRYPT_ENCODESTRING, sizeof(str), (LPARAM) str); + gg->checknewuser(uin, str); + db_set_w(NULL, gg->m_szModuleName, GG_KEY_UIN, uin); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, str); + + // Write Gadu-Gadu email + GetDlgItemTextA(hwndDlg, IDC_EMAIL, str, sizeof(str)); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, str); + } + } + } + break; + } + return FALSE; +} diff --git a/protocols/Gadu-Gadu/dynstuff.c b/protocols/Gadu-Gadu/dynstuff.c deleted file mode 100644 index d90c4e7908..0000000000 --- a/protocols/Gadu-Gadu/dynstuff.c +++ /dev/null @@ -1,613 +0,0 @@ -/* $Id: dynstuff.c 11259 2010-02-17 04:47:22Z borkra $ */ - -/* - * (C) Copyright 2001-2003 Wojtek Kaniewski - * Dawid Jarosz - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License Version 2 as - * published by the Free Software Foundation. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "gg.h" - -#include -#include -#include -#include - -/* - * list_add_sorted() - * - * dodaje do listy dany element. przy okazji może też skopiować zawarto¶ć. - * je¶li poda się jako ostatni parametr funkcję porównuj±c± zawarto¶ć - * elementów, może posortować od razu. - * - * - list - wskaĽnik do listy, - * - data - wskaĽnik do elementu, - * - alloc_size - rozmiar elementu, je¶li chcemy go skopiować. - * - * zwraca wskaĽnik zaalokowanego elementu lub NULL w przpadku błędu. - */ -void *list_add_sorted(list_t *list, void *data, int alloc_size, int (*comparision)(void *, void *)) -{ - list_t new, tmp; - - if (!list) { - errno = EFAULT; - return NULL; - } - - new = malloc(sizeof(struct list)); - - new->data = data; - new->next = NULL; - - if (alloc_size) { - new->data = malloc(alloc_size); - memcpy(new->data, data, alloc_size); - } - - if (!(tmp = *list)) { - *list = new; - } else { - if (!comparision) { - while (tmp->next) - tmp = tmp->next; - tmp->next = new; - } else { - list_t prev = NULL; - - while (comparision(new->data, tmp->data) > 0) { - prev = tmp; - tmp = tmp->next; - if (!tmp) - break; - } - - if (!prev) { - tmp = *list; - *list = new; - new->next = tmp; - } else { - prev->next = new; - new->next = tmp; - } - } - } - - return new->data; -} - -/* - * list_add() - * - * wrapper do list_add_sorted(), który zachowuje poprzedni± składnię. - */ -void *list_add(list_t *list, void *data, int alloc_size) -{ - return list_add_sorted(list, data, alloc_size, NULL); -} - -/* - * list_remove() - * - * usuwa z listy wpis z podanym elementem. - * - * - list - wskaĽnik do listy, - * - data - element, - * - free_data - zwolnić pamięć po elemencie. - */ -int list_remove(list_t *list, void *data, int free_data) -{ - list_t tmp, last = NULL; - - if (!list || !*list) { - errno = EFAULT; - return -1; - } - - tmp = *list; - if (tmp->data == data) { - *list = tmp->next; - } else { - for (; tmp && tmp->data != data; tmp = tmp->next) - last = tmp; - if (!tmp) { - errno = ENOENT; - return -1; - } - last->next = tmp->next; - } - - if (free_data) - free(tmp->data); - free(tmp); - - return 0; -} - -/* - * list_count() - * - * zwraca ilo¶ć elementów w danej li¶cie. - * - * - list - lista. - */ -int list_count(list_t list) -{ - int count = 0; - - for (; list; list = list->next) - count++; - - return count; -} - -/* - * list_destroy() - * - * niszczy wszystkie elementy listy. - * - * - list - lista, - * - free_data - czy zwalniać bufor danych? - */ -int list_destroy(list_t list, int free_data) -{ - list_t tmp; - - while (list) { - if (free_data) - free(list->data); - - tmp = list->next; - - free(list); - - list = tmp; - } - - return 0; -} - -/* - * string_realloc() - * - * upewnia się, że w stringu będzie wystarczaj±co dużo miejsca. - * - * - s - ci±g znaków, - * - count - wymagana ilo¶ć znaków (bez końcowego '\0'). - */ -static void string_realloc(string_t s, int count) -{ - char *tmp; - - if (s->str && count + 1 <= s->size) - return; - - tmp = realloc(s->str, count + 81); - if (!s->str) - *tmp = 0; - tmp[count + 80] = 0; - s->size = count + 81; - s->str = tmp; -} - -/* - * string_append_c() - * - * dodaje do danego ci±gu jeden znak, alokuj±c przy tym odpowiedni± ilo¶ć - * pamięci. - * - * - s - ci±g znaków. - * - c - znaczek do dopisania. - */ -int string_append_c(string_t s, char c) -{ - if (!s) { - errno = EFAULT; - return -1; - } - - string_realloc(s, s->len + 1); - - s->str[s->len + 1] = 0; - s->str[s->len++] = c; - - return 0; -} - -/* - * string_append_n() - * - * dodaje tekst do bufora alokuj±c odpowiedni± ilo¶ć pamięci. - * - * - s - ci±g znaków, - * - str - tekst do dopisania, - * - count - ile znaków tego tekstu dopisać? (-1 znaczy, że cały). - */ -int string_append_n(string_t s, const char *str, int count) -{ - if (!s || !str) { - errno = EFAULT; - return -1; - } - - if (count == -1) - count = (int)strlen(str); - - string_realloc(s, s->len + count); - - s->str[s->len + count] = 0; - strncpy(s->str + s->len, str, count); - - s->len += count; - - return 0; -} - -int string_append(string_t s, const char *str) -{ - return string_append_n(s, str, -1); -} - -/* - * string_insert_n() - * - * wstawia tekst w podane miejsce bufora. - * - * - s - ci±g znaków, - * - index - miejsce, gdzie mamy wpisać (liczone od 0), - * - str - tekst do dopisania, - * - count - ilo¶ć znaków do dopisania (-1 znaczy, że wszystkie). - */ -void string_insert_n(string_t s, int index, const char *str, int count) -{ - if (!s || !str) - return; - - if (count == -1) - count = (int)strlen(str); - - if (index > s->len) - index = s->len; - - string_realloc(s, s->len + count); - - memmove(s->str + index + count, s->str + index, s->len + 1 - index); - memmove(s->str + index, str, count); - - s->len += count; -} - -void string_insert(string_t s, int index, const char *str) -{ - string_insert_n(s, index, str, -1); -} - -/* - * string_init() - * - * inicjuje strukturę string. alokuje pamięć i przypisuje pierwsz± warto¶ć. - * - * - value - je¶li NULL, ci±g jest pusty, inaczej kopiuje tam. - * - * zwraca zaalokowan± strukturę `string'. - */ -string_t string_init(const char *value) -{ - string_t tmp = malloc(sizeof(struct string)); - - if (!value) - value = ""; - - tmp->str = _strdup(value); - tmp->len = (int)strlen(value); - tmp->size = (int)strlen(value) + 1; - - return tmp; -} - -/* - * string_clear() - * - * czy¶ci zawarto¶ć struktury `string'. - * - * - s - ci±g znaków. - */ -void string_clear(string_t s) -{ - if (!s) - return; - - if (s->size > 160) { - s->str = realloc(s->str, 80); - s->size = 80; - } - - s->str[0] = 0; - s->len = 0; -} - -/* - * string_free() - * - * zwalnia pamięć po strukturze string i może też zwolnić pamięć po samym - * ci±gu znaków. - * - * - s - struktura, któr± wycinamy, - * - free_string - zwolnić pamięć po ci±gu znaków? - * - * je¶li free_string=0 zwraca wskaĽnik do ci±gu, inaczej NULL. - */ -char *string_free(string_t s, int free_string) -{ - char *tmp = NULL; - - if (!s) - return NULL; - - if (free_string) - free(s->str); - else - tmp = s->str; - - free(s); - - return tmp; -} - -/* - * _itoa() - * - * prosta funkcja, która zwraca tekstow± reprezentację liczby. w obrębie - * danego wywołania jakiej¶ funkcji lub wyrażenia może być wywołania 10 - * razy, ponieważ tyle mamy statycznych buforów. lepsze to niż ci±głe - * tworzenie tymczasowych buforów na stosie i sprintf()owanie. - * - * - i - liczba do zamiany. - * - * zwraca adres do bufora, którego _NIE_NALEŻY_ zwalniać. - */ -const char *ditoa(long int i) -{ - static char bufs[10][16]; - static int index = 0; - char *tmp = bufs[index++]; - - if (index > 9) - index = 0; - - mir_snprintf(tmp, 16, "%ld", i); - - return tmp; -} - -/* - * array_make() - * - * tworzy tablicę tekstów z jednego, rozdzielonego podanymi znakami. - * - * - string - tekst wej¶ciowy, - * - sep - lista elementów oddzielaj±cych, - * - max - maksymalna ilo¶ć elementów tablicy. je¶li równe 0, nie ma - * ograniczeń rozmiaru tablicy. - * - trim - czy większ± ilo¶ć elementów oddzielaj±cych traktować jako - * jeden (na przykład spacje, tabulacja itp.) - * - quotes - czy pola mog± być zapisywane w cudzysłowiach lub - * apostrofach z escapowanymi znakami. - * - * zaalokowan± tablicę z zaalokowanymi ci±gami znaków, któr± należy - * zwolnić funkcj± array_free() - */ -char **array_make(const char *string, const char *sep, int max, int trim, int quotes) -{ - const char *p, *q; - char **result = NULL; - int items = 0, last = 0; - - if (!string || !sep) - goto failure; - - for (p = string; ; ) { - int len = 0; - char *token = NULL; - - if (max && items >= max - 1) - last = 1; - - if (trim) { - while (*p && strchr(sep, *p)) - p++; - if (!*p) - break; - } - - if (!last && quotes && (*p == '\'' || *p == '\"')) { - char sep = *p; - - for (q = p + 1, len = 0; *q; q++, len++) { - if (*q == '\\') { - q++; - if (!*q) - break; - } else if (*q == sep) - break; - } - - if ((token = calloc(1, len + 1))) { - char *r = token; - - for (q = p + 1; *q; q++, r++) { - if (*q == '\\') { - q++; - - if (!*q) - break; - - switch (*q) { - case 'n': - *r = '\n'; - break; - case 'r': - *r = '\r'; - break; - case 't': - *r = '\t'; - break; - default: - *r = *q; - } - } else if (*q == sep) { - break; - } else - *r = *q; - } - - *r = 0; - } - - p = (*q) ? q + 1 : q; - - } else { - for (q = p, len = 0; *q && (last || !strchr(sep, *q)); q++, len++); - token = calloc(1, len + 1); - strncpy(token, p, len); - token[len] = 0; - p = q; - } - - result = realloc(result, (items + 2) * sizeof(char*)); - result[items] = token; - result[++items] = NULL; - - if (!*p) - break; - - p++; - } - -failure: - if (!items) - result = calloc(1, sizeof(char*)); - - return result; -} - -/* - * array_count() - * - * zwraca ilo¶ć elementów tablicy. - */ -int array_count(char **array) -{ - int result = 0; - - if (!array) - return 0; - - while (*array) { - result++; - array++; - } - - return result; -} - -/* - * array_add() - * - * dodaje element do tablicy. - */ -void array_add(char ***array, char *string) -{ - int count = array_count(*array); - - *array = realloc(*array, (count + 2) * sizeof(char*)); - (*array)[count + 1] = NULL; - (*array)[count] = string; -} - -/* - * array_join() - * - * ł±czy elementy tablicy w jeden string oddzielaj±c elementy odpowiednim - * separatorem. - * - * - array - wskaĽnik do tablicy, - * - sep - seperator. - * - * zwrócony ci±g znaków należy zwolnić. - */ -char *array_join(char **array, const char *sep) -{ - string_t s = string_init(NULL); - int i; - - if (!array) - return _strdup(""); - - for (i = 0; array[i]; i++) { - if (i) - string_append(s, sep); - - string_append(s, array[i]); - } - - return string_free(s, 0); -} - -/* - * array_contains() - * - * stwierdza, czy tablica zawiera podany element. - * - * - array - tablica, - * - string - szukany ci±g znaków, - * - casesensitive - czy mamy zwracać uwagę na wielko¶ć znaków? - * - * 0/1 - */ -int array_contains(char **array, const char *string, int casesensitive) -{ - int i; - - if (!array || !string) - return 0; - - for (i = 0; array[i]; i++) { - if (casesensitive && !strcmp(array[i], string)) - return 1; - if (!casesensitive && !strcasecmp(array[i], string)) - return 1; - } - - return 0; -} - -/* - * array_free() - * - * zwalnia pamieć zajmowan± przez tablicę. - */ -void array_free(char **array) -{ - char **tmp; - - if (!array) - return; - - for (tmp = array; *tmp; tmp++) - free(*tmp); - - free(array); -} diff --git a/protocols/Gadu-Gadu/dynstuff.cpp b/protocols/Gadu-Gadu/dynstuff.cpp new file mode 100644 index 0000000000..9ae41daf59 --- /dev/null +++ b/protocols/Gadu-Gadu/dynstuff.cpp @@ -0,0 +1,612 @@ +/* $Id: dynstuff.c 11259 2010-02-17 04:47:22Z borkra $ */ + +/* + * (C) Copyright 2001-2003 Wojtek Kaniewski + * Dawid Jarosz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "gg.h" + +#include +#include +#include +#include + +/* + * list_add_sorted() + * + * dodaje do listy dany element. przy okazji może też skopiować zawarto¶ć. + * je¶li poda się jako ostatni parametr funkcję porównuj±c± zawarto¶ć + * elementów, może posortować od razu. + * + * - list - wskaĽnik do listy, + * - data - wskaĽnik do elementu, + * - alloc_size - rozmiar elementu, je¶li chcemy go skopiować. + * + * zwraca wskaĽnik zaalokowanego elementu lub NULL w przpadku błędu. + */ +void *list_add_sorted(list_t *list, void *data, int alloc_size, int (*comparision)(void *, void *)) +{ + if (!list) { + errno = EFAULT; + return NULL; + } + + list_t newlist = (list_t)malloc(sizeof(struct list)); + + newlist->data = data; + newlist->next = NULL; + + if (alloc_size) { + newlist->data = malloc(alloc_size); + memcpy(newlist->data, data, alloc_size); + } + + list_t tmp; + if (!(tmp = *list)) { + *list = newlist; + } else { + if (!comparision) { + while (tmp->next) + tmp = tmp->next; + tmp->next = newlist; + } else { + list_t prev = NULL; + + while (comparision(newlist->data, tmp->data) > 0) { + prev = tmp; + tmp = tmp->next; + if (!tmp) + break; + } + + if (!prev) { + tmp = *list; + *list = newlist; + newlist->next = tmp; + } else { + prev->next = newlist; + newlist->next = tmp; + } + } + } + + return newlist->data; +} + +/* + * list_add() + * + * wrapper do list_add_sorted(), który zachowuje poprzedni± składnię. + */ +void *list_add(list_t *list, void *data, int alloc_size) +{ + return list_add_sorted(list, data, alloc_size, NULL); +} + +/* + * list_remove() + * + * usuwa z listy wpis z podanym elementem. + * + * - list - wskaĽnik do listy, + * - data - element, + * - free_data - zwolnić pamięć po elemencie. + */ +int list_remove(list_t *list, void *data, int free_data) +{ + list_t tmp, last = NULL; + + if (!list || !*list) { + errno = EFAULT; + return -1; + } + + tmp = *list; + if (tmp->data == data) { + *list = tmp->next; + } else { + for (; tmp && tmp->data != data; tmp = tmp->next) + last = tmp; + if (!tmp) { + errno = ENOENT; + return -1; + } + last->next = tmp->next; + } + + if (free_data) + free(tmp->data); + free(tmp); + + return 0; +} + +/* + * list_count() + * + * zwraca ilo¶ć elementów w danej li¶cie. + * + * - list - lista. + */ +int list_count(list_t list) +{ + int count = 0; + + for (; list; list = list->next) + count++; + + return count; +} + +/* + * list_destroy() + * + * niszczy wszystkie elementy listy. + * + * - list - lista, + * - free_data - czy zwalniać bufor danych? + */ +int list_destroy(list_t list, int free_data) +{ + list_t tmp; + + while (list) { + if (free_data) + free(list->data); + + tmp = list->next; + + free(list); + + list = tmp; + } + + return 0; +} + +/* + * string_realloc() + * + * upewnia się, że w stringu będzie wystarczaj±co dużo miejsca. + * + * - s - ci±g znaków, + * - count - wymagana ilo¶ć znaków (bez końcowego '\0'). + */ +static void string_realloc(string_t s, int count) +{ + char *tmp; + + if (s->str && count + 1 <= s->size) + return; + + tmp = (char*)realloc(s->str, count + 81); + if (!s->str) + *tmp = 0; + tmp[count + 80] = 0; + s->size = count + 81; + s->str = tmp; +} + +/* + * string_append_c() + * + * dodaje do danego ci±gu jeden znak, alokuj±c przy tym odpowiedni± ilo¶ć + * pamięci. + * + * - s - ci±g znaków. + * - c - znaczek do dopisania. + */ +int string_append_c(string_t s, char c) +{ + if (!s) { + errno = EFAULT; + return -1; + } + + string_realloc(s, s->len + 1); + + s->str[s->len + 1] = 0; + s->str[s->len++] = c; + + return 0; +} + +/* + * string_append_n() + * + * dodaje tekst do bufora alokuj±c odpowiedni± ilo¶ć pamięci. + * + * - s - ci±g znaków, + * - str - tekst do dopisania, + * - count - ile znaków tego tekstu dopisać? (-1 znaczy, że cały). + */ +int string_append_n(string_t s, const char *str, int count) +{ + if (!s || !str) { + errno = EFAULT; + return -1; + } + + if (count == -1) + count = (int)strlen(str); + + string_realloc(s, s->len + count); + + s->str[s->len + count] = 0; + strncpy(s->str + s->len, str, count); + + s->len += count; + + return 0; +} + +int string_append(string_t s, const char *str) +{ + return string_append_n(s, str, -1); +} + +/* + * string_insert_n() + * + * wstawia tekst w podane miejsce bufora. + * + * - s - ci±g znaków, + * - index - miejsce, gdzie mamy wpisać (liczone od 0), + * - str - tekst do dopisania, + * - count - ilo¶ć znaków do dopisania (-1 znaczy, że wszystkie). + */ +void string_insert_n(string_t s, int index, const char *str, int count) +{ + if (!s || !str) + return; + + if (count == -1) + count = (int)strlen(str); + + if (index > s->len) + index = s->len; + + string_realloc(s, s->len + count); + + memmove(s->str + index + count, s->str + index, s->len + 1 - index); + memmove(s->str + index, str, count); + + s->len += count; +} + +void string_insert(string_t s, int index, const char *str) +{ + string_insert_n(s, index, str, -1); +} + +/* + * string_init() + * + * inicjuje strukturę string. alokuje pamięć i przypisuje pierwsz± warto¶ć. + * + * - value - je¶li NULL, ci±g jest pusty, inaczej kopiuje tam. + * + * zwraca zaalokowan± strukturę `string'. + */ +string_t string_init(const char *value) +{ + string_t tmp = (string_t)malloc(sizeof(struct string)); + + if (!value) + value = ""; + + tmp->str = _strdup(value); + tmp->len = (int)strlen(value); + tmp->size = (int)strlen(value) + 1; + + return tmp; +} + +/* + * string_clear() + * + * czy¶ci zawarto¶ć struktury `string'. + * + * - s - ci±g znaków. + */ +void string_clear(string_t s) +{ + if (!s) + return; + + if (s->size > 160) { + s->str = (char*)realloc(s->str, 80); + s->size = 80; + } + + s->str[0] = 0; + s->len = 0; +} + +/* + * string_free() + * + * zwalnia pamięć po strukturze string i może też zwolnić pamięć po samym + * ci±gu znaków. + * + * - s - struktura, któr± wycinamy, + * - free_string - zwolnić pamięć po ci±gu znaków? + * + * je¶li free_string=0 zwraca wskaĽnik do ci±gu, inaczej NULL. + */ +char *string_free(string_t s, int free_string) +{ + char *tmp = NULL; + + if (!s) + return NULL; + + if (free_string) + free(s->str); + else + tmp = s->str; + + free(s); + + return tmp; +} + +/* + * _itoa() + * + * prosta funkcja, która zwraca tekstow± reprezentację liczby. w obrębie + * danego wywołania jakiej¶ funkcji lub wyrażenia może być wywołania 10 + * razy, ponieważ tyle mamy statycznych buforów. lepsze to niż ci±głe + * tworzenie tymczasowych buforów na stosie i sprintf()owanie. + * + * - i - liczba do zamiany. + * + * zwraca adres do bufora, którego _NIE_NALEŻY_ zwalniać. + */ + +const char *ditoa(long int i) +{ + static char bufs[10][16]; + static int index = 0; + char *tmp = bufs[index++]; + + if (index > 9) + index = 0; + + mir_snprintf(tmp, 16, "%ld", i); + return tmp; +} + +/* + * array_make() + * + * tworzy tablicę tekstów z jednego, rozdzielonego podanymi znakami. + * + * - string - tekst wej¶ciowy, + * - sep - lista elementów oddzielaj±cych, + * - max - maksymalna ilo¶ć elementów tablicy. je¶li równe 0, nie ma + * ograniczeń rozmiaru tablicy. + * - trim - czy większ± ilo¶ć elementów oddzielaj±cych traktować jako + * jeden (na przykład spacje, tabulacja itp.) + * - quotes - czy pola mog± być zapisywane w cudzysłowiach lub + * apostrofach z escapowanymi znakami. + * + * zaalokowan± tablicę z zaalokowanymi ci±gami znaków, któr± należy + * zwolnić funkcj± array_free() + */ +char **array_make(const char *string, const char *sep, int max, int trim, int quotes) +{ + const char *p, *q; + char **result = NULL; + int items = 0, last = 0; + + if (!string || !sep) + goto failure; + + for (p = string; ; ) { + int len = 0; + char *token = NULL; + + if (max && items >= max - 1) + last = 1; + + if (trim) { + while (*p && strchr(sep, *p)) + p++; + if (!*p) + break; + } + + if (!last && quotes && (*p == '\'' || *p == '\"')) { + char sep = *p; + + for (q = p + 1, len = 0; *q; q++, len++) { + if (*q == '\\') { + q++; + if (!*q) + break; + } else if (*q == sep) + break; + } + + if ((token = (char*)calloc(1, len + 1))) { + char *r = token; + + for (q = p + 1; *q; q++, r++) { + if (*q == '\\') { + q++; + + if (!*q) + break; + + switch (*q) { + case 'n': + *r = '\n'; + break; + case 'r': + *r = '\r'; + break; + case 't': + *r = '\t'; + break; + default: + *r = *q; + } + } else if (*q == sep) { + break; + } else + *r = *q; + } + + *r = 0; + } + + p = (*q) ? q + 1 : q; + + } else { + for (q = p, len = 0; *q && (last || !strchr(sep, *q)); q++, len++); + token = (char*)calloc(1, len + 1); + strncpy(token, p, len); + token[len] = 0; + p = q; + } + + result = (char**)realloc(result, (items + 2) * sizeof(char*)); + result[items] = token; + result[++items] = NULL; + + if (!*p) + break; + + p++; + } + +failure: + if (!items) + result = (char**)calloc(1, sizeof(char*)); + + return result; +} + +/* + * array_count() + * + * zwraca ilo¶ć elementów tablicy. + */ +int array_count(char **array) +{ + int result = 0; + + if (!array) + return 0; + + while (*array) { + result++; + array++; + } + + return result; +} + +/* + * array_add() + * + * dodaje element do tablicy. + */ +void array_add(char ***array, char *string) +{ + int count = array_count(*array); + + *array = (char**)realloc(*array, (count + 2) * sizeof(char*)); + (*array)[count + 1] = NULL; + (*array)[count] = string; +} + +/* + * array_join() + * + * ł±czy elementy tablicy w jeden string oddzielaj±c elementy odpowiednim + * separatorem. + * + * - array - wskaĽnik do tablicy, + * - sep - seperator. + * + * zwrócony ci±g znaków należy zwolnić. + */ +char *array_join(char **array, const char *sep) +{ + string_t s = string_init(NULL); + int i; + + if (!array) + return _strdup(""); + + for (i = 0; array[i]; i++) { + if (i) + string_append(s, sep); + + string_append(s, array[i]); + } + + return string_free(s, 0); +} + +/* + * array_contains() + * + * stwierdza, czy tablica zawiera podany element. + * + * - array - tablica, + * - string - szukany ci±g znaków, + * - casesensitive - czy mamy zwracać uwagę na wielko¶ć znaków? + * + * 0/1 + */ +int array_contains(char **array, const char *string, int casesensitive) +{ + int i; + + if (!array || !string) + return 0; + + for (i = 0; array[i]; i++) { + if (casesensitive && !strcmp(array[i], string)) + return 1; + if (!casesensitive && !strcasecmp(array[i], string)) + return 1; + } + + return 0; +} + +/* + * array_free() + * + * zwalnia pamieć zajmowan± przez tablicę. + */ +void array_free(char **array) +{ + char **tmp; + + if (!array) + return; + + for (tmp = array; *tmp; tmp++) + free(*tmp); + + free(array); +} diff --git a/protocols/Gadu-Gadu/filetransfer.c b/protocols/Gadu-Gadu/filetransfer.c deleted file mode 100644 index cc55b993b8..0000000000 --- a/protocols/Gadu-Gadu/filetransfer.c +++ /dev/null @@ -1,982 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 Adam Strzelecki -// -// 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 "gg.h" -#include -#include -#include - -void __cdecl gg_dccmainthread(GGPROTO *gg, void *empty); - -void gg_dccstart(GGPROTO *gg) -{ - DWORD exitCode = 0; - - if(gg->dcc) return; - - // Startup dcc thread - GetExitCodeThread(gg->pth_dcc.hThread, &exitCode); - // Check if dcc thread isn't running already - if(exitCode == STILL_ACTIVE) - { -#ifdef DEBUGMODE - gg_netlog(gg, "gg_dccstart(): DCC thread still active. Exiting..."); -#endif - // Signalize mainthread it's started - if(gg->event) SetEvent(gg->event); - return; - } - - // Check if we wan't direct connections - if (!DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_DIRECTCONNS, GG_KEYDEF_DIRECTCONNS)) - { - gg_netlog(gg, "gg_dccstart(): No direct connections setup."); - if(gg->event) SetEvent(gg->event); - return; - } - - // Start thread - gg->pth_dcc.hThread = gg_forkthreadex(gg, gg_dccmainthread, NULL, &gg->pth_dcc.dwThreadId); -} - -void gg_dccconnect(GGPROTO *gg, uin_t uin) -{ - struct gg_dcc *dcc; - HANDLE hContact = gg_getcontact(gg, uin, 0, 0, NULL); - DWORD ip, myuin; WORD port; - - gg_netlog(gg, "gg_dccconnect(): Connecting to uin %d.", uin); - - // If unknown user or not on list ignore - if (!hContact) return; - - // Read user IP and port - ip = swap32(DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_CLIENTIP, 0)); - port = DBGetContactSettingWord(hContact, GG_PROTO, GG_KEY_CLIENTPORT, 0); - myuin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0); - - // If not port nor ip nor my uin (?) specified - if (!ip || !port || !uin) return; - - if (!(dcc = gg_dcc_get_file(ip, port, myuin, uin))) - return; - - // Add client dcc to watches - EnterCriticalSection(&gg->ft_mutex); - list_add(&gg->watches, dcc, 0); - LeaveCriticalSection(&gg->ft_mutex); -} - -////////////////////////////////////////////////////////// -// THREAD: File transfer fail -struct ftfaildata -{ - HANDLE hContact; - HANDLE hProcess; -}; -void __cdecl gg_ftfailthread(GGPROTO *gg, void *param) -{ - struct ftfaildata *ft = (struct ftfaildata *)param; - SleepEx(100, FALSE); - gg_netlog(gg, "gg_ftfailthread(): Sending failed file transfer."); - ProtoBroadcastAck(GG_PROTO, ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft->hProcess, 0); - free(ft); -} -HANDLE ftfail(GGPROTO *gg, HANDLE hContact) -{ - struct ftfaildata *ft = malloc(sizeof(struct ftfaildata)); -#ifdef DEBUGMODE - gg_netlog(gg, "gg_ftfail(): Failing file transfer..."); -#endif - srand(time(NULL)); - ft->hProcess = (HANDLE)rand(); - ft->hContact = hContact; - gg_forkthread(gg, gg_ftfailthread, ft); - return ft->hProcess; -} - -//////////////////////////////////////////////////////////// -// Main DCC connection session thread - -// Info refresh min time (msec) / half-sec -#define GGSTATREFRESHEVERY 500 - -void __cdecl gg_dccmainthread(GGPROTO *gg, void *empty) -{ - uin_t uin; - struct gg_event *e; - struct timeval tv; - fd_set rd, wd; - int ret; - SOCKET maxfd; - DWORD tick; - list_t l; - char filename[MAX_PATH]; - - // Zero up lists - gg->watches = gg->transfers = gg->requests = l = NULL; - - gg_netlog(gg, "gg_dccmainthread(): DCC Server Thread Starting"); - - // Readup number - if (!(uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0))) - { - gg_netlog(gg, "gg_dccmainthread(): No Gadu-Gadu number specified. Exiting."); - if(gg->event) SetEvent(gg->event); - return; - } - - // Create listen socket on config direct port - if (!(gg->dcc = gg_dcc_socket_create(uin, (uint16_t)DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_DIRECTPORT, GG_KEYDEF_DIRECTPORT)))) - { - gg_netlog(gg, "gg_dccmainthread(): Cannot create DCC listen socket. Exiting."); - // Signalize mainthread we haven't start - if(gg->event) SetEvent(gg->event); - return; - } - - gg_dcc_port = gg->dcc->port; - gg_dcc_ip = inet_addr("255.255.255.255"); - gg_netlog(gg, "gg_dccmainthread(): Listening on port %d.", gg_dcc_port); - - // Signalize mainthread we started - if(gg->event) SetEvent(gg->event); - - // Add main dcc handler to watches - list_add(&gg->watches, gg->dcc, 0); - - // Do while we are in the main server thread - while(gg->pth_dcc.dwThreadId && gg->dcc) - { - // Timeouts - tv.tv_sec = 1; - tv.tv_usec = 0; - - // Prepare descriptiors for select - FD_ZERO(&rd); - FD_ZERO(&wd); - - for (maxfd = 0, l = gg->watches; l; l = l->next) - { - struct gg_common *w = l->data; - - if (!w || w->state == GG_STATE_ERROR || w->state == GG_STATE_IDLE || w->state == GG_STATE_DONE) - continue; - - // Check if it's proper descriptor - if (w->fd == -1) continue; - - if (w->fd > maxfd) - maxfd = w->fd; - if ((w->check & GG_CHECK_READ)) - FD_SET(w->fd, &rd); - if ((w->check & GG_CHECK_WRITE)) - FD_SET(w->fd, &wd); - } - - // Wait for data on selects - ret = select(maxfd + 1, &rd, &wd, NULL, &tv); - - // Check for select error - if (ret == -1) - { - if (errno == EBADF) - gg_netlog(gg, "gg_dccmainthread(): Bad descriptor on select()."); - else if (errno != EINTR) - gg_netlog(gg, "gg_dccmainthread(): Unknown error on select()."); - continue; - } - - // Process watches (carefull with l) - l = gg->watches; - EnterCriticalSection(&gg->ft_mutex); - while (l) - { - struct gg_common *c = l->data; - struct gg_dcc *dcc = l->data; - struct gg_dcc7 *dcc7 = l->data; - l = l->next; - - switch (c->type) - { - default: - if (!dcc || (!FD_ISSET(dcc->fd, &rd) && !FD_ISSET(dcc->fd, &wd))) - continue; - - ///////////////////////////////////////////////////////////////// - // Process DCC events - - // Connection broken/closed - if (!(e = gg_dcc_socket_watch_fd(dcc))) - { - gg_netlog(gg, "gg_dccmainthread(): Socket closed."); - // Remove socket and _close - list_remove(&gg->watches, dcc, 0); - gg_dcc_socket_free(dcc); - - // Check if it's main socket - if(dcc == gg->dcc) gg->dcc = NULL; - continue; - } - else gg_netlog(gg, "gg_dccmainthread(): Event: %s", ggdebug_eventtype(e)); - - switch(e->type) - { - // Client connected - case GG_EVENT_DCC_NEW: - list_add(&gg->watches, e->event.dcc_new, 0); - e->event.dcc_new = NULL; - break; - - // - case GG_EVENT_NONE: - // If transfer in progress do status - if(dcc->file_fd != -1 && dcc->offset > 0 && (((tick = GetTickCount()) - dcc->tick) > GGSTATREFRESHEVERY)) - { - PROTOFILETRANSFERSTATUS pfts; - dcc->tick = tick; - strncpy(filename, dcc->folder, sizeof(filename)); - strncat(filename, dcc->file_info.filename, sizeof(filename) - strlen(filename)); - memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS)); - pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS); - pfts.hContact = (HANDLE)dcc->contact; - pfts.flags = (dcc->type == GG_SESSION_DCC_SEND); - pfts.pszFiles = NULL; - pfts.totalFiles = 1; - pfts.currentFileNumber = 0; - pfts.totalBytes = dcc->file_info.size; - pfts.totalProgress = dcc->offset; - pfts.szWorkingDir = dcc->folder; - pfts.szCurrentFile = filename; - pfts.currentFileSize = dcc->file_info.size; - pfts.currentFileProgress = dcc->offset; - pfts.currentFileTime = 0; - ProtoBroadcastAck(GG_PROTO, dcc->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc, (LPARAM)&pfts); - } - break; - - // Connection was successfuly ended - case GG_EVENT_DCC_DONE: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, Transfer done ! Closing connection.", dcc->peer_uin); - // Remove from watches - list_remove(&gg->watches, dcc, 0); - // Close file & success - if(dcc->file_fd != -1) - { - PROTOFILETRANSFERSTATUS pfts; - strncpy(filename, dcc->folder, sizeof(filename)); - strncat(filename, dcc->file_info.filename, sizeof(filename) - strlen(filename)); - memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS)); - pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS); - pfts.hContact = (HANDLE)dcc->contact; - pfts.flags = (dcc->type == GG_SESSION_DCC_SEND); - pfts.pszFiles = NULL; - pfts.totalFiles = 1; - pfts.currentFileNumber = 0; - pfts.totalBytes = dcc->file_info.size; - pfts.totalProgress = dcc->file_info.size; - pfts.szWorkingDir = dcc->folder; - pfts.szCurrentFile = filename; - pfts.currentFileSize = dcc->file_info.size; - pfts.currentFileProgress = dcc->file_info.size; - pfts.currentFileTime = 0; - ProtoBroadcastAck(GG_PROTO, dcc->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc, (LPARAM)&pfts); - _close(dcc->file_fd); dcc->file_fd = -1; - ProtoBroadcastAck(GG_PROTO, dcc->contact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dcc, 0); - } - // Free dcc - gg_free_dcc(dcc); if(dcc == gg->dcc) gg->dcc = NULL; - break; - - // Client error - case GG_EVENT_DCC_ERROR: - switch (e->event.dcc_error) - { - case GG_ERROR_DCC_HANDSHAKE: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, Handshake error.", dcc->peer_uin); - break; - case GG_ERROR_DCC_NET: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, Network error.", dcc->peer_uin); - break; - case GG_ERROR_DCC_FILE: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, File read/write error.", dcc->peer_uin); - break; - case GG_ERROR_DCC_EOF: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, End of file/connection error.", dcc->peer_uin); - break; - case GG_ERROR_DCC_REFUSED: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, Connection refused error.", dcc->peer_uin); - break; - default: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, Unknown error.", dcc->peer_uin); - } - // Don't do anything if it's main socket - if(dcc == gg->dcc) break; - - // Remove from watches - list_remove(&gg->watches, dcc, 0); - - // Close file & fail - if(dcc->contact) - { - _close(dcc->file_fd); dcc->file_fd = -1; - ProtoBroadcastAck(GG_PROTO, dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc, 0); - } - // Free dcc - gg_free_dcc(dcc); if(dcc == gg->dcc) gg->dcc = NULL; - break; - - // Need file acknowledgement - case GG_EVENT_DCC_NEED_FILE_ACK: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, File ack filename \"%s\" size %d.", dcc->peer_uin, - dcc->file_info.filename, dcc->file_info.size); - // Do not watch for transfer until user accept it - list_remove(&gg->watches, dcc, 0); - // Add to waiting transfers - list_add(&gg->transfers, dcc, 0); - - ////////////////////////////////////////////////// - // Add file recv request - { - CCSDATA ccs; - PROTORECVEVENT pre; - char *szBlob; - char *szFilename = dcc->file_info.filename; - char *szMsg = dcc->file_info.filename; - - // Make new ggtransfer struct - dcc->contact = gg_getcontact(gg, dcc->peer_uin, 0, 0, NULL); - szBlob = (char *)malloc(sizeof(DWORD) + strlen(szFilename) + strlen(szMsg) + 2); - // Store current dcc - *(PDWORD)szBlob = (DWORD)dcc; - // Store filename - strcpy(szBlob + sizeof(DWORD), szFilename); - // Store description - strcpy(szBlob + sizeof(DWORD) + strlen(szFilename) + 1, szMsg); - ccs.szProtoService = PSR_FILE; - ccs.hContact = dcc->contact; - ccs.wParam = 0; - ccs.lParam = (LPARAM)⪯ - pre.flags = 0; - pre.timestamp = time(NULL); - pre.szMessage = szBlob; - pre.lParam = 0; - CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); - free(szBlob); - } - break; - - // Need client accept - case GG_EVENT_DCC_CLIENT_ACCEPT: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, Client accept.", dcc->peer_uin); - // Check if user is on the list and if it is my uin - if(gg_getcontact(gg, dcc->peer_uin, 0, 0, NULL) && - DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, -1) == dcc->uin) - break; - - // Kill unauthorized dcc - list_remove(&gg->watches, dcc, 0); - gg_free_dcc(dcc); if(dcc == gg->dcc) gg->dcc = NULL; - break; - - // Client connected as we wished to (callback) - case GG_EVENT_DCC_CALLBACK: - { - int found = 0; - gg_netlog(gg, "gg_dccmainthread(): Callback from client %d.", dcc->peer_uin); - // Seek for stored callback request - for (l = gg->requests; l; l = l->next) - { - struct gg_dcc *req = l->data; - - if (req && req->peer_uin == dcc->peer_uin) - { - gg_dcc_set_type(dcc, GG_SESSION_DCC_SEND); - found = 1; - - // Copy data req ===> dcc - dcc->folder = req->folder; - dcc->contact = req->contact; - dcc->file_fd = req->file_fd; - memcpy(&dcc->file_info, &req->file_info, sizeof(struct gg_file_info)); - // Copy data back to dcc ===> req - memcpy(req, dcc, sizeof(struct gg_dcc)); - - // Remove request - list_remove(&gg->requests, req, 0); - // Remove dcc from watches - list_remove(&gg->watches, dcc, 0); - // Add request to watches - list_add(&gg->watches, req, 0); - // Free old dat - gg_free_dcc(dcc); - gg_netlog(gg, "gg_dccmainthread(): Found stored request to client %d, filename \"%s\" size %d, folder \"%s\".", - req->peer_uin, req->file_info.filename, req->file_info.size, req->folder); - break; - } - } - - if (!found) - { - gg_netlog(gg, "gg_dccmainthread(): Unknown request to client %d.", dcc->peer_uin); - // Kill unauthorized dcc - list_remove(&gg->watches, dcc, 0); - gg_free_dcc(dcc); if(dcc == gg->dcc) gg->dcc = NULL; - } - break; - } - } - - // Free event - gg_free_event(e); - break; - - case GG_SESSION_DCC7_SOCKET: - case GG_SESSION_DCC7_GET: - case GG_SESSION_DCC7_SEND: - case GG_SESSION_DCC7_VOICE: - if (!dcc7 || (!FD_ISSET(dcc7->fd, &rd) && !FD_ISSET(dcc7->fd, &wd))) - continue; - - ///////////////////////////////////////////////////////////////// - // Process DCC7 events - - // Connection broken/closed - if (!(e = gg_dcc7_watch_fd(dcc7))) - { - gg_netlog(gg, "gg_dccmainthread(): Socket closed."); - // Remove socket and _close - list_remove(&gg->watches, dcc7, 0); - gg_dcc7_free(dcc7); - continue; - } - else gg_netlog(gg, "gg_dccmainthread(): Event: %s", ggdebug_eventtype(e)); - - switch(e->type) - { - // - case GG_EVENT_NONE: - // If transfer in progress do status - if(dcc7->file_fd != -1 && dcc7->offset > 0 && (((tick = GetTickCount()) - dcc7->tick) > GGSTATREFRESHEVERY)) - { - PROTOFILETRANSFERSTATUS pfts; - dcc7->tick = tick; - strncpy(filename, dcc7->folder, sizeof(filename)); - strncat(filename, dcc7->filename, sizeof(filename) - strlen(filename)); - memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS)); - pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS); - pfts.hContact = (HANDLE)dcc7->contact; - pfts.flags = (dcc7->type == GG_SESSION_DCC7_SEND); - pfts.pszFiles = NULL; - pfts.totalFiles = 1; - pfts.currentFileNumber = 0; - pfts.totalBytes = dcc7->size; - pfts.totalProgress = dcc7->offset; - pfts.szWorkingDir = dcc7->folder; - pfts.szCurrentFile = filename; - pfts.currentFileSize = dcc7->size; - pfts.currentFileProgress = dcc7->offset; - pfts.currentFileTime = 0; - ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc7, (LPARAM)&pfts); - } - break; - - // Connection was successfuly ended - case GG_EVENT_DCC7_DONE: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, Transfer done ! Closing connection.", dcc->peer_uin); - // Remove from watches - list_remove(&gg->watches, dcc7, 0); - // Close file & success - if(dcc7->file_fd != -1) - { - PROTOFILETRANSFERSTATUS pfts; - strncpy(filename, dcc7->folder, sizeof(filename)); - strncat(filename, dcc7->filename, sizeof(filename) - strlen(filename)); - memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS)); - pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS); - pfts.hContact = (HANDLE)dcc7->contact; - pfts.flags = (dcc7->type == GG_SESSION_DCC7_SEND); - pfts.pszFiles = NULL; - pfts.totalFiles = 1; - pfts.currentFileNumber = 0; - pfts.totalBytes = dcc7->size; - pfts.totalProgress = dcc7->size; - pfts.szWorkingDir = dcc7->folder; - pfts.szCurrentFile = filename; - pfts.currentFileSize = dcc7->size; - pfts.currentFileProgress = dcc7->size; - pfts.currentFileTime = 0; - ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc7, (LPARAM)&pfts); - _close(dcc7->file_fd); dcc7->file_fd = -1; - ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dcc7, 0); - } - // Free dcc - gg_dcc7_free(dcc7); - break; - - // Client error - case GG_EVENT_DCC7_ERROR: - switch (e->event.dcc7_error) - { - case GG_ERROR_DCC7_HANDSHAKE: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, Handshake error.", dcc7->peer_uin); - break; - case GG_ERROR_DCC7_NET: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, Network error.", dcc7->peer_uin); - break; - case GG_ERROR_DCC7_FILE: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, File read/write error.", dcc7->peer_uin); - break; - case GG_ERROR_DCC7_EOF: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, End of file/connection error.", dcc7->peer_uin); - break; - case GG_ERROR_DCC7_REFUSED: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, Connection refused error.", dcc7->peer_uin); - break; - case GG_ERROR_DCC7_RELAY: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, Relay connection error.", dcc7->peer_uin); - break; - default: - gg_netlog(gg, "gg_dccmainthread(): Client: %d, Unknown error.", dcc7->peer_uin); - } - // Remove from watches - list_remove(&gg->watches, dcc7, 0); - - // Close file & fail - if (dcc7->file_fd != -1) - { - _close(dcc7->file_fd); - dcc7->file_fd = -1; - } - - if (dcc7->contact) - ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); - - // Free dcc - gg_dcc7_free(dcc7); - break; - } - - // Free event - gg_free_event(e); - break; - } - } - LeaveCriticalSection(&gg->ft_mutex); - } - - // Close all dcc client sockets - for (l = gg->watches; l; l = l->next) - { - struct gg_common *c = l->data; - if (!c) continue; - if (c->type == GG_SESSION_DCC7_SOCKET || c->type == GG_SESSION_DCC7_SEND || c->type == GG_SESSION_DCC7_GET) - { - struct gg_dcc7 *dcc7 = l->data; - gg_dcc7_free(dcc7); - } - else - { - struct gg_dcc *dcc = l->data; - gg_dcc_socket_free(dcc); - - // Check if it's main socket - if(dcc == gg->dcc) gg->dcc = NULL; - } - } - // Close all waiting for aknowledgle transfers - for (l = gg->transfers; l; l = l->next) - { - struct gg_common *c = l->data; - if (!c) continue; - if (c->type == GG_SESSION_DCC7_SOCKET || c->type == GG_SESSION_DCC7_SEND || c->type == GG_SESSION_DCC7_GET) - { - struct gg_dcc7 *dcc7 = l->data; - gg_dcc7_free(dcc7); - } - else - { - struct gg_dcc *dcc = l->data; - gg_dcc_socket_free(dcc); - } - } - // Close all waiting dcc requests - for (l = gg->requests; l; l = l->next) - { - struct gg_dcc *dcc = l->data; - if(dcc) gg_free_dcc(dcc); - } - list_destroy(gg->watches, 0); - list_destroy(gg->transfers, 0); - list_destroy(gg->requests, 0); - - gg_dcc_port = 0; - gg_dcc_ip = 0; - gg_netlog(gg, "gg_dccmainthread(): DCC Server Thread Ending"); -} - -//////////////////////////////////////////////////////////// -// Called when received an file -int gg_recvfile(PROTO_INTERFACE *proto, HANDLE hContact, PROTOFILEEVENT* pre) -{ - CCSDATA ccs = { hContact, PSR_FILE, 0, ( LPARAM )pre }; - return CallService( MS_PROTO_RECVFILE, 0, ( LPARAM )&ccs ); -} - -//////////////////////////////////////////////////////////// -// Called when user sends a file -HANDLE gg_sendfile(PROTO_INTERFACE *proto, HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles) -{ - GGPROTO *gg = (GGPROTO *) proto; - char *bslash, *filename; - struct gg_dcc *dcc; - DWORD ip, ver; - WORD port; - uin_t myuin, uin; - - // Check if main dcc thread is on - if (!gg_isonline(gg)) return ftfail(gg, hContact); - - filename = gg_t2a(ppszFiles[0]); - - // Read user IP and port - ip = swap32(DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_CLIENTIP, 0)); - port = DBGetContactSettingWord(hContact, GG_PROTO, GG_KEY_CLIENTPORT, 0); - myuin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0); - uin = DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0); - ver = DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_CLIENTVERSION, 0); - - // Use DCC7 if a contact is using at least version 7.6 or unknown version - if ((ver & 0x00ffffff) >= 0x29 || !ver) { - struct gg_dcc7 *dcc7; - - EnterCriticalSection(&gg->sess_mutex); - if (!(dcc7 = gg_dcc7_send_file(gg->sess, uin, filename, NULL, NULL))) { - LeaveCriticalSection(&gg->sess_mutex); - gg_netlog(gg, "gg_sendfile(): Failed to send file \"%s\".", filename); - mir_free(filename); - return ftfail(gg, hContact); - } - LeaveCriticalSection(&gg->sess_mutex); - - gg_netlog(gg, "gg_sendfile(): Sending file \"%s\" to %d.", filename, uin); - - // Add dcc to watches - list_add(&gg->watches, dcc7, 0); - - // Store handle - dcc7->contact = hContact; - dcc7->folder = _strdup(filename); - dcc7->tick = 0; - // Make folder name - bslash = strrchr(dcc7->folder, '\\'); - if(bslash) - *(bslash + 1) = 0; - else - *(dcc7->folder) = 0; - mir_free(filename); - return dcc7; - } - - // Return if bad connection info - if (!port || !uin || !myuin) - { - gg_netlog(gg, "gg_sendfile(): Bad contact uin or my uin. Exit."); - mir_free(filename); - return ftfail(gg, hContact); - } - - // Try to connect if not ask user to connect me - if ((ip && port >= 10 && !(dcc = gg_dcc_send_file(ip, port, myuin, uin))) || (port < 10 && port > 0)) - { - // Make fake dcc structure - dcc = malloc(sizeof(struct gg_dcc)); - memset(dcc, 0, sizeof(struct gg_dcc)); - // Fill up structures - dcc->uin = myuin; - dcc->peer_uin = uin; - dcc->fd = -1; - dcc->type = GG_SESSION_DCC_SEND; - gg_netlog(gg, "gg_sendfile(): Requesting user to connect us and scheduling gg_dcc struct for a later use."); - EnterCriticalSection(&gg->sess_mutex); - gg_dcc_request(gg->sess, uin); - LeaveCriticalSection(&gg->sess_mutex); - list_add(&gg->requests, dcc, 0); - } - - // Write filename - if(gg_dcc_fill_file_info(dcc, filename) == -1) - { - gg_netlog(gg, "gg_sendfile(): Cannot open and file fileinfo \"%s\".", filename); - gg_free_dcc(dcc); - mir_free(filename); - return ftfail(gg, hContact); - } - - gg_netlog(gg, "gg_sendfile(): Sending file \"%s\" to %d in %s mode.", filename, uin, (dcc->fd != -1) ? "active" : "passive"); - - // Add dcc to watches if not passive - if(dcc->fd != -1) list_add(&gg->watches, dcc, 0); - - // Store handle - dcc->contact = hContact; - dcc->folder = _strdup(filename); - dcc->tick = 0; - // Make folder name - bslash = strrchr(dcc->folder, '\\'); - if(bslash) - *(bslash + 1) = 0; - else - *(dcc->folder) = 0; - - mir_free(filename); - return dcc; -} - -HANDLE gg_dccfileallow(GGPROTO *gg, HANDLE hTransfer, const PROTOCHAR* szPath) -{ - struct gg_dcc *dcc = (struct gg_dcc *) hTransfer; - char fileName[MAX_PATH], *path = gg_t2a(szPath); - strncpy(fileName, path, sizeof(fileName)); - strncat(fileName, dcc->file_info.filename, sizeof(fileName) - strlen(fileName)); - dcc->folder = _strdup((char *) path); - dcc->tick = 0; - mir_free(path); - - // Remove transfer from waiting list - EnterCriticalSection(&gg->ft_mutex); - list_remove(&gg->transfers, dcc, 0); - LeaveCriticalSection(&gg->ft_mutex); - - // Open file for appending and check if ok - if ((dcc->file_fd = _open(fileName, _O_WRONLY | _O_APPEND | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE)) == -1) - { - gg_netlog(gg, "gg_dccfileallow(): Failed to create file \"%s\".", fileName); - ProtoBroadcastAck(GG_PROTO, dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc, 0); - // Free transfer - gg_free_dcc(dcc); - return 0; - } - - // Put an offset to the file - dcc->offset = _lseek(dcc->file_fd, 0, SEEK_END); - - // Add to watches and start transfer - EnterCriticalSection(&gg->ft_mutex); - list_add(&gg->watches, dcc, 0); - LeaveCriticalSection(&gg->ft_mutex); - - gg_netlog(gg, "gg_dccfileallow(): Receiving file \"%s\" from %d.", dcc->file_info.filename, dcc->peer_uin); - - return hTransfer; -} - -HANDLE gg_dcc7fileallow(GGPROTO *gg, HANDLE hTransfer, const PROTOCHAR* szPath) -{ - struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer; - char fileName[MAX_PATH], *path = gg_t2a(szPath); - int iFtRemoveRes; - strncpy(fileName, path, sizeof(fileName)); - strncat(fileName, dcc7->filename, sizeof(fileName) - strlen(fileName)); - dcc7->folder = _strdup((char *) path); - dcc7->tick = 0; - mir_free(path); - - // Remove transfer from waiting list - EnterCriticalSection(&gg->ft_mutex); - iFtRemoveRes = list_remove(&gg->transfers, dcc7, 0); - LeaveCriticalSection(&gg->ft_mutex); - - if (iFtRemoveRes == -1) - { - gg_netlog(gg, "gg_dcc7fileallow(): File transfer denied."); - ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DENIED, dcc7, 0); - // Free transfer - gg_dcc7_free(dcc7); - return 0; - } - - // Open file for appending and check if ok - if ((dcc7->file_fd = _open(fileName, _O_WRONLY | _O_APPEND | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE)) == -1) - { - gg_netlog(gg, "gg_dcc7fileallow(): Failed to create file \"%s\".", fileName); - gg_dcc7_reject(dcc7, GG_DCC7_REJECT_USER); - ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); - // Free transfer - gg_dcc7_free(dcc7); - return 0; - } - - // Put an offset to the file - dcc7->offset = _lseek(dcc7->file_fd, 0, SEEK_END); - gg_dcc7_accept(dcc7, dcc7->offset); - - // Add to watches and start transfer - EnterCriticalSection(&gg->ft_mutex); - list_add(&gg->watches, dcc7, 0); - LeaveCriticalSection(&gg->ft_mutex); - - gg_netlog(gg, "gg_dcc7fileallow(): Receiving file \"%s\" from %d.", dcc7->filename, dcc7->peer_uin); - - return hTransfer; -} - -//////////////////////////////////////////////////////////// -// File receiving allowed -HANDLE gg_fileallow(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath) -{ - struct gg_common *c = (struct gg_common *) hTransfer; - - // Check if its proper dcc - if (!c) return NULL; - - if (c->type == GG_SESSION_DCC7_GET) - return gg_dcc7fileallow((GGPROTO *)proto, hTransfer, szPath); - - return gg_dccfileallow((GGPROTO *)proto, hTransfer, szPath); -} - -int gg_dccfiledeny(GGPROTO *gg, HANDLE hTransfer) -{ - struct gg_dcc *dcc = (struct gg_dcc *) hTransfer; - - // Remove transfer from any list - EnterCriticalSection(&gg->ft_mutex); - if(gg->watches) list_remove(&gg->watches, dcc, 0); - if(gg->requests) list_remove(&gg->requests, dcc, 0); - if(gg->transfers) list_remove(&gg->transfers, dcc, 0); - LeaveCriticalSection(&gg->ft_mutex); - - gg_netlog(gg, "gg_dccfiledeny(): Rejected file \"%s\" from/to %d.", dcc->file_info.filename, dcc->peer_uin); - - // Free transfer - gg_free_dcc(dcc); - - return 0; -} - -int gg_dcc7filedeny(GGPROTO *gg, HANDLE hTransfer) -{ - struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer; - - gg_dcc7_reject(dcc7, GG_DCC7_REJECT_USER); - - // Remove transfer from any list - EnterCriticalSection(&gg->ft_mutex); - if(gg->watches) list_remove(&gg->watches, dcc7, 0); - if(gg->transfers) list_remove(&gg->transfers, dcc7, 0); - LeaveCriticalSection(&gg->ft_mutex); - - gg_netlog(gg, "gg_dcc7filedeny(): Rejected file \"%s\" from/to %d.", dcc7->filename, dcc7->peer_uin); - - // Free transfer - gg_dcc7_free(dcc7); - - return 0; -} - -//////////////////////////////////////////////////////////// -// File receiving denied -int gg_filedeny(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason) -{ - struct gg_common *c = (struct gg_common *) hTransfer; - - // Check if its proper dcc - if (!c) return 0; - - if (c->type == GG_SESSION_DCC7_GET) - return gg_dcc7filedeny((GGPROTO *)proto, hTransfer); - - return gg_dccfiledeny((GGPROTO *)proto, hTransfer); -} - -int gg_dccfilecancel(GGPROTO *gg, HANDLE hTransfer) -{ - struct gg_dcc *dcc = (struct gg_dcc *) hTransfer; - - // Remove transfer from any list - EnterCriticalSection(&gg->ft_mutex); - if(gg->watches) list_remove(&gg->watches, dcc, 0); - if(gg->requests) list_remove(&gg->requests, dcc, 0); - if(gg->transfers) list_remove(&gg->transfers, dcc, 0); - LeaveCriticalSection(&gg->ft_mutex); - - // Send failed info - ProtoBroadcastAck(GG_PROTO, dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc, 0); - // Close file - if(dcc->file_fd != -1) - { - _close(dcc->file_fd); - dcc->file_fd = -1; - } - - gg_netlog(gg, "gg_dccfilecancel(): Canceled file \"%s\" from/to %d.", dcc->file_info.filename, dcc->peer_uin); - - // Free transfer - gg_free_dcc(dcc); - - return 0; -} - -int gg_dcc7filecancel(GGPROTO *gg, HANDLE hTransfer) -{ - struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer; - - if (dcc7->type == GG_SESSION_DCC7_SEND && dcc7->state == GG_STATE_WAITING_FOR_ACCEPT) - gg_dcc7_abort(dcc7); - - // Remove transfer from any list - EnterCriticalSection(&gg->ft_mutex); - if(gg->watches) list_remove(&gg->watches, dcc7, 0); - if(gg->transfers) list_remove(&gg->transfers, dcc7, 0); - LeaveCriticalSection(&gg->ft_mutex); - - // Send failed info - ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); - // Close file - if(dcc7->file_fd != -1) - { - _close(dcc7->file_fd); - dcc7->file_fd = -1; - } - - gg_netlog(gg, "gg_dcc7filecancel(): Canceled file \"%s\" from/to %d.", dcc7->filename, dcc7->peer_uin); - - // Free transfer - gg_dcc7_free(dcc7); - - return 0; -} - -//////////////////////////////////////////////////////////// -// File transfer canceled -int gg_filecancel(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hTransfer) -{ - GGPROTO *gg = (GGPROTO *) proto; - struct gg_common *c = (struct gg_common *) hTransfer; - - // Check if its proper dcc - if (!c) return 0; - - if (c->type == GG_SESSION_DCC7_SEND || c->type == GG_SESSION_DCC7_GET) - return gg_dcc7filecancel((GGPROTO *) proto, hTransfer); - - return gg_dccfilecancel((GGPROTO *) proto, hTransfer); -} diff --git a/protocols/Gadu-Gadu/filetransfer.cpp b/protocols/Gadu-Gadu/filetransfer.cpp new file mode 100644 index 0000000000..4c58c59b15 --- /dev/null +++ b/protocols/Gadu-Gadu/filetransfer.cpp @@ -0,0 +1,987 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 Adam Strzelecki +// +// 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 "gg.h" +#include +#include +#include + +void GGPROTO::dccstart() +{ + DWORD exitCode = 0; + + if (dcc) return; + + // Startup dcc thread + GetExitCodeThread(pth_dcc.hThread, &exitCode); + // Check if dcc thread isn't running already + if (exitCode == STILL_ACTIVE) + { +#ifdef DEBUGMODE + netlog("gg_dccstart(): DCC thread still active. Exiting..."); +#endif + // Signalize mainthread it's started + if (hEvent) SetEvent(hEvent); + return; + } + + // Check if we wan't direct connections + if (!db_get_b(NULL, m_szModuleName, GG_KEY_DIRECTCONNS, GG_KEYDEF_DIRECTCONNS)) + { + netlog("gg_dccstart(): No direct connections setup."); + if (hEvent) SetEvent(hEvent); + return; + } + + // Start thread + pth_dcc.hThread = forkthreadex(&GGPROTO::dccmainthread, NULL, &pth_dcc.dwThreadId); +} + +void GGPROTO::dccconnect(uin_t uin) +{ + struct gg_dcc *dcc; + HANDLE hContact = getcontact(uin, 0, 0, NULL); + DWORD ip, myuin; WORD port; + + netlog("gg_dccconnect(): Connecting to uin %d.", uin); + + // If unknown user or not on list ignore + if (!hContact) return; + + // Read user IP and port + ip = swap32(db_get_b(hContact, m_szModuleName, GG_KEY_CLIENTIP, 0)); + port = db_get_w(hContact, m_szModuleName, GG_KEY_CLIENTPORT, 0); + myuin = db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0); + + // If not port nor ip nor my uin (?) specified + if (!ip || !port || !uin) return; + + if (!(dcc = gg_dcc_get_file(ip, port, myuin, uin))) + return; + + // Add client dcc to watches + EnterCriticalSection(&ft_mutex); + list_add(&watches, dcc, 0); + LeaveCriticalSection(&ft_mutex); +} + +////////////////////////////////////////////////////////// +// THREAD: File transfer fail +struct ftfaildata +{ + HANDLE hContact; + HANDLE hProcess; +}; + +void __cdecl GGPROTO::ftfailthread(void *param) +{ + struct ftfaildata *ft = (struct ftfaildata *)param; + SleepEx(100, FALSE); + netlog("gg_ftfailthread(): Sending failed file transfer."); + ProtoBroadcastAck(m_szModuleName, ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft->hProcess, 0); + free(ft); +} + +HANDLE ftfail(GGPROTO *gg, HANDLE hContact) +{ + ftfaildata *ft = (ftfaildata*)malloc(sizeof(struct ftfaildata)); +#ifdef DEBUGMODE + gg->netlog("gg_ftfail(): Failing file transfer..."); +#endif + srand(time(NULL)); + ft->hProcess = (HANDLE)rand(); + ft->hContact = hContact; + gg->forkthread(&GGPROTO::ftfailthread, ft); + return ft->hProcess; +} + +//////////////////////////////////////////////////////////// +// Main DCC connection session thread + +// Info refresh min time (msec) / half-sec +#define GGSTATREFRESHEVERY 500 + +void __cdecl GGPROTO::dccmainthread(void*) +{ + uin_t uin; + gg_event *e; + struct timeval tv; + fd_set rd, wd; + int ret; + SOCKET maxfd; + DWORD tick; + list_t l; + char filename[MAX_PATH]; + + // Zero up lists + watches = transfers = requests = l = NULL; + + netlog("gg_dccmainthread(): DCC Server Thread Starting"); + + // Readup number + if (!(uin = db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0))) + { + netlog("gg_dccmainthread(): No Gadu-Gadu number specified. Exiting."); + if (hEvent) SetEvent(hEvent); + return; + } + + // Create listen socket on config direct port + if (!(dcc = gg_dcc_socket_create(uin, (uint16_t)db_get_w(NULL, m_szModuleName, GG_KEY_DIRECTPORT, GG_KEYDEF_DIRECTPORT)))) + { + netlog("gg_dccmainthread(): Cannot create DCC listen socket. Exiting."); + // Signalize mainthread we haven't start + if (hEvent) SetEvent(hEvent); + return; + } + + gg_dcc_port = dcc->port; + gg_dcc_ip = inet_addr("255.255.255.255"); + netlog("gg_dccmainthread(): Listening on port %d.", gg_dcc_port); + + // Signalize mainthread we started + if (hEvent) SetEvent(hEvent); + + // Add main dcc handler to watches + list_add(&watches, dcc, 0); + + // Do while we are in the main server thread + while(pth_dcc.dwThreadId && dcc) + { + // Timeouts + tv.tv_sec = 1; + tv.tv_usec = 0; + + // Prepare descriptiors for select + FD_ZERO(&rd); + FD_ZERO(&wd); + + for (maxfd = 0, l = watches; l; l = l->next) + { + gg_common *w = (gg_common*)l->data; + + if (!w || w->state == GG_STATE_ERROR || w->state == GG_STATE_IDLE || w->state == GG_STATE_DONE) + continue; + + // Check if it's proper descriptor + if (w->fd == -1) continue; + + if (w->fd > maxfd) + maxfd = w->fd; + if ((w->check & GG_CHECK_READ)) + FD_SET(w->fd, &rd); + if ((w->check & GG_CHECK_WRITE)) + FD_SET(w->fd, &wd); + } + + // Wait for data on selects + ret = select(maxfd + 1, &rd, &wd, NULL, &tv); + + // Check for select error + if (ret == -1) + { + if (errno == EBADF) + netlog("gg_dccmainthread(): Bad descriptor on select()."); + else if (errno != EINTR) + netlog("gg_dccmainthread(): Unknown error on select()."); + continue; + } + + // Process watches (carefull with l) + l = watches; + EnterCriticalSection(&ft_mutex); + while (l) + { + struct gg_common *c = (gg_common*)l->data; + struct gg_dcc *dcc = (gg_dcc*)l->data; + struct gg_dcc7 *dcc7 = (gg_dcc7*)l->data; + l = l->next; + + switch (c->type) + { + default: + if (!dcc || (!FD_ISSET(dcc->fd, &rd) && !FD_ISSET(dcc->fd, &wd))) + continue; + + ///////////////////////////////////////////////////////////////// + // Process DCC events + + // Connection broken/closed + if (!(e = gg_dcc_socket_watch_fd(dcc))) + { + netlog("gg_dccmainthread(): Socket closed."); + // Remove socket and _close + list_remove(&watches, dcc, 0); + gg_dcc_socket_free(dcc); + + // Check if it's main socket + if (dcc == dcc) dcc = NULL; + continue; + } + else netlog("gg_dccmainthread(): Event: %s", ggdebug_eventtype(e)); + + switch(e->type) + { + // Client connected + case GG_EVENT_DCC_NEW: + list_add(&watches, e->event.dcc_new, 0); + e->event.dcc_new = NULL; + break; + + // + case GG_EVENT_NONE: + // If transfer in progress do status + if (dcc->file_fd != -1 && dcc->offset > 0 && (((tick = GetTickCount()) - dcc->tick) > GGSTATREFRESHEVERY)) + { + PROTOFILETRANSFERSTATUS pfts; + dcc->tick = tick; + strncpy(filename, dcc->folder, sizeof(filename)); + strncat(filename, (char*)dcc->file_info.filename, sizeof(filename) - strlen(filename)); + memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS)); + pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS); + pfts.hContact = (HANDLE)dcc->contact; + pfts.flags = (dcc->type == GG_SESSION_DCC_SEND); + pfts.pszFiles = NULL; + pfts.totalFiles = 1; + pfts.currentFileNumber = 0; + pfts.totalBytes = dcc->file_info.size; + pfts.totalProgress = dcc->offset; + pfts.szWorkingDir = dcc->folder; + pfts.szCurrentFile = filename; + pfts.currentFileSize = dcc->file_info.size; + pfts.currentFileProgress = dcc->offset; + pfts.currentFileTime = 0; + ProtoBroadcastAck(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc, (LPARAM)&pfts); + } + break; + + // Connection was successfuly ended + case GG_EVENT_DCC_DONE: + netlog("gg_dccmainthread(): Client: %d, Transfer done ! Closing connection.", dcc->peer_uin); + // Remove from watches + list_remove(&watches, dcc, 0); + // Close file & success + if (dcc->file_fd != -1) + { + PROTOFILETRANSFERSTATUS pfts; + strncpy(filename, dcc->folder, sizeof(filename)); + strncat(filename, (char*)dcc->file_info.filename, sizeof(filename) - strlen(filename)); + memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS)); + pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS); + pfts.hContact = (HANDLE)dcc->contact; + pfts.flags = (dcc->type == GG_SESSION_DCC_SEND); + pfts.pszFiles = NULL; + pfts.totalFiles = 1; + pfts.currentFileNumber = 0; + pfts.totalBytes = dcc->file_info.size; + pfts.totalProgress = dcc->file_info.size; + pfts.szWorkingDir = dcc->folder; + pfts.szCurrentFile = filename; + pfts.currentFileSize = dcc->file_info.size; + pfts.currentFileProgress = dcc->file_info.size; + pfts.currentFileTime = 0; + ProtoBroadcastAck(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc, (LPARAM)&pfts); + _close(dcc->file_fd); dcc->file_fd = -1; + ProtoBroadcastAck(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dcc, 0); + } + // Free dcc + gg_free_dcc(dcc); if (dcc == dcc) dcc = NULL; + break; + + // Client error + case GG_EVENT_DCC_ERROR: + switch (e->event.dcc_error) + { + case GG_ERROR_DCC_HANDSHAKE: + netlog("gg_dccmainthread(): Client: %d, Handshake error.", dcc->peer_uin); + break; + case GG_ERROR_DCC_NET: + netlog("gg_dccmainthread(): Client: %d, Network error.", dcc->peer_uin); + break; + case GG_ERROR_DCC_FILE: + netlog("gg_dccmainthread(): Client: %d, File read/write error.", dcc->peer_uin); + break; + case GG_ERROR_DCC_EOF: + netlog("gg_dccmainthread(): Client: %d, End of file/connection error.", dcc->peer_uin); + break; + case GG_ERROR_DCC_REFUSED: + netlog("gg_dccmainthread(): Client: %d, Connection refused error.", dcc->peer_uin); + break; + default: + netlog("gg_dccmainthread(): Client: %d, Unknown error.", dcc->peer_uin); + } + // Don't do anything if it's main socket + if (dcc == dcc) break; + + // Remove from watches + list_remove(&watches, dcc, 0); + + // Close file & fail + if (dcc->contact) + { + _close(dcc->file_fd); dcc->file_fd = -1; + ProtoBroadcastAck(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc, 0); + } + // Free dcc + gg_free_dcc(dcc); if (dcc == dcc) dcc = NULL; + break; + + // Need file acknowledgement + case GG_EVENT_DCC_NEED_FILE_ACK: + netlog("gg_dccmainthread(): Client: %d, File ack filename \"%s\" size %d.", dcc->peer_uin, + dcc->file_info.filename, dcc->file_info.size); + // Do not watch for transfer until user accept it + list_remove(&watches, dcc, 0); + // Add to waiting transfers + list_add(&transfers, dcc, 0); + + ////////////////////////////////////////////////// + // Add file recv request + { + CCSDATA ccs; + PROTORECVEVENT pre; + char *szBlob; + char *szFilename = (char*)dcc->file_info.filename; + char *szMsg = (char*)dcc->file_info.filename; + + // Make new ggtransfer struct + dcc->contact = getcontact(dcc->peer_uin, 0, 0, NULL); + szBlob = (char *)malloc(sizeof(DWORD) + strlen(szFilename) + strlen(szMsg) + 2); + // Store current dcc + *(PDWORD)szBlob = (DWORD)dcc; + // Store filename + strcpy(szBlob + sizeof(DWORD), szFilename); + // Store description + strcpy(szBlob + sizeof(DWORD) + strlen(szFilename) + 1, szMsg); + ccs.szProtoService = PSR_FILE; + ccs.hContact = dcc->contact; + ccs.wParam = 0; + ccs.lParam = (LPARAM)⪯ + pre.flags = 0; + pre.timestamp = time(NULL); + pre.szMessage = szBlob; + pre.lParam = 0; + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); + free(szBlob); + } + break; + + // Need client accept + case GG_EVENT_DCC_CLIENT_ACCEPT: + netlog("gg_dccmainthread(): Client: %d, Client accept.", dcc->peer_uin); + // Check if user is on the list and if it is my uin + if (getcontact(dcc->peer_uin, 0, 0, NULL) && + db_get_b(NULL, m_szModuleName, GG_KEY_UIN, -1) == dcc->uin) + break; + + // Kill unauthorized dcc + list_remove(&watches, dcc, 0); + gg_free_dcc(dcc); if (dcc == dcc) dcc = NULL; + break; + + // Client connected as we wished to (callback) + case GG_EVENT_DCC_CALLBACK: + { + int found = 0; + netlog("gg_dccmainthread(): Callback from client %d.", dcc->peer_uin); + // Seek for stored callback request + for (l = requests; l; l = l->next) + { + struct gg_dcc *req = (gg_dcc*)l->data; + + if (req && req->peer_uin == dcc->peer_uin) + { + gg_dcc_set_type(dcc, GG_SESSION_DCC_SEND); + found = 1; + + // Copy data req ===> dcc + dcc->folder = req->folder; + dcc->contact = req->contact; + dcc->file_fd = req->file_fd; + memcpy(&dcc->file_info, &req->file_info, sizeof(struct gg_file_info)); + // Copy data back to dcc ===> req + memcpy(req, dcc, sizeof(struct gg_dcc)); + + // Remove request + list_remove(&requests, req, 0); + // Remove dcc from watches + list_remove(&watches, dcc, 0); + // Add request to watches + list_add(&watches, req, 0); + // Free old dat + gg_free_dcc(dcc); + netlog("gg_dccmainthread(): Found stored request to client %d, filename \"%s\" size %d, folder \"%s\".", + req->peer_uin, req->file_info.filename, req->file_info.size, req->folder); + break; + } + } + + if (!found) + { + netlog("gg_dccmainthread(): Unknown request to client %d.", dcc->peer_uin); + // Kill unauthorized dcc + list_remove(&watches, dcc, 0); + gg_free_dcc(dcc); if (dcc == dcc) dcc = NULL; + } + break; + } + } + + // Free event + gg_free_event(e); + break; + + case GG_SESSION_DCC7_SOCKET: + case GG_SESSION_DCC7_GET: + case GG_SESSION_DCC7_SEND: + case GG_SESSION_DCC7_VOICE: + if (!dcc7 || (!FD_ISSET(dcc7->fd, &rd) && !FD_ISSET(dcc7->fd, &wd))) + continue; + + ///////////////////////////////////////////////////////////////// + // Process DCC7 events + + // Connection broken/closed + if (!(e = gg_dcc7_watch_fd(dcc7))) + { + netlog("gg_dccmainthread(): Socket closed."); + // Remove socket and _close + list_remove(&watches, dcc7, 0); + gg_dcc7_free(dcc7); + continue; + } + else netlog("gg_dccmainthread(): Event: %s", ggdebug_eventtype(e)); + + switch(e->type) + { + // + case GG_EVENT_NONE: + // If transfer in progress do status + if (dcc7->file_fd != -1 && dcc7->offset > 0 && (((tick = GetTickCount()) - dcc7->tick) > GGSTATREFRESHEVERY)) + { + PROTOFILETRANSFERSTATUS pfts; + dcc7->tick = tick; + strncpy(filename, dcc7->folder, sizeof(filename)); + strncat(filename, (char*)dcc7->filename, sizeof(filename) - strlen(filename)); + memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS)); + pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS); + pfts.hContact = (HANDLE)dcc7->contact; + pfts.flags = (dcc7->type == GG_SESSION_DCC7_SEND); + pfts.pszFiles = NULL; + pfts.totalFiles = 1; + pfts.currentFileNumber = 0; + pfts.totalBytes = dcc7->size; + pfts.totalProgress = dcc7->offset; + pfts.szWorkingDir = dcc7->folder; + pfts.szCurrentFile = filename; + pfts.currentFileSize = dcc7->size; + pfts.currentFileProgress = dcc7->offset; + pfts.currentFileTime = 0; + ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc7, (LPARAM)&pfts); + } + break; + + // Connection was successfuly ended + case GG_EVENT_DCC7_DONE: + netlog("gg_dccmainthread(): Client: %d, Transfer done ! Closing connection.", dcc->peer_uin); + // Remove from watches + list_remove(&watches, dcc7, 0); + // Close file & success + if (dcc7->file_fd != -1) + { + PROTOFILETRANSFERSTATUS pfts; + strncpy(filename, dcc7->folder, sizeof(filename)); + strncat(filename, (char*)dcc7->filename, sizeof(filename) - strlen(filename)); + memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS)); + pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS); + pfts.hContact = (HANDLE)dcc7->contact; + pfts.flags = (dcc7->type == GG_SESSION_DCC7_SEND); + pfts.pszFiles = NULL; + pfts.totalFiles = 1; + pfts.currentFileNumber = 0; + pfts.totalBytes = dcc7->size; + pfts.totalProgress = dcc7->size; + pfts.szWorkingDir = dcc7->folder; + pfts.szCurrentFile = filename; + pfts.currentFileSize = dcc7->size; + pfts.currentFileProgress = dcc7->size; + pfts.currentFileTime = 0; + ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc7, (LPARAM)&pfts); + _close(dcc7->file_fd); dcc7->file_fd = -1; + ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dcc7, 0); + } + // Free dcc + gg_dcc7_free(dcc7); + break; + + // Client error + case GG_EVENT_DCC7_ERROR: + switch (e->event.dcc7_error) + { + case GG_ERROR_DCC7_HANDSHAKE: + netlog("gg_dccmainthread(): Client: %d, Handshake error.", dcc7->peer_uin); + break; + case GG_ERROR_DCC7_NET: + netlog("gg_dccmainthread(): Client: %d, Network error.", dcc7->peer_uin); + break; + case GG_ERROR_DCC7_FILE: + netlog("gg_dccmainthread(): Client: %d, File read/write error.", dcc7->peer_uin); + break; + case GG_ERROR_DCC7_EOF: + netlog("gg_dccmainthread(): Client: %d, End of file/connection error.", dcc7->peer_uin); + break; + case GG_ERROR_DCC7_REFUSED: + netlog("gg_dccmainthread(): Client: %d, Connection refused error.", dcc7->peer_uin); + break; + case GG_ERROR_DCC7_RELAY: + netlog("gg_dccmainthread(): Client: %d, Relay connection error.", dcc7->peer_uin); + break; + default: + netlog("gg_dccmainthread(): Client: %d, Unknown error.", dcc7->peer_uin); + } + // Remove from watches + list_remove(&watches, dcc7, 0); + + // Close file & fail + if (dcc7->file_fd != -1) + { + _close(dcc7->file_fd); + dcc7->file_fd = -1; + } + + if (dcc7->contact) + ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); + + // Free dcc + gg_dcc7_free(dcc7); + break; + } + + // Free event + gg_free_event(e); + break; + } + } + LeaveCriticalSection(&ft_mutex); + } + + // Close all dcc client sockets + for (l = watches; l; l = l->next) + { + struct gg_common *c = (gg_common*)l->data; + if (!c) continue; + if (c->type == GG_SESSION_DCC7_SOCKET || c->type == GG_SESSION_DCC7_SEND || c->type == GG_SESSION_DCC7_GET) + { + struct gg_dcc7 *dcc7 = (gg_dcc7*)l->data; + gg_dcc7_free(dcc7); + } + else + { + struct gg_dcc *dcc = (gg_dcc*)l->data; + gg_dcc_socket_free(dcc); + + // Check if it's main socket + if (dcc == dcc) dcc = NULL; + } + } + // Close all waiting for aknowledgle transfers + for (l = transfers; l; l = l->next) + { + struct gg_common *c = (gg_common*)l->data; + if (!c) continue; + if (c->type == GG_SESSION_DCC7_SOCKET || c->type == GG_SESSION_DCC7_SEND || c->type == GG_SESSION_DCC7_GET) + { + struct gg_dcc7 *dcc7 = (gg_dcc7*)l->data; + gg_dcc7_free(dcc7); + } + else + { + struct gg_dcc *dcc = (gg_dcc*)l->data; + gg_dcc_socket_free(dcc); + } + } + // Close all waiting dcc requests + for (l = requests; l; l = l->next) + { + struct gg_dcc *dcc = (gg_dcc*)l->data; + if (dcc) gg_free_dcc(dcc); + } + list_destroy(watches, 0); + list_destroy(transfers, 0); + list_destroy(requests, 0); + + gg_dcc_port = 0; + gg_dcc_ip = 0; + netlog("gg_dccmainthread(): DCC Server Thread Ending"); +} + +HANDLE GGPROTO::dccfileallow(HANDLE hTransfer, const PROTOCHAR* szPath) +{ + struct gg_dcc *dcc = (struct gg_dcc *) hTransfer; + char fileName[MAX_PATH], *path = mir_t2a(szPath); + strncpy(fileName, path, sizeof(fileName)); + strncat(fileName, (char*)dcc->file_info.filename, sizeof(fileName) - strlen(fileName)); + dcc->folder = _strdup((char *) path); + dcc->tick = 0; + mir_free(path); + + // Remove transfer from waiting list + EnterCriticalSection(&ft_mutex); + list_remove(&transfers, dcc, 0); + LeaveCriticalSection(&ft_mutex); + + // Open file for appending and check if ok + if ((dcc->file_fd = _open(fileName, _O_WRONLY | _O_APPEND | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE)) == -1) + { + netlog("gg_dccfileallow(): Failed to create file \"%s\".", fileName); + ProtoBroadcastAck(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc, 0); + // Free transfer + gg_free_dcc(dcc); + return 0; + } + + // Put an offset to the file + dcc->offset = _lseek(dcc->file_fd, 0, SEEK_END); + + // Add to watches and start transfer + EnterCriticalSection(&ft_mutex); + list_add(&watches, dcc, 0); + LeaveCriticalSection(&ft_mutex); + + netlog("gg_dccfileallow(): Receiving file \"%s\" from %d.", dcc->file_info.filename, dcc->peer_uin); + + return hTransfer; +} + +HANDLE GGPROTO::dcc7fileallow(HANDLE hTransfer, const PROTOCHAR* szPath) +{ + struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer; + char fileName[MAX_PATH], *path = mir_t2a(szPath); + int iFtRemoveRes; + strncpy(fileName, path, sizeof(fileName)); + strncat(fileName, (char*)dcc7->filename, sizeof(fileName) - strlen(fileName)); + dcc7->folder = _strdup((char *) path); + dcc7->tick = 0; + mir_free(path); + + // Remove transfer from waiting list + EnterCriticalSection(&ft_mutex); + iFtRemoveRes = list_remove(&transfers, dcc7, 0); + LeaveCriticalSection(&ft_mutex); + + if (iFtRemoveRes == -1) + { + netlog("gg_dcc7fileallow(): File transfer denied."); + ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DENIED, dcc7, 0); + // Free transfer + gg_dcc7_free(dcc7); + return 0; + } + + // Open file for appending and check if ok + if ((dcc7->file_fd = _open(fileName, _O_WRONLY | _O_APPEND | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE)) == -1) + { + netlog("gg_dcc7fileallow(): Failed to create file \"%s\".", fileName); + gg_dcc7_reject(dcc7, GG_DCC7_REJECT_USER); + ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); + // Free transfer + gg_dcc7_free(dcc7); + return 0; + } + + // Put an offset to the file + dcc7->offset = _lseek(dcc7->file_fd, 0, SEEK_END); + gg_dcc7_accept(dcc7, dcc7->offset); + + // Add to watches and start transfer + EnterCriticalSection(&ft_mutex); + list_add(&watches, dcc7, 0); + LeaveCriticalSection(&ft_mutex); + + netlog("gg_dcc7fileallow(): Receiving file \"%s\" from %d.", dcc7->filename, dcc7->peer_uin); + + return hTransfer; +} + +int GGPROTO::dccfiledeny(HANDLE hTransfer) +{ + struct gg_dcc *dcc = (struct gg_dcc *) hTransfer; + + // Remove transfer from any list + EnterCriticalSection(&ft_mutex); + if (watches) list_remove(&watches, dcc, 0); + if (requests) list_remove(&requests, dcc, 0); + if (transfers) list_remove(&transfers, dcc, 0); + LeaveCriticalSection(&ft_mutex); + + netlog("gg_dccfiledeny(): Rejected file \"%s\" from/to %d.", dcc->file_info.filename, dcc->peer_uin); + + // Free transfer + gg_free_dcc(dcc); + + return 0; +} + +int GGPROTO::dcc7filedeny(HANDLE hTransfer) +{ + struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer; + + gg_dcc7_reject(dcc7, GG_DCC7_REJECT_USER); + + // Remove transfer from any list + EnterCriticalSection(&ft_mutex); + if (watches) list_remove(&watches, dcc7, 0); + if (transfers) list_remove(&transfers, dcc7, 0); + LeaveCriticalSection(&ft_mutex); + + netlog("gg_dcc7filedeny(): Rejected file \"%s\" from/to %d.", dcc7->filename, dcc7->peer_uin); + + // Free transfer + gg_dcc7_free(dcc7); + + return 0; +} + +int GGPROTO::dccfilecancel(HANDLE hTransfer) +{ + struct gg_dcc *dcc = (struct gg_dcc *) hTransfer; + + // Remove transfer from any list + EnterCriticalSection(&ft_mutex); + if (watches) list_remove(&watches, dcc, 0); + if (requests) list_remove(&requests, dcc, 0); + if (transfers) list_remove(&transfers, dcc, 0); + LeaveCriticalSection(&ft_mutex); + + // Send failed info + ProtoBroadcastAck(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc, 0); + // Close file + if (dcc->file_fd != -1) + { + _close(dcc->file_fd); + dcc->file_fd = -1; + } + + netlog("gg_dccfilecancel(): Canceled file \"%s\" from/to %d.", dcc->file_info.filename, dcc->peer_uin); + + // Free transfer + gg_free_dcc(dcc); + + return 0; +} + +int GGPROTO::dcc7filecancel(HANDLE hTransfer) +{ + struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer; + + if (dcc7->type == GG_SESSION_DCC7_SEND && dcc7->state == GG_STATE_WAITING_FOR_ACCEPT) + gg_dcc7_abort(dcc7); + + // Remove transfer from any list + EnterCriticalSection(&ft_mutex); + if (watches) list_remove(&watches, dcc7, 0); + if (transfers) list_remove(&transfers, dcc7, 0); + LeaveCriticalSection(&ft_mutex); + + // Send failed info + ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); + // Close file + if (dcc7->file_fd != -1) + { + _close(dcc7->file_fd); + dcc7->file_fd = -1; + } + + netlog("gg_dcc7filecancel(): Canceled file \"%s\" from/to %d.", dcc7->filename, dcc7->peer_uin); + + // Free transfer + gg_dcc7_free(dcc7); + + return 0; +} + +//////////////////////////////////////////////////////////// +// File receiving allowed + +HANDLE GGPROTO::FileAllow(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath) +{ + // Check if its proper dcc + struct gg_common *c = (struct gg_common *) hTransfer; + if (!c) + return NULL; + + if (c->type == GG_SESSION_DCC7_GET) + return dcc7fileallow(hTransfer, szPath); + + return dccfileallow(hTransfer, szPath); +} + +//////////////////////////////////////////////////////////// +// File transfer canceled + +int GGPROTO::FileCancel(HANDLE hContact, HANDLE hTransfer) +{ + // Check if its proper dcc + struct gg_common *c = (struct gg_common *) hTransfer; + if (!c) + return 0; + + if (c->type == GG_SESSION_DCC7_SEND || c->type == GG_SESSION_DCC7_GET) + return dcc7filecancel(hTransfer); + + return dccfilecancel(hTransfer); +} + +//////////////////////////////////////////////////////////// +// File receiving denied + +int GGPROTO::FileDeny(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason) +{ + // Check if its proper dcc + struct gg_common *c = (struct gg_common *) hTransfer; + if (!c) + return 0; + + if (c->type == GG_SESSION_DCC7_GET) + return dcc7filedeny(hTransfer); + + return dccfiledeny(hTransfer); +} + +//////////////////////////////////////////////////////////// +// Called when received an file + +int GGPROTO::RecvFile(HANDLE hContact, PROTOFILEEVENT* pre) +{ + CCSDATA ccs = { hContact, PSR_FILE, 0, ( LPARAM )pre }; + return CallService( MS_PROTO_RECVFILE, 0, ( LPARAM )&ccs ); +} + +//////////////////////////////////////////////////////////// +// Called when user sends a file + +HANDLE GGPROTO::SendFile(HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles) +{ + char *bslash, *filename; + struct gg_dcc *dcc; + DWORD ip, ver; + WORD port; + uin_t myuin, uin; + + // Check if main dcc thread is on + if (!isonline()) + return ftfail(this, hContact); + + filename = mir_t2a(ppszFiles[0]); + + // Read user IP and port + ip = swap32(db_get_b(hContact, m_szModuleName, GG_KEY_CLIENTIP, 0)); + port = db_get_w(hContact, m_szModuleName, GG_KEY_CLIENTPORT, 0); + myuin = db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0); + uin = db_get_b(hContact, m_szModuleName, GG_KEY_UIN, 0); + ver = db_get_b(hContact, m_szModuleName, GG_KEY_CLIENTVERSION, 0); + + // Use DCC7 if a contact is using at least version 7.6 or unknown version + if ((ver & 0x00ffffff) >= 0x29 || !ver) { + struct gg_dcc7 *dcc7; + + EnterCriticalSection(&sess_mutex); + if (!(dcc7 = gg_dcc7_send_file(sess, uin, filename, NULL, NULL))) { + LeaveCriticalSection(&sess_mutex); + netlog("gg_sendfile(): Failed to send file \"%s\".", filename); + mir_free(filename); + return ftfail(this, hContact); + } + LeaveCriticalSection(&sess_mutex); + + netlog("gg_sendfile(): Sending file \"%s\" to %d.", filename, uin); + + // Add dcc to watches + list_add(&watches, dcc7, 0); + + // Store handle + dcc7->contact = hContact; + dcc7->folder = _strdup(filename); + dcc7->tick = 0; + // Make folder name + bslash = strrchr(dcc7->folder, '\\'); + if (bslash) + *(bslash + 1) = 0; + else + *(dcc7->folder) = 0; + mir_free(filename); + return dcc7; + } + + // Return if bad connection info + if (!port || !uin || !myuin) + { + netlog("gg_sendfile(): Bad contact uin or my uin. Exit."); + mir_free(filename); + return ftfail(this, hContact); + } + + // Try to connect if not ask user to connect me + if ((ip && port >= 10 && !(dcc = gg_dcc_send_file(ip, port, myuin, uin))) || (port < 10 && port > 0)) + { + // Make fake dcc structure + dcc = (gg_dcc*)malloc(sizeof(struct gg_dcc)); + memset(dcc, 0, sizeof(struct gg_dcc)); + // Fill up structures + dcc->uin = myuin; + dcc->peer_uin = uin; + dcc->fd = -1; + dcc->type = GG_SESSION_DCC_SEND; + netlog("gg_sendfile(): Requesting user to connect us and scheduling gg_dcc struct for a later use."); + EnterCriticalSection(&sess_mutex); + gg_dcc_request(sess, uin); + LeaveCriticalSection(&sess_mutex); + list_add(&requests, dcc, 0); + } + + // Write filename + if (gg_dcc_fill_file_info(dcc, filename) == -1) + { + netlog("gg_sendfile(): Cannot open and file fileinfo \"%s\".", filename); + gg_free_dcc(dcc); + mir_free(filename); + return ftfail(this, hContact); + } + + netlog("gg_sendfile(): Sending file \"%s\" to %d in %s mode.", filename, uin, (dcc->fd != -1) ? "active" : "passive"); + + // Add dcc to watches if not passive + if (dcc->fd != -1) list_add(&watches, dcc, 0); + + // Store handle + dcc->contact = hContact; + dcc->folder = _strdup(filename); + dcc->tick = 0; + // Make folder name + bslash = strrchr(dcc->folder, '\\'); + if (bslash) + *(bslash + 1) = 0; + else + *(dcc->folder) = 0; + + mir_free(filename); + return dcc; +} + diff --git a/protocols/Gadu-Gadu/gg.c b/protocols/Gadu-Gadu/gg.c deleted file mode 100644 index 74ae376a1f..0000000000 --- a/protocols/Gadu-Gadu/gg.c +++ /dev/null @@ -1,749 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2009 Adam Strzelecki -// Copyright (c) 2009-2012 Bartosz Białek -// -// 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 "gg.h" -#include "version.h" -#include - -// Plugin info -PLUGININFOEX pluginInfo = { - sizeof(PLUGININFOEX), - "Gadu-Gadu Protocol", - __VERSION_DWORD, - "Provides support for Gadu-Gadu protocol", - "Bartosz Białek, Adam Strzelecki", - "dezred"/*antispam*/"@"/*antispam*/"gmail"/*antispam*/"."/*antispam*/"com", - "© 2009-2012 Bartosz Białek, 2003-2009 Adam Strzelecki", - "http://www.miranda-im.pl/", - 0, - // {F3FF65F3-250E-416A-BEE9-58C93F85AB33} - { 0xf3ff65f3, 0x250e, 0x416a, { 0xbe, 0xe9, 0x58, 0xc9, 0x3f, 0x85, 0xab, 0x33 } } -}; -static const MUUID interfaces[] = {MIID_PROTOCOL, MIID_LAST}; - -// Other variables -HINSTANCE hInstance; - -XML_API xi; -SSL_API si; -CLIST_INTERFACE *pcli; -int hLangpack; -list_t g_Instances; - -// Event hooks -static HANDLE hHookModulesLoaded = NULL; -static HANDLE hHookPreShutdown = NULL; - -static unsigned long crc_table[256]; - -////////////////////////////////////////////////////////// -// Extra winsock function for error description -char *ws_strerror(int code) -{ - static char err_desc[160]; - - // Not a windows error display WinSock - if(code == 0) - { - char buff[128]; - int len; - len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, - NULL, WSAGetLastError(), 0, buff, - sizeof(buff), NULL); - if(len == 0) - mir_snprintf(err_desc, sizeof(err_desc), "WinSock %u: Unknown error.", WSAGetLastError()); - else - mir_snprintf(err_desc, sizeof(err_desc), "WinSock %d: %s", WSAGetLastError(), buff); - return err_desc; - } - - // Return normal error - return strerror(code); -} - -////////////////////////////////////////////////////////// -// Build the crc table -void crc_gentable(void) -{ - unsigned long crc, poly; - int i, j; - - poly = 0xEDB88320L; - for (i = 0; i < 256; i++) - { - crc = i; - for (j = 8; j > 0; j--) - { - if (crc & 1) - crc = (crc >> 1) ^ poly; - else - crc >>= 1; - } - crc_table[i] = crc; - } -} - -////////////////////////////////////////////////////////// -// Calculate the crc value -unsigned long crc_get(char *mem) -{ - register unsigned long crc = 0xFFFFFFFF; - while(mem && *mem) - crc = ((crc>>8) & 0x00FFFFFF) ^ crc_table[(crc ^ *(mem++)) & 0xFF]; - - return (crc ^ 0xFFFFFFFF); -} - -////////////////////////////////////////////////////////// -// http_error_string() -// -// returns http error text -const char *http_error_string(int h) -{ - switch (h) - { - case 0: - return Translate((errno == ENOMEM) ? "HTTP failed memory" : "HTTP failed connecting"); - case GG_ERROR_RESOLVING: - return Translate("HTTP failed resolving"); - case GG_ERROR_CONNECTING: - return Translate("HTTP failed connecting"); - case GG_ERROR_READING: - return Translate("HTTP failed reading"); - case GG_ERROR_WRITING: - return Translate("HTTP failed writing"); - } - - return Translate("Unknown HTTP error"); -} - -////////////////////////////////////////////////////////// -// Gets plugin info -__declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion) -{ - return &pluginInfo; -} -__declspec(dllexport) const MUUID* MirandaPluginInterfaces(void) -{ - return interfaces; -} - -////////////////////////////////////////////////////////// -// Cleanups from last plugin -void gg_cleanuplastplugin(GGPROTO *gg, DWORD version) -{ - HANDLE hContact; - char *szProto; - - // Remove bad e-mail and phones from - if(version < PLUGIN_MAKE_VERSION(0, 0, 1, 4)) - { -#ifdef DEBUGMODE - gg_netlog(gg, "gg_cleanuplastplugin(%d): Cleaning junk Phone settings from < 0.0.1.4 ...", version); -#endif - // Look for contact in DB - hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); - while (hContact) - { - szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if(szProto != NULL && !strcmp(szProto, GG_PROTO)) - { - // Do contact cleanup - DBDeleteContactSetting(hContact, GG_PROTO, GG_KEY_EMAIL); - DBDeleteContactSetting(hContact, GG_PROTO, "Phone"); - } - hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); - } - } - - // Remove GG entries for non GG contacts - if(version < PLUGIN_MAKE_VERSION(0, 0, 3, 5)) - { -#ifdef DEBUGMODE - gg_netlog(gg, "gg_cleanuplastplugin(%d): Cleaning junk Nick settings from < 0.0.3.5 ...", version); -#endif - // Look for contact in DB - hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); - while (hContact) - { - szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if(szProto != NULL && strcmp(szProto, GG_PROTO)) - { - // Do nick entry cleanup - DBDeleteContactSetting(hContact, GG_PROTO, GG_KEY_NICK); - } - hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); - } - } - - // Remove old unneeded entry - if(version < PLUGIN_MAKE_VERSION(0, 0, 5, 3)) - DBDeleteContactSetting(NULL, GG_PROTO, "ShowNotOnMyList"); - - // Store this plugin version - DBWriteContactSettingDword(NULL, GG_PROTO, GG_PLUGINVERSION, pluginInfo.version); -} - -////////////////////////////////////////////////////////// -// Custom folders initialization -void gg_initcustomfolders(GGPROTO *gg) -{ - char szPath[MAX_PATH]; - char *tmpPath = Utils_ReplaceVars("%miranda_avatarcache%"); - mir_snprintf(szPath, MAX_PATH, "%s\\%s", tmpPath, GG_PROTO); - mir_free(tmpPath); - gg->hAvatarsFolder = FoldersRegisterCustomPath(GG_PROTO, "Avatars", szPath); - - tmpPath = Utils_ReplaceVars("%miranda_userdata%"); - mir_snprintf(szPath, MAX_PATH, "%s\\%s\\ImageCache", tmpPath, GG_PROTO); - mir_free(tmpPath); - gg->hImagesFolder = FoldersRegisterCustomPath(GG_PROTO, "Images", szPath); -} - -////////////////////////////////////////////////////////// -// When Miranda loaded its modules -static int gg_modulesloaded(WPARAM wParam, LPARAM lParam) -{ - // Get SSL API - mir_getSI(&si); - - // File Association Manager support - gg_links_init(); - - return 0; -} - -////////////////////////////////////////////////////////// -// When Miranda starting shutdown sequence -static int gg_preshutdown(WPARAM wParam, LPARAM lParam) -{ - gg_links_destroy(); - - return 0; -} - -////////////////////////////////////////////////////////// -// Gets protocol instance associated with a contact -static GGPROTO* gg_getprotoinstance(HANDLE hContact) -{ - char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); - list_t l = g_Instances; - - if (szProto == NULL) - return NULL; - - for (; l; l = l->next) - { - GGPROTO* gg = l->data; - if (strcmp(szProto, GG_PROTO) == 0) - return gg; - } - - return NULL; -} - -////////////////////////////////////////////////////////// -// Handles PrebuildContactMenu event -static int gg_prebuildcontactmenu(WPARAM wParam, LPARAM lParam) -{ - const HANDLE hContact = (HANDLE)wParam; - CLISTMENUITEM mi = {0}; - GGPROTO* gg = gg_getprotoinstance(hContact); - - if (gg == NULL) - return 0; - - mi.cbSize = sizeof(mi); - mi.flags = CMIM_NAME | CMIM_FLAGS | CMIF_ICONFROMICOLIB; - if (DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0) == DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0) || - DBGetContactSettingByte(hContact, GG_PROTO, "ChatRoom", 0) || - DBGetContactSettingByte(hContact, "CList", "NotOnList", 0)) - mi.flags |= CMIF_HIDDEN; - mi.pszName = DBGetContactSettingByte(hContact, GG_PROTO, GG_KEY_BLOCK, 0) ? LPGEN("&Unblock") : LPGEN("&Block"); - CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)gg->hBlockMenuItem, (LPARAM)&mi); - - return 0; -} - -////////////////////////////////////////////////////////// -// Contact block service function -INT_PTR gg_blockuser(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - const HANDLE hContact = (HANDLE)wParam; - DBWriteContactSettingByte(hContact, GG_PROTO, GG_KEY_BLOCK, !DBGetContactSettingByte(hContact, GG_PROTO, GG_KEY_BLOCK, 0)); - gg_notifyuser(gg, hContact, 1); - return 0; -} - - -////////////////////////////////////////////////////////// -// Contact blocking initialization -#define GGS_BLOCKUSER "%s/BlockUser" -static void gg_block_init(GGPROTO *gg) -{ - CLISTMENUITEM mi = {0}; - char service[64]; - - mi.cbSize = sizeof(mi); - mi.flags = CMIF_ICONFROMICOLIB; - - mir_snprintf(service, sizeof(service), GGS_BLOCKUSER, GG_PROTO); - CreateProtoServiceFunction(service, gg_blockuser, gg); - mi.position = -500050000; - mi.icolibItem = GetIconHandle(IDI_BLOCK); - mi.pszName = LPGEN("&Block"); - mi.pszService = service; - mi.pszContactOwner = GG_PROTO; - gg->hBlockMenuItem = Menu_AddContactMenuItem(&mi); - - gg->hPrebuildMenuHook = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, gg_prebuildcontactmenu); -} - -////////////////////////////////////////////////////////// -// Contact blocking uninitialization -static void gg_block_uninit(GGPROTO *gg) -{ - UnhookEvent(gg->hPrebuildMenuHook); - CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)gg->hBlockMenuItem, 0); -} - -////////////////////////////////////////////////////////// -// Menus initialization -void gg_menus_init(GGPROTO *gg) -{ - HGENMENU hGCRoot, hCLRoot, hRoot = MO_GetProtoRootMenu(gg->proto.m_szModuleName); - CLISTMENUITEM mi = {0}; - - mi.cbSize = sizeof(mi); - if (hRoot == NULL) - { - mi.ptszName = GG_PROTONAME; - mi.position = 500090000; - mi.hParentMenu = HGENMENU_ROOT; - mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTPOPUP | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED; - mi.icolibItem = GetIconHandle(IDI_GG); - hGCRoot = hCLRoot = hRoot = gg->hMenuRoot = Menu_AddProtoMenuItem(&mi); - } - else - { - mi.hParentMenu = hRoot; - mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE | CMIF_TCHAR; - - mi.ptszName = LPGENT("Conference"); - mi.position = 200001; - mi.icolibItem = GetIconHandle(IDI_CONFERENCE); - hGCRoot = Menu_AddProtoMenuItem(&mi); - - mi.ptszName = LPGENT("Contact list"); - mi.position = 200002; - mi.icolibItem = GetIconHandle(IDI_LIST); - hCLRoot = Menu_AddProtoMenuItem(&mi); - - if (gg->hMenuRoot) - CallService(MS_CLIST_REMOVEMAINMENUITEM, (WPARAM)gg->hMenuRoot, 0); - gg->hMenuRoot = NULL; - } - - gg_gc_menus_init(gg, hGCRoot); - gg_import_init(gg, hCLRoot); - gg_sessions_menus_init(gg, hRoot); -} - -////////////////////////////////////////////////////////// -// Custom protocol event -int gg_event(PROTO_INTERFACE *proto, PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam) -{ - GGPROTO *gg = (GGPROTO *)proto; - switch( eventType ) - { - case EV_PROTO_ONLOAD: - { - gg->hookOptsInit = HookProtoEvent(ME_OPT_INITIALISE, gg_options_init, gg); - gg->hookUserInfoInit = HookProtoEvent(ME_USERINFO_INITIALISE, gg_details_init, gg); -#ifdef DEBUGMODE - gg_netlog(gg, "gg_event(EV_PROTO_ONLOAD): loading modules..."); -#endif - // Init misc stuff - gg_icolib_init(); - gg_initpopups(gg); - gg_gc_init(gg); - gg_keepalive_init(gg); - gg_img_init(gg); - gg_block_init(gg); - - // Try to fetch user avatar - gg_getuseravatar(gg); - break; - } - case EV_PROTO_ONEXIT: -#ifdef DEBUGMODE - gg_netlog(gg, "gg_event(EV_PROTO_ONEXIT)/gg_preshutdown(): signalling shutdown..."); -#endif - // Stop avatar request thread - gg_uninitavatarrequestthread(gg); - // Stop main connection session thread -#ifdef DEBUGMODE - gg_netlog(gg, "gg_event(EV_PROTO_ONEXIT): Waiting until Server Thread finished, if needed."); -#endif - gg_threadwait(gg, &gg->pth_sess); - gg_img_shutdown(gg); - gg_sessions_closedlg(gg); - break; - - case EV_PROTO_ONOPTIONS: - return gg_options_init(gg, wParam, lParam); - - case EV_PROTO_ONMENU: - gg_menus_init(gg); - break; - - case EV_PROTO_ONRENAME: -#ifdef DEBUGMODE - gg_netlog(gg, "gg_event(EV_PROTO_ONRENAME): renaming account..."); -#endif - mir_free(gg->name); - gg->name = gg_t2a(gg->proto.m_tszUserName); - if (gg->hMenuRoot) - { - CLISTMENUITEM mi = {0}; - mi.cbSize = sizeof(mi); - mi.flags = CMIM_NAME | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED; - mi.ptszName = GG_PROTONAME; - CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)gg->hMenuRoot, (LPARAM)&mi); - } - break; - - case EV_PROTO_ONCONTACTDELETED: - return gg_contactdeleted(gg, wParam, lParam); - - case EV_PROTO_DBSETTINGSCHANGED: - return gg_dbsettingchanged(gg, wParam, lParam); - } - return TRUE; -} - -////////////////////////////////////////////////////////// -// Module instance initialization -static GGPROTO *gg_proto_init(const char* pszProtoName, const TCHAR* tszUserName) -{ - DWORD dwVersion; - GGPROTO *gg = (GGPROTO *)mir_alloc(sizeof(GGPROTO)); - char szVer[MAX_PATH]; - char name[128]; - NETLIBUSER nlu = { 0 }; - - ZeroMemory(gg, sizeof(GGPROTO)); - gg->proto.vtbl = (PROTO_INTERFACE_VTBL*)mir_alloc(sizeof(PROTO_INTERFACE_VTBL)); - // Are we running under unicode Miranda core ? - CallService(MS_SYSTEM_GETVERSIONTEXT, MAX_PATH, (LPARAM)szVer); - _strlwr(szVer); // make sure it is lowercase - gg->unicode_core = (strstr(szVer, "unicode") != NULL); - - // Init mutexes - InitializeCriticalSection(&gg->sess_mutex); - InitializeCriticalSection(&gg->ft_mutex); - InitializeCriticalSection(&gg->img_mutex); - InitializeCriticalSection(&gg->modemsg_mutex); - InitializeCriticalSection(&gg->avatar_mutex); - InitializeCriticalSection(&gg->sessions_mutex); - - // Init instance names - gg->proto.m_szModuleName = mir_strdup(pszProtoName); - gg->proto.m_szProtoName = GGDEF_PROTONAME; - gg->proto.m_iVersion = 2; - -/* Anyway we won't get Unicode in GG yet */ -#ifdef _UNICODE - gg->name = gg->proto.m_tszUserName = mir_tstrdup(tszUserName); -#else - gg->proto.m_tszUserName = gg->unicode_core ? (TCHAR *)mir_wstrdup((wchar_t *)tszUserName) : (TCHAR *)mir_strdup((char *)tszUserName); - gg->name = gg_t2a(tszUserName); -#endif - - // Register netlib user - nlu.cbSize = sizeof(nlu); - nlu.flags = NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS; - nlu.szSettingsModule = gg->proto.m_szModuleName; - mir_snprintf(name, SIZEOF(name), Translate("%s connection"), gg->proto.m_tszUserName); - nlu.ptszDescriptiveName = name; - - gg->netlib = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu); - - // Register services - gg_registerservices(gg); - - // Offline contacts and clear logon time - gg_setalloffline(gg); - DBWriteContactSettingDword(NULL, GG_PROTO, GG_KEY_LOGONTIME, 0); - - if ((dwVersion = DBGetContactSettingDword(NULL, GG_PROTO, GG_PLUGINVERSION, 0)) < pluginInfo.version) - gg_cleanuplastplugin(gg, dwVersion); - - // Add to the instance list - list_add(&g_Instances, gg, 0); - - gg_links_instance_init(gg); - gg_initcustomfolders(gg); - gg_initavatarrequestthread(gg); - - return gg; -} - -////////////////////////////////////////////////////////// -// Module instance uninitialization -static int gg_proto_uninit(PROTO_INTERFACE *proto) -{ - GGPROTO *gg = (GGPROTO *)proto; - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_proto_uninit(): destroying protocol interface"); -#endif - - // Destroy modules - gg_block_uninit(gg); - gg_img_destroy(gg); - gg_keepalive_destroy(gg); - gg_gc_destroy(gg); - - // Remove from the instance list - list_remove(&g_Instances, gg, 0); - - if (gg->hMenuRoot) - CallService(MS_CLIST_REMOVEMAINMENUITEM, (WPARAM)gg->hMenuRoot, 0); - - // Close handles - LocalEventUnhook(gg->hookOptsInit); - LocalEventUnhook(gg->hookUserInfoInit); - Netlib_CloseHandle(gg->netlib); - - // Destroy mutexes - DeleteCriticalSection(&gg->sess_mutex); - DeleteCriticalSection(&gg->ft_mutex); - DeleteCriticalSection(&gg->img_mutex); - DeleteCriticalSection(&gg->modemsg_mutex); - DeleteCriticalSection(&gg->avatar_mutex); - DeleteCriticalSection(&gg->sessions_mutex); - - // Free status messages - if (gg->modemsg.online) mir_free(gg->modemsg.online); - if (gg->modemsg.away) mir_free(gg->modemsg.away); - if (gg->modemsg.dnd) mir_free(gg->modemsg.dnd); - if (gg->modemsg.freechat) mir_free(gg->modemsg.freechat); - if (gg->modemsg.invisible) mir_free(gg->modemsg.invisible); - if (gg->modemsg.offline) mir_free(gg->modemsg.offline); - - mir_free(gg->proto.m_szModuleName); - mir_free(gg->proto.m_tszUserName); - mir_free(gg->name); - mir_free(gg->proto.vtbl); - mir_free(gg); - - return 0; -} - -////////////////////////////////////////////////////////// -// When plugin is loaded -int __declspec(dllexport) Load(void) -{ - WSADATA wsaData; - PROTOCOLDESCRIPTOR pd; - - - mir_getXI(&xi); - mir_getLP(&pluginInfo); - - // Init winsock - if (WSAStartup(MAKEWORD( 1, 1 ), &wsaData)) - return 1; - - pcli = (CLIST_INTERFACE*)CallService(MS_CLIST_RETRIEVE_INTERFACE, 0, (LPARAM)hInstance); - - // Hook system events - hHookModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, gg_modulesloaded); - hHookPreShutdown = HookEvent(ME_SYSTEM_PRESHUTDOWN, gg_preshutdown); - - // Prepare protocol name - ZeroMemory(&pd, sizeof(pd)); - pd.cbSize = sizeof(pd); - pd.szName = GGDEF_PROTO; - pd.fnInit = (pfnInitProto)gg_proto_init; - pd.fnUninit = (pfnUninitProto)gg_proto_uninit; - pd.type = PROTOTYPE_PROTOCOL; - - // Register module - CallService(MS_PROTO_REGISTERMODULE, 0, (LPARAM) &pd); - gg_links_instancemenu_init(); - - // Instance list - g_Instances = NULL; - - return 0; -} - -////////////////////////////////////////////////////////// -// When plugin is unloaded -int __declspec(dllexport) Unload() -{ - LocalEventUnhook(hHookModulesLoaded); - LocalEventUnhook(hHookPreShutdown); - - // Cleanup WinSock - WSACleanup(); - - return 0; -} - -////////////////////////////////////////////////////////// -// Adds a new protocol specific service function -void CreateProtoService(const char* szService, GGPROTOFUNC serviceProc, GGPROTO *gg) -{ - char str[MAXMODULELABELLENGTH]; - mir_snprintf(str, sizeof(str), "%s%s", gg->proto.m_szModuleName, szService); - CreateServiceFunctionObj(str, (MIRANDASERVICEOBJ)serviceProc, gg); -} - -////////////////////////////////////////////////////////// -// Forks a thread -void gg_forkthread(GGPROTO *gg, GGThreadFunc pFunc, void *param) -{ - CloseHandle((HANDLE)mir_forkthreadowner((pThreadFuncOwner)&pFunc, gg, param, NULL)); -} - -////////////////////////////////////////////////////////// -// Forks a thread and returns a pseudo handle for it -HANDLE gg_forkthreadex(GGPROTO *gg, GGThreadFunc pFunc, void *param, UINT *threadId) -{ - return (HANDLE)mir_forkthreadowner((pThreadFuncOwner)&pFunc, gg, param, threadId); -} - -////////////////////////////////////////////////////////// -// Wait for thread to stop -void gg_threadwait(GGPROTO *gg, GGTHREAD *thread) -{ - if (!thread->hThread) return; - while (WaitForSingleObjectEx(thread->hThread, INFINITE, TRUE) != WAIT_OBJECT_0); - CloseHandle(thread->hThread); - ZeroMemory(thread, sizeof(GGTHREAD)); -} - -////////////////////////////////////////////////////////// -// DEBUGING FUNCTIONS -struct -{ - int type; - char *text; -} -static const ggdebug_eventype2string[] = -{ - {GG_EVENT_NONE, "GG_EVENT_NONE"}, - {GG_EVENT_MSG, "GG_EVENT_MSG"}, - {GG_EVENT_NOTIFY, "GG_EVENT_NOTIFY"}, - {GG_EVENT_NOTIFY_DESCR, "GG_EVENT_NOTIFY_DESCR"}, - {GG_EVENT_STATUS, "GG_EVENT_STATUS"}, - {GG_EVENT_ACK, "GG_EVENT_ACK"}, - {GG_EVENT_PONG, "GG_EVENT_PONG"}, - {GG_EVENT_CONN_FAILED, "GG_EVENT_CONN_FAILED"}, - {GG_EVENT_CONN_SUCCESS, "GG_EVENT_CONN_SUCCESS"}, - {GG_EVENT_DISCONNECT, "GG_EVENT_DISCONNECT"}, - {GG_EVENT_DCC_NEW, "GG_EVENT_DCC_NEW"}, - {GG_EVENT_DCC_ERROR, "GG_EVENT_DCC_ERROR"}, - {GG_EVENT_DCC_DONE, "GG_EVENT_DCC_DONE"}, - {GG_EVENT_DCC_CLIENT_ACCEPT, "GG_EVENT_DCC_CLIENT_ACCEPT"}, - {GG_EVENT_DCC_CALLBACK, "GG_EVENT_DCC_CALLBACK"}, - {GG_EVENT_DCC_NEED_FILE_INFO, "GG_EVENT_DCC_NEED_FILE_INFO"}, - {GG_EVENT_DCC_NEED_FILE_ACK, "GG_EVENT_DCC_NEED_FILE_ACK"}, - {GG_EVENT_DCC_NEED_VOICE_ACK, "GG_EVENT_DCC_NEED_VOICE_ACK"}, - {GG_EVENT_DCC_VOICE_DATA, "GG_EVENT_DCC_VOICE_DATA"}, - {GG_EVENT_PUBDIR50_SEARCH_REPLY,"GG_EVENT_PUBDIR50_SEARCH_REPLY"}, - {GG_EVENT_PUBDIR50_READ, "GG_EVENT_PUBDIR50_READ"}, - {GG_EVENT_PUBDIR50_WRITE, "GG_EVENT_PUBDIR50_WRITE"}, - {GG_EVENT_STATUS60, "GG_EVENT_STATUS60"}, - {GG_EVENT_NOTIFY60, "GG_EVENT_NOTIFY60"}, - {GG_EVENT_USERLIST, "GG_EVENT_USERLIST"}, - {GG_EVENT_IMAGE_REQUEST, "GG_EVENT_IMAGE_REQUEST"}, - {GG_EVENT_IMAGE_REPLY, "GG_EVENT_IMAGE_REPLY"}, - {GG_EVENT_DCC_ACK, "GG_EVENT_DCC_ACK"}, - {GG_EVENT_DCC7_NEW, "GG_EVENT_DCC7_NEW"}, - {GG_EVENT_DCC7_ACCEPT, "GG_EVENT_DCC7_ACCEPT"}, - {GG_EVENT_DCC7_REJECT, "GG_EVENT_DCC7_REJECT"}, - {GG_EVENT_DCC7_CONNECTED, "GG_EVENT_DCC7_CONNECTED"}, - {GG_EVENT_DCC7_ERROR, "GG_EVENT_DCC7_ERROR"}, - {GG_EVENT_DCC7_DONE, "GG_EVENT_DCC7_DONE"}, - {GG_EVENT_DCC7_PENDING, "GG_EVENT_DCC7_PENDING"}, - {GG_EVENT_XML_EVENT, "GG_EVENT_XML_EVENT"}, - {GG_EVENT_DISCONNECT_ACK, "GG_EVENT_DISCONNECT_ACK"}, - {GG_EVENT_XML_ACTION, "GG_EVENT_XML_ACTION"}, - {GG_EVENT_TYPING_NOTIFICATION, "GG_EVENT_TYPING_NOTIFICATION"}, - {GG_EVENT_USER_DATA, "GG_EVENT_USER_DATA"}, - {GG_EVENT_MULTILOGON_MSG, "GG_EVENT_MULTILOGON_MSG"}, - {GG_EVENT_MULTILOGON_INFO, "GG_EVENT_MULTILOGON_INFO"}, - {-1, ""} -}; - -const char *ggdebug_eventtype(struct gg_event *e) -{ - int i; - for(i = 0; ggdebug_eventype2string[i].type != -1; i++) - if(ggdebug_eventype2string[i].type == e->type) - return ggdebug_eventype2string[i].text; - return ggdebug_eventype2string[i].text; -} - -#ifdef DEBUGMODE -void gg_debughandler(int level, const char *format, va_list ap) -{ - char szText[1024], *szFormat = _strdup(format); - // Kill end line - char *nl = strrchr(szFormat, '\n'); - if(nl) *nl = 0; - - strncpy(szText, "[libgadu] \0", sizeof(szText)); - - mir_vsnprintf(szText + strlen(szText), sizeof(szText) - strlen(szText), szFormat, ap); - CallService(MS_NETLIB_LOG, (WPARAM) NULL, (LPARAM) szText); - free(szFormat); -} -#endif - -////////////////////////////////////////////////////////// -// Log funcion -int gg_netlog(const GGPROTO *gg, const char *fmt, ...) -{ - va_list va; - char szText[1024]; - - va_start(va, fmt); - mir_vsnprintf(szText, sizeof(szText), fmt, va); - va_end(va); - return CallService(MS_NETLIB_LOG, (WPARAM) gg->netlib, (LPARAM) szText); -} - -////////////////////////////////////////////////////////// -// main DLL function -BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, LPVOID reserved) -{ - crc_gentable(); - hInstance = hInst; -#ifdef DEBUGMODE - gg_debug_handler = gg_debughandler; -#endif - return TRUE; -} diff --git a/protocols/Gadu-Gadu/gg.cpp b/protocols/Gadu-Gadu/gg.cpp new file mode 100644 index 0000000000..023d620f1a --- /dev/null +++ b/protocols/Gadu-Gadu/gg.cpp @@ -0,0 +1,527 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// Copyright (c) 2009-2012 Bartosz Białek +// +// 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 "gg.h" +#include "version.h" +#include + +// Plugin info +PLUGININFOEX pluginInfo = { + sizeof(PLUGININFOEX), + "Gadu-Gadu Protocol", + __VERSION_DWORD, + "Provides support for Gadu-Gadu protocol", + "Bartosz Białek, Adam Strzelecki", + "dezred"/*antispam*/"@"/*antispam*/"gmail"/*antispam*/"."/*antispam*/"com", + "© 2009-2012 Bartosz Białek, 2003-2009 Adam Strzelecki", + "http://www.miranda-im.pl/", + 0, + // {F3FF65F3-250E-416A-BEE9-58C93F85AB33} + { 0xf3ff65f3, 0x250e, 0x416a, { 0xbe, 0xe9, 0x58, 0xc9, 0x3f, 0x85, 0xab, 0x33 } } +}; +static const MUUID interfaces[] = {MIID_PROTOCOL, MIID_LAST}; + +// Other variables +HINSTANCE hInstance; + +XML_API xi; +SSL_API si; +CLIST_INTERFACE *pcli; +int hLangpack; +list_t g_Instances; + +// Event hooks +static HANDLE hHookModulesLoaded = NULL; +static HANDLE hHookPreShutdown = NULL; + +static unsigned long crc_table[256]; + +////////////////////////////////////////////////////////// +// Extra winsock function for error description + +TCHAR* ws_strerror(int code) +{ + static TCHAR err_desc[160]; + + // Not a windows error display WinSock + if (code == 0) + { + TCHAR buff[128]; + int len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, WSAGetLastError(), 0, buff, SIZEOF(buff), NULL); + if (len == 0) + mir_sntprintf(err_desc, SIZEOF(err_desc), _T("WinSock %u: Unknown error."), WSAGetLastError()); + else + mir_sntprintf(err_desc, SIZEOF(err_desc), _T("WinSock %d: %s"), WSAGetLastError(), buff); + return err_desc; + } + + // Return normal error + return _tcserror(code); +} + +////////////////////////////////////////////////////////// +// Build the crc table +void crc_gentable(void) +{ + unsigned long crc, poly; + int i, j; + + poly = 0xEDB88320L; + for (i = 0; i < 256; i++) + { + crc = i; + for (j = 8; j > 0; j--) + { + if (crc & 1) + crc = (crc >> 1) ^ poly; + else + crc >>= 1; + } + crc_table[i] = crc; + } +} + +////////////////////////////////////////////////////////// +// Calculate the crc value +unsigned long crc_get(char *mem) +{ + register unsigned long crc = 0xFFFFFFFF; + while(mem && *mem) + crc = ((crc>>8) & 0x00FFFFFF) ^ crc_table[(crc ^ *(mem++)) & 0xFF]; + + return (crc ^ 0xFFFFFFFF); +} + +////////////////////////////////////////////////////////// +// http_error_string() +// +// returns http error text +const char *http_error_string(int h) +{ + switch (h) + { + case 0: + return Translate((errno == ENOMEM) ? "HTTP failed memory" : "HTTP failed connecting"); + case GG_ERROR_RESOLVING: + return Translate("HTTP failed resolving"); + case GG_ERROR_CONNECTING: + return Translate("HTTP failed connecting"); + case GG_ERROR_READING: + return Translate("HTTP failed reading"); + case GG_ERROR_WRITING: + return Translate("HTTP failed writing"); + } + + return Translate("Unknown HTTP error"); +} + +////////////////////////////////////////////////////////// +// Gets plugin info + +extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion) +{ + return &pluginInfo; +} + +extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void) +{ + return interfaces; +} + +////////////////////////////////////////////////////////// +// Cleanups from last plugin + +void GGPROTO::cleanuplastplugin(DWORD version) +{ + HANDLE hContact; + char *szProto; + + // Remove bad e-mail and phones from + if (version < PLUGIN_MAKE_VERSION(0, 0, 1, 4)) + { +#ifdef DEBUGMODE + netlog("gg_cleanuplastplugin(%d): Cleaning junk Phone settings from < 0.0.1.4 ...", version); +#endif + // Look for contact in DB + hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while (hContact) + { + szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && !strcmp(szProto, m_szModuleName)) + { + // Do contact cleanup + db_unset(hContact, m_szModuleName, GG_KEY_EMAIL); + db_unset(hContact, m_szModuleName, "Phone"); + } + hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); + } + } + + // Remove GG entries for non GG contacts + if (version < PLUGIN_MAKE_VERSION(0, 0, 3, 5)) + { +#ifdef DEBUGMODE + netlog("gg_cleanuplastplugin(%d): Cleaning junk Nick settings from < 0.0.3.5 ...", version); +#endif + // Look for contact in DB + hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while (hContact) + { + szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && strcmp(szProto, m_szModuleName)) + { + // Do nick entry cleanup + db_unset(hContact, m_szModuleName, GG_KEY_NICK); + } + hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); + } + } + + // Remove old unneeded entry + if (version < PLUGIN_MAKE_VERSION(0, 0, 5, 3)) + db_unset(NULL, m_szModuleName, "ShowNotOnMyList"); + + // Store this plugin version + db_set_w(NULL, m_szModuleName, GG_PLUGINVERSION, pluginInfo.version); +} + +////////////////////////////////////////////////////////// +// When Miranda loaded its modules +static int gg_modulesloaded(WPARAM wParam, LPARAM lParam) +{ + // Get SSL API + mir_getSI(&si); + + // File Association Manager support + gg_links_init(); + + return 0; +} + +////////////////////////////////////////////////////////// +// When Miranda starting shutdown sequence +static int gg_preshutdown(WPARAM wParam, LPARAM lParam) +{ + gg_links_destroy(); + + return 0; +} + +////////////////////////////////////////////////////////// +// Gets protocol instance associated with a contact +static GGPROTO* gg_getprotoinstance(HANDLE hContact) +{ + char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + list_t l = g_Instances; + + if (szProto == NULL) + return NULL; + + for (; l; l = l->next) + { + GGPROTO* gg = (GGPROTO*)l->data; + if (strcmp(szProto, gg->m_szModuleName) == 0) + return gg; + } + + return NULL; +} + +////////////////////////////////////////////////////////// +// Handles PrebuildContactMenu event +static int gg_prebuildcontactmenu(WPARAM wParam, LPARAM lParam) +{ + const HANDLE hContact = (HANDLE)wParam; + CLISTMENUITEM mi = {0}; + GGPROTO* gg = gg_getprotoinstance(hContact); + + if (gg == NULL) + return 0; + + mi.cbSize = sizeof(mi); + mi.flags = CMIM_NAME | CMIM_FLAGS | CMIF_ICONFROMICOLIB; + if (db_get_b(hContact, gg->m_szModuleName, GG_KEY_UIN, 0) == db_get_b(NULL, gg->m_szModuleName, GG_KEY_UIN, 0) || + db_get_b(hContact, gg->m_szModuleName, "ChatRoom", 0) || + db_get_b(hContact, "CList", "NotOnList", 0)) + mi.flags |= CMIF_HIDDEN; + mi.pszName = db_get_b(hContact, gg->m_szModuleName, GG_KEY_BLOCK, 0) ? LPGEN("&Unblock") : LPGEN("&Block"); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)gg->hBlockMenuItem, (LPARAM)&mi); + + return 0; +} + +////////////////////////////////////////////////////////// +// Contact block service function +INT_PTR GGPROTO::blockuser(WPARAM wParam, LPARAM lParam) +{ + const HANDLE hContact = (HANDLE)wParam; + db_set_b(hContact, m_szModuleName, GG_KEY_BLOCK, !db_get_b(hContact, m_szModuleName, GG_KEY_BLOCK, 0)); + notifyuser(hContact, 1); + return 0; +} + + +////////////////////////////////////////////////////////// +// Contact blocking initialization + +#define GGS_BLOCKUSER "%s/BlockUser" +void GGPROTO::block_init() +{ + CLISTMENUITEM mi = {0}; + char service[64]; + + mi.cbSize = sizeof(mi); + mi.flags = CMIF_ICONFROMICOLIB; + + mir_snprintf(service, sizeof(service), GGS_BLOCKUSER, m_szModuleName); + createProtoService(service, &GGPROTO::blockuser); + mi.position = -500050000; + mi.icolibItem = GetIconHandle(IDI_BLOCK); + mi.pszName = LPGEN("&Block"); + mi.pszService = service; + mi.pszContactOwner = m_szModuleName; + hBlockMenuItem = Menu_AddContactMenuItem(&mi); + + hPrebuildMenuHook = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, gg_prebuildcontactmenu); +} + +////////////////////////////////////////////////////////// +// Contact blocking uninitialization + +void GGPROTO::block_uninit() +{ + UnhookEvent(hPrebuildMenuHook); + CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)hBlockMenuItem, 0); +} + +////////////////////////////////////////////////////////// +// Menus initialization +void GGPROTO::menus_init() +{ + HGENMENU hGCRoot, hCLRoot, hRoot = MO_GetProtoRootMenu(m_szModuleName); + CLISTMENUITEM mi = {0}; + + mi.cbSize = sizeof(mi); + if (hRoot == NULL) + { + mi.ptszName = m_tszUserName; + mi.position = 500090000; + mi.hParentMenu = HGENMENU_ROOT; + mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTPOPUP | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED; + mi.icolibItem = GetIconHandle(IDI_GG); + hGCRoot = hCLRoot = hRoot = hMenuRoot = Menu_AddProtoMenuItem(&mi); + } + else + { + mi.hParentMenu = hRoot; + mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE | CMIF_TCHAR; + + mi.ptszName = LPGENT("Conference"); + mi.position = 200001; + mi.icolibItem = GetIconHandle(IDI_CONFERENCE); + hGCRoot = Menu_AddProtoMenuItem(&mi); + + mi.ptszName = LPGENT("Contact list"); + mi.position = 200002; + mi.icolibItem = GetIconHandle(IDI_LIST); + hCLRoot = Menu_AddProtoMenuItem(&mi); + + if (hMenuRoot) + CallService(MS_CLIST_REMOVEMAINMENUITEM, (WPARAM)hMenuRoot, 0); + hMenuRoot = NULL; + } + + gc_menus_init(hGCRoot); + import_init(hCLRoot); + sessions_menus_init(hRoot); +} + +////////////////////////////////////////////////////////// +// Module instance initialization + +static GGPROTO *gg_proto_init(const char* pszProtoName, const TCHAR* tszUserName) +{ + GGPROTO *gg = new GGPROTO(pszProtoName, tszUserName); + list_add(&g_Instances, gg, 0); + return gg; +} + +////////////////////////////////////////////////////////// +// Module instance uninitialization + +static int gg_proto_uninit(PROTO_INTERFACE *proto) +{ + GGPROTO *gg = (GGPROTO *)proto; + list_remove(&g_Instances, gg, 0); + delete gg; + return 0; +} + +////////////////////////////////////////////////////////// +// When plugin is loaded + +extern "C" int __declspec(dllexport) Load(void) +{ + mir_getXI(&xi); + mir_getLP(&pluginInfo); + + pcli = (CLIST_INTERFACE*)CallService(MS_CLIST_RETRIEVE_INTERFACE, 0, (LPARAM)hInstance); + + // Hook system events + hHookModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, gg_modulesloaded); + hHookPreShutdown = HookEvent(ME_SYSTEM_PRESHUTDOWN, gg_preshutdown); + + // Prepare protocol name + PROTOCOLDESCRIPTOR pd = { 0 }; + pd.cbSize = sizeof(pd); + pd.szName = GGDEF_PROTO; + pd.fnInit = (pfnInitProto)gg_proto_init; + pd.fnUninit = (pfnUninitProto)gg_proto_uninit; + pd.type = PROTOTYPE_PROTOCOL; + + // Register module + CallService(MS_PROTO_REGISTERMODULE, 0, (LPARAM) &pd); + gg_links_instancemenu_init(); + + // Instance list + g_Instances = NULL; + + return 0; +} + +////////////////////////////////////////////////////////// +// When plugin is unloaded + +extern "C" int __declspec(dllexport) Unload() +{ + LocalEventUnhook(hHookModulesLoaded); + LocalEventUnhook(hHookPreShutdown); + + // Cleanup WinSock + WSACleanup(); + return 0; +} + +////////////////////////////////////////////////////////// +// DEBUGING FUNCTIONS +struct +{ + int type; + char *text; +} +static const ggdebug_eventype2string[] = +{ + {GG_EVENT_NONE, "GG_EVENT_NONE"}, + {GG_EVENT_MSG, "GG_EVENT_MSG"}, + {GG_EVENT_NOTIFY, "GG_EVENT_NOTIFY"}, + {GG_EVENT_NOTIFY_DESCR, "GG_EVENT_NOTIFY_DESCR"}, + {GG_EVENT_STATUS, "GG_EVENT_STATUS"}, + {GG_EVENT_ACK, "GG_EVENT_ACK"}, + {GG_EVENT_PONG, "GG_EVENT_PONG"}, + {GG_EVENT_CONN_FAILED, "GG_EVENT_CONN_FAILED"}, + {GG_EVENT_CONN_SUCCESS, "GG_EVENT_CONN_SUCCESS"}, + {GG_EVENT_DISCONNECT, "GG_EVENT_DISCONNECT"}, + {GG_EVENT_DCC_NEW, "GG_EVENT_DCC_NEW"}, + {GG_EVENT_DCC_ERROR, "GG_EVENT_DCC_ERROR"}, + {GG_EVENT_DCC_DONE, "GG_EVENT_DCC_DONE"}, + {GG_EVENT_DCC_CLIENT_ACCEPT, "GG_EVENT_DCC_CLIENT_ACCEPT"}, + {GG_EVENT_DCC_CALLBACK, "GG_EVENT_DCC_CALLBACK"}, + {GG_EVENT_DCC_NEED_FILE_INFO, "GG_EVENT_DCC_NEED_FILE_INFO"}, + {GG_EVENT_DCC_NEED_FILE_ACK, "GG_EVENT_DCC_NEED_FILE_ACK"}, + {GG_EVENT_DCC_NEED_VOICE_ACK, "GG_EVENT_DCC_NEED_VOICE_ACK"}, + {GG_EVENT_DCC_VOICE_DATA, "GG_EVENT_DCC_VOICE_DATA"}, + {GG_EVENT_PUBDIR50_SEARCH_REPLY,"GG_EVENT_PUBDIR50_SEARCH_REPLY"}, + {GG_EVENT_PUBDIR50_READ, "GG_EVENT_PUBDIR50_READ"}, + {GG_EVENT_PUBDIR50_WRITE, "GG_EVENT_PUBDIR50_WRITE"}, + {GG_EVENT_STATUS60, "GG_EVENT_STATUS60"}, + {GG_EVENT_NOTIFY60, "GG_EVENT_NOTIFY60"}, + {GG_EVENT_USERLIST, "GG_EVENT_USERLIST"}, + {GG_EVENT_IMAGE_REQUEST, "GG_EVENT_IMAGE_REQUEST"}, + {GG_EVENT_IMAGE_REPLY, "GG_EVENT_IMAGE_REPLY"}, + {GG_EVENT_DCC_ACK, "GG_EVENT_DCC_ACK"}, + {GG_EVENT_DCC7_NEW, "GG_EVENT_DCC7_NEW"}, + {GG_EVENT_DCC7_ACCEPT, "GG_EVENT_DCC7_ACCEPT"}, + {GG_EVENT_DCC7_REJECT, "GG_EVENT_DCC7_REJECT"}, + {GG_EVENT_DCC7_CONNECTED, "GG_EVENT_DCC7_CONNECTED"}, + {GG_EVENT_DCC7_ERROR, "GG_EVENT_DCC7_ERROR"}, + {GG_EVENT_DCC7_DONE, "GG_EVENT_DCC7_DONE"}, + {GG_EVENT_DCC7_PENDING, "GG_EVENT_DCC7_PENDING"}, + {GG_EVENT_XML_EVENT, "GG_EVENT_XML_EVENT"}, + {GG_EVENT_DISCONNECT_ACK, "GG_EVENT_DISCONNECT_ACK"}, + {GG_EVENT_XML_ACTION, "GG_EVENT_XML_ACTION"}, + {GG_EVENT_TYPING_NOTIFICATION, "GG_EVENT_TYPING_NOTIFICATION"}, + {GG_EVENT_USER_DATA, "GG_EVENT_USER_DATA"}, + {GG_EVENT_MULTILOGON_MSG, "GG_EVENT_MULTILOGON_MSG"}, + {GG_EVENT_MULTILOGON_INFO, "GG_EVENT_MULTILOGON_INFO"}, + {-1, ""} +}; + +const char *ggdebug_eventtype(gg_event *e) +{ + int i; + for(i = 0; ggdebug_eventype2string[i].type != -1; i++) + if (ggdebug_eventype2string[i].type == e->type) + return ggdebug_eventype2string[i].text; + return ggdebug_eventype2string[i].text; +} + +#ifdef DEBUGMODE +void gg_debughandler(int level, const char *format, va_list ap) +{ + char szText[1024], *szFormat = _strdup(format); + // Kill end line + char *nl = strrchr(szFormat, '\n'); + if (nl) *nl = 0; + + strncpy(szText, "[libgadu] \0", sizeof(szText)); + + mir_vsnprintf(szText + strlen(szText), sizeof(szText) - strlen(szText), szFormat, ap); + CallService(MS_NETLIB_LOG, (WPARAM) NULL, (LPARAM) szText); + free(szFormat); +} +#endif + +////////////////////////////////////////////////////////// +// Log funcion + +int GGPROTO::netlog(const char *fmt, ...) +{ + va_list va; + char szText[1024]; + + va_start(va, fmt); + mir_vsnprintf(szText, sizeof(szText), fmt, va); + va_end(va); + return CallService(MS_NETLIB_LOG, (WPARAM)netlib, (LPARAM) szText); +} + +////////////////////////////////////////////////////////// +// main DLL function + +BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, LPVOID reserved) +{ + crc_gentable(); + hInstance = hInst; +#ifdef DEBUGMODE + gg_debug_handler = gg_debughandler; +#endif + return TRUE; +} diff --git a/protocols/Gadu-Gadu/gg.h b/protocols/Gadu-Gadu/gg.h index 7f247c553f..2b1157092b 100644 --- a/protocols/Gadu-Gadu/gg.h +++ b/protocols/Gadu-Gadu/gg.h @@ -34,10 +34,6 @@ #include -#ifdef __cplusplus -extern "C" { -#endif - // Windows headers // Visual C++ .NET tries to include winsock.h // which is very ver bad @@ -56,6 +52,7 @@ extern "C" { // Miranda IM headers #include #include +#include #include #include #include @@ -101,75 +98,24 @@ extern "C" { #include "resource.h" // libgadu headers +extern "C" { #include "libgadu/libgadu.h" #include "dynstuff.h" +}; // Search // Extended search result structure, used for all searches -typedef struct +struct GGSEARCHRESULT : public PROTOSEARCHRESULT { - PROTOSEARCHRESULT hdr; uin_t uin; -} GGSEARCHRESULT; +}; typedef struct { HANDLE hThread; - DWORD dwThreadId; + UINT dwThreadId; } GGTHREAD; -typedef struct -{ - PROTO_INTERFACE proto; - LPTSTR name; - CRITICAL_SECTION ft_mutex, sess_mutex, img_mutex, modemsg_mutex, avatar_mutex, sessions_mutex; - list_t watches, transfers, requests, chats, imagedlgs, avatar_requests, avatar_transfers, sessions; - int gc_enabled, gc_id, list_remove, unicode_core, check_first_conn; - uin_t next_uin; - unsigned long last_crc; - GGTHREAD pth_dcc; - GGTHREAD pth_sess; - GGTHREAD pth_avatar; - struct gg_session *sess; - struct gg_dcc *dcc; - HANDLE event; - HANDLE hConnStopEvent; - SOCKET sock; - UINT_PTR timer; - struct - { - char *online; - char *away; - char *dnd; - char *freechat; - char *invisible; - char *offline; - } modemsg; - HANDLE netlib, - hookOptsInit, - hookUserInfoInit, - hookGCUserEvent, - hookGCMenuBuild; - HGENMENU hMenuRoot; - HGENMENU hMainMenu[7]; - HANDLE hPrebuildMenuHook; - HANDLE hBlockMenuItem; - HANDLE hImageMenuItem; - HANDLE hInstanceMenuItem; - HANDLE hAvatarsFolder; - HANDLE hImagesFolder; - HANDLE hwndSessionsDlg; -} GGPROTO; - -typedef struct -{ - int mode; - uin_t uin; - char *pass; - char *email; - GGPROTO *gg; -} GGUSERUTILDLGDATA; - typedef struct { uin_t *recipients; @@ -184,18 +130,13 @@ typedef struct char val[256]; } GGTOKEN; -// GG Thread Function -typedef void (__cdecl GGThreadFunc)(void*, void*); - #if 0 /* #ifdef DEBUGMODE */ -#define EnterCriticalSection(lpCS) {gg_netlog(gg,"EnterCriticalSection @ %s:%d", __FILE__, __LINE__); EnterCriticalSection(lpCS);} -#define LeaveCriticalSection(lpCS) {gg_netlog(gg,"LeaveCriticalSection @ %s:%d", __FILE__, __LINE__); LeaveCriticalSection(lpCS);} +#define EnterCriticalSection(lpCS) {netlog(gg,"EnterCriticalSection @ %s:%d", __FILE__, __LINE__); EnterCriticalSection(lpCS);} +#define LeaveCriticalSection(lpCS) {netlog(gg,"LeaveCriticalSection @ %s:%d", __FILE__, __LINE__); LeaveCriticalSection(lpCS);} #endif // Wrappers of the old interface -#define GG_PROTO (gg->proto.m_szModuleName) -#define GG_PROTONAME (gg->name) #define GGDEF_PROTO "GG" // Default Proto #define GGDEF_PROTONAME "Gadu-Gadu" // Default ProtoName @@ -338,7 +279,7 @@ typedef void (__cdecl GGThreadFunc)(void*, void*); #define GG_POPUP_WARNING 8 #define GG_POPUP_MULTILOGON 16 -#define LocalEventUnhook(hook) if(hook) UnhookEvent(hook) +#define LocalEventUnhook(hook) if (hook) UnhookEvent(hook) // Some MSVC compatibility with gcc #ifdef _MSC_VER @@ -356,6 +297,7 @@ typedef void (__cdecl GGThreadFunc)(void*, void*); extern HINSTANCE hInstance; extern CLIST_INTERFACE *pcli; extern list_t g_Instances; +extern PLUGININFOEX pluginInfo; // Screen saver #ifndef SPI_GETSCREENSAVERRUNNING @@ -368,80 +310,14 @@ extern list_t g_Instances; /* Helper functions */ const char *http_error_string(int h); unsigned long crc_get(char *mem); -int status_m2gg(GGPROTO *gg, int status, int descr); -int status_gg2m(GGPROTO *gg, int status); +int gg_normalizestatus(int status); char *gg_status2db(int status, const char *suffix); -char *ws_strerror(int code); +TCHAR *ws_strerror(int code); uint32_t swap32(uint32_t x); const char *gg_version2string(int v); -void gg_checknewuser(GGPROTO* gg, uin_t uin, const char* passwd); - -/* Thread functions */ -void gg_forkthread(GGPROTO *gg, GGThreadFunc pFunc, void *param); -HANDLE gg_forkthreadex(GGPROTO *gg, GGThreadFunc pFunc, void *param, UINT *threadId); -void gg_threadwait(GGPROTO *gg, GGTHREAD *thread); - -/* Global GG functions */ -void gg_notifyuser(GGPROTO *gg, HANDLE hContact, int refresh); -void gg_setalloffline(GGPROTO *gg); -void gg_disconnect(GGPROTO *gg); -HANDLE gg_getcontact(GGPROTO *gg, uin_t uin, int create, int inlist, char *nick); -void gg_registerservices(GGPROTO *gg); -void __cdecl gg_mainthread(GGPROTO *gg, void *empty); -int gg_isonline(GGPROTO *gg); -int gg_refreshstatus(GGPROTO *gg, int status); - -void gg_broadcastnewstatus(GGPROTO *gg, int newStatus); -int gg_contactdeleted(GGPROTO *gg, WPARAM wParam, LPARAM lParam); -int gg_dbsettingchanged(GGPROTO *gg, WPARAM wParam, LPARAM lParam); -void gg_notifyall(GGPROTO *gg); -void gg_changecontactstatus(GGPROTO *gg, uin_t uin, int status, const char *idescr, int time, uint32_t remote_ip, uint16_t remote_port, uint32_t version); -char *gg_getstatusmsg(GGPROTO *gg, int status); -void gg_dccstart(GGPROTO *gg); -void gg_dccconnect(GGPROTO *gg, uin_t uin); -int gg_gettoken(GGPROTO *gg, GGTOKEN *token); -void gg_parsecontacts(GGPROTO *gg, char *contacts); -int gg_getinfo(PROTO_INTERFACE *proto, HANDLE hContact, int infoType); -void gg_remindpassword(GGPROTO *gg, uin_t uin, const char *email); -void *gg_img_loadpicture(GGPROTO *gg, struct gg_event* e, char *szFileName); -int gg_img_releasepicture(void *img); -int gg_img_display(GGPROTO *gg, HANDLE hContact, void *img); -int gg_img_displayasmsg(GGPROTO *gg, HANDLE hContact, void *img); -int gg_event(PROTO_INTERFACE *proto, PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam); /* Avatar functions */ -void gg_getavatarfilename(GGPROTO *gg, HANDLE hContact, char *pszDest, int cbLen); char *gg_avatarhash(char *param); -void gg_getavatar(GGPROTO *gg, HANDLE hContact, char *szAvatarURL); -void gg_requestavatar(GGPROTO *gg, HANDLE hContact, int iWaitFor); -void gg_initavatarrequestthread(GGPROTO *gg); -void gg_uninitavatarrequestthread(GGPROTO *gg); -void gg_getuseravatar(GGPROTO *gg); -void gg_setavatar(GGPROTO *gg, const char *szFilename); -INT_PTR gg_getavatarinfo(GGPROTO *gg, WPARAM wParam, LPARAM lParam); - -/* File transfer functions */ -HANDLE gg_fileallow(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath); -int gg_filecancel(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hTransfer); -int gg_filedeny(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason); -int gg_recvfile(PROTO_INTERFACE *proto, HANDLE hContact, PROTOFILEEVENT* pre); -HANDLE gg_sendfile(PROTO_INTERFACE *proto, HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles); - -/* Import module */ -void gg_import_init(GGPROTO *gg, HGENMENU hRoot); - -/* Keep-alive module */ -void gg_keepalive_init(GGPROTO *gg); -void gg_keepalive_destroy(GGPROTO *gg); - -/* Image reception functions */ -int gg_img_init(GGPROTO *gg); -int gg_img_destroy(GGPROTO *gg); -int gg_img_shutdown(GGPROTO *gg); -INT_PTR gg_img_recvimage(GGPROTO *gg, WPARAM wParam, LPARAM lParam); -INT_PTR gg_img_sendimage(GGPROTO *gg, WPARAM wParam, LPARAM lParam); -int gg_img_sendonrequest(GGPROTO *gg, struct gg_event* e); -BOOL gg_img_opened(GGPROTO *gg, uin_t uin); /* IcoLib functions */ void gg_icolib_init(); @@ -455,50 +331,12 @@ void WindowFreeIcon(HWND hWnd); void gg_links_instancemenu_init(); void gg_links_init(); void gg_links_destroy(); -void gg_links_instance_init(GGPROTO* gg); - -/* OAuth functions */ -char *gg_oauth_header(GGPROTO *gg, const char *httpmethod, const char *url); -int gg_oauth_checktoken(GGPROTO *gg, int force); - -/* UI page initializers */ -int gg_options_init(GGPROTO *gg, WPARAM wParam, LPARAM lParam); -int gg_details_init(GGPROTO *gg, WPARAM wParam, LPARAM lParam); - -/* Groupchat functions */ -int gg_gc_init(GGPROTO *gg); -void gg_gc_menus_init(GGPROTO *gg, HGENMENU hRoot); -int gg_gc_destroy(GGPROTO *gg); -char * gg_gc_getchat(GGPROTO *gg, uin_t sender, uin_t *recipients, int recipients_count); -GGGC *gg_gc_lookup(GGPROTO *gg, char *id); -int gg_gc_changenick(GGPROTO *gg, HANDLE hContact, char *pszNick); -#define UIN2ID(uin,id) _itoa(uin,id,10) - -/* Popups functions */ -void gg_initpopups(GGPROTO* gg); -void gg_showpopup(GGPROTO* gg, const char* nickname, const char* msg, int flags); -/* Sessions functions */ -INT_PTR gg_sessions_view(GGPROTO* gg, WPARAM wParam, LPARAM lParam); -void gg_sessions_updatedlg(GGPROTO* gg); -BOOL gg_sessions_closedlg(GGPROTO* gg); -void gg_sessions_menus_init(GGPROTO* gg, HGENMENU hRoot); - -/* Event helpers */ -#define HookProtoEvent(name, func, proto) HookEventObj(name, (MIRANDAHOOKOBJ)func, proto) -#define CreateProtoServiceFunction(name, func, proto) CreateServiceFunctionObj(name, (MIRANDASERVICEOBJ)func, proto) -typedef INT_PTR (*GGPROTOFUNC)(GGPROTO*,WPARAM,LPARAM); -void CreateProtoService(const char* szService, GGPROTOFUNC serviceProc, GGPROTO *gg); - -/* ANSI <-> Unicode conversion helpers */ -#define gg_a2t(s) gg->unicode_core ? (TCHAR *)mir_a2u(s) : (TCHAR *)mir_strdup(s) -#define gg_t2a(s) gg->unicode_core ? mir_u2a((wchar_t *)s) : mir_strdup(s) +#define UIN2ID(uin,id) _itoa(uin,id,10) // Debug functions -int gg_netlog(const GGPROTO *gg, const char *fmt, ...); -const char *ggdebug_eventtype(struct gg_event *e); +const char *ggdebug_eventtype(gg_event *e); + +#include "gg_proto.h" -#ifdef __cplusplus -} -#endif #endif diff --git a/protocols/Gadu-Gadu/groupchat.c b/protocols/Gadu-Gadu/groupchat.c deleted file mode 100644 index ddeea1a7cf..0000000000 --- a/protocols/Gadu-Gadu/groupchat.c +++ /dev/null @@ -1,681 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 Adam Strzelecki -// -// 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 "gg.h" -#include "m_metacontacts.h" - -#define GG_GC_GETCHAT "%s/GCGetChat" -#define GGS_OPEN_CONF "%s/OpenConf" -#define GGS_CLEAR_IGNORED "%s/ClearIgnored" - -int gg_gc_event(GGPROTO *gg, WPARAM wParam, LPARAM lParam); -INT_PTR gg_gc_openconf(GGPROTO *gg, WPARAM wParam, LPARAM lParam); -INT_PTR gg_gc_clearignored(GGPROTO *gg, WPARAM wParam, LPARAM lParam); - -//////////////////////////////////////////////////////////////////////////////// -// Inits Gadu-Gadu groupchat module using chat.dll -int gg_gc_init(GGPROTO *gg) -{ - if(ServiceExists(MS_GC_REGISTER)) - { - char service[64]; - GCREGISTER gcr = {0}; - - // Register Gadu-Gadu proto - gcr.cbSize = sizeof(GCREGISTER); - gcr.dwFlags = GC_TCHAR; - gcr.iMaxText = 0; - gcr.nColors = 0; - gcr.pColors = 0; - gcr.ptszModuleDispName = gg->proto.m_tszUserName; - gcr.pszModule = GG_PROTO; - CallServiceSync(MS_GC_REGISTER, 0, (LPARAM)&gcr); - gg->hookGCUserEvent = HookProtoEvent(ME_GC_EVENT, gg_gc_event, gg); - gg->gc_enabled = TRUE; - // create & hook event - mir_snprintf(service, 64, GG_GC_GETCHAT, GG_PROTO); - gg_netlog(gg, "gg_gc_init(): Registered with groupchat plugin."); - } - else - gg_netlog(gg, "gg_gc_init(): Cannot register with groupchat plugin !!!"); - - return 1; -} - -//////////////////////////////////////////////////////////////////////////////// -// Groupchat menus initialization -void gg_gc_menus_init(GGPROTO *gg, HGENMENU hRoot) -{ - if(gg->gc_enabled) - { - CLISTMENUITEM mi = {0}; - char service[64]; - - mi.cbSize = sizeof(mi); - mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE; - mi.hParentMenu = hRoot; - - // Conferencing - mir_snprintf(service, sizeof(service), GGS_OPEN_CONF, GG_PROTO); - CreateProtoServiceFunction(service, gg_gc_openconf, gg); - mi.position = 2000050001; - mi.icolibItem = GetIconHandle(IDI_CONFERENCE); - mi.pszName = LPGEN("Open &conference..."); - mi.pszService = service; - gg->hMainMenu[0] = Menu_AddProtoMenuItem(&mi); - - // Clear ignored conferences - mir_snprintf(service, sizeof(service), GGS_CLEAR_IGNORED, GG_PROTO); - CreateProtoServiceFunction(service, gg_gc_clearignored, gg); - mi.position = 2000050002; - mi.icolibItem = GetIconHandle(IDI_CLEAR_CONFERENCE); - mi.pszName = LPGEN("&Clear ignored conferences"); - mi.pszService = service; - gg->hMainMenu[1] = Menu_AddProtoMenuItem(&mi); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Releases Gadu-Gadu groupchat module using chat.dll -int gg_gc_destroy(GGPROTO *gg) -{ - list_t l; - for(l = gg->chats; l; l = l->next) - { - GGGC *chat = (GGGC *)l->data; - if(chat->recipients) free(chat->recipients); - } - list_destroy(gg->chats, 1); gg->chats = NULL; - LocalEventUnhook(gg->hookGCUserEvent); - LocalEventUnhook(gg->hookGCMenuBuild); - - return 1; -} - -GGGC *gg_gc_lookup(GGPROTO *gg, char *id) -{ - GGGC *chat; - list_t l; - - for(l = gg->chats; l; l = l->next) - { - chat = (GGGC *)l->data; - if(chat && !strcmp(chat->id, id)) - return chat; - } - - return NULL; -} - -int gg_gc_event(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - GCHOOK *gch = (GCHOOK *)lParam; - GGGC *chat = NULL; - uin_t uin; - - // Check if we got our protocol, and fields are set - if (!gch - || !gch->pDest - || !gch->pDest->pszID - || !gch->pDest->pszModule - || lstrcmpi(gch->pDest->pszModule, GG_PROTO) - || !(uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0)) - || !(chat = gg_gc_lookup(gg, gch->pDest->pszID))) - return 0; - - // Window terminated - if(gch->pDest->iType == SESSION_TERMINATE) - { - HANDLE hContact = NULL; - gg_netlog(gg, "gg_gc_event(): Terminating chat %x, id %s from chat window...", chat, gch->pDest->pszID); - // Destroy chat entry - free(chat->recipients); - list_remove(&gg->chats, chat, 1); - // Remove contact from contact list (duh!) should be done by chat.dll !! - hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); - while (hContact) - { - DBVARIANT dbv; - if (!DBGetContactSettingString(hContact, GG_PROTO, "ChatRoomID", &dbv)) - { - if(dbv.pszVal && !strcmp(gch->pDest->pszID, dbv.pszVal)) - CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0); - DBFreeVariant(&dbv); - } - hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); - } - return 1; - } - - // Message typed / send only if online - if(gg_isonline(gg) && (gch->pDest->iType == GC_USER_MESSAGE) && gch->pszText) - { - char id[32]; - DBVARIANT dbv; - GCDEST gcdest = {GG_PROTO, gch->pDest->pszID, GC_EVENT_MESSAGE}; - GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; - int lc; - - UIN2ID(uin, id); - - gcevent.pszUID = id; - gcevent.pszText = gch->pszText; - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_NICK, &dbv)) - gcevent.pszNick = dbv.pszVal; - else - gcevent.pszNick = Translate("Me"); - - // Get rid of CRLF at back - lc = (int)strlen(gch->pszText) - 1; - while(lc >= 0 && (gch->pszText[lc] == '\n' || gch->pszText[lc] == '\r')) gch->pszText[lc --] = 0; - gcevent.time = time(NULL); - gcevent.bIsMe = 1; - gcevent.dwFlags = GCEF_ADDTOLOG; - gg_netlog(gg, "gg_gc_event(): Sending conference message to room %s, \"%s\".", gch->pDest->pszID, gch->pszText); - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); - if(gcevent.pszNick == dbv.pszVal) DBFreeVariant(&dbv); - EnterCriticalSection(&gg->sess_mutex); - gg_send_message_confer(gg->sess, GG_CLASS_CHAT, chat->recipients_count, chat->recipients, gch->pszText); - LeaveCriticalSection(&gg->sess_mutex); - return 1; - } - - // Privmessage selected - if(gch->pDest->iType == GC_USER_PRIVMESS) - { - HANDLE hContact = NULL; - if ((uin = atoi(gch->pszUID)) && (hContact = gg_getcontact(gg, uin, 1, 0, NULL))) - CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, (LPARAM)0); - } - gg_netlog(gg, "gg_gc_event(): Unhandled event %d, chat %x, uin %d, text \"%s\".", gch->pDest->iType, chat, uin, gch->pszText); - - return 0; -} - -typedef struct _gg_gc_echat -{ - uin_t sender; - uin_t *recipients; - int recipients_count; - char * chat_id; -} gg_gc_echat; - -//////////////////////////////////////////////////////////////////////////////// -// This is main groupchat initialization routine -char *gg_gc_getchat(GGPROTO *gg, uin_t sender, uin_t *recipients, int recipients_count) -{ - list_t l; int i; - GGGC *chat; - char *senderName, status[256], *name, id[32]; - uin_t uin; DBVARIANT dbv; - GCSESSION gcwindow; - GCDEST gcdest = {GG_PROTO, 0, GC_EVENT_ADDGROUP}; - GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; - - gg_netlog(gg, "gg_gc_getchat(): Count %d.", recipients_count); - if (!recipients) return NULL; - - // Look for existing chat - for(l = gg->chats; l; l = l->next) - { - GGGC *chat = (GGGC *)l->data; - if (!chat) continue; - - if(chat->recipients_count == recipients_count + (sender ? 1 : 0)) - { - int i, j, found = 0, sok = (sender == 0); - if (!sok) for(i = 0; i < chat->recipients_count; i++) - if(sender == chat->recipients[i]) - { - sok = 1; - break; - } - if(sok) - for(i = 0; i < chat->recipients_count; i++) - for(j = 0; j < recipients_count; j++) - if(recipients[j] == chat->recipients[i]) found++; - // Found all recipients - if(found == recipients_count) - { - if(chat->ignore) - gg_netlog(gg, "gg_gc_getchat(): Ignoring existing id %s, size %d.", chat->id, chat->recipients_count); - else - gg_netlog(gg, "gg_gc_getchat(): Returning existing id %s, size %d.", chat->id, chat->recipients_count); - return !(chat->ignore) ? chat->id : NULL; - } - } - } - - // Make new uin list to chat mapping - chat = (GGGC *)malloc(sizeof(GGGC)); - UIN2ID(gg->gc_id ++, chat->id); chat->ignore = FALSE; - - // Check groupchat policy (new) / only for incoming - if(sender) - { - int unknown = (gg_getcontact(gg, sender, 0, 0, NULL) == NULL), - unknownSender = unknown; - for(i = 0; i < recipients_count; i++) - if (!gg_getcontact(gg, recipients[i], 0, 0, NULL)) - unknown ++; - if ((DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_DEFAULT, GG_KEYDEF_GC_POLICY_DEFAULT) == 2) || - (DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL) == 2 && - recipients_count >= DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) || - (DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN) == 2 && - unknown >= DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN))) - chat->ignore = TRUE; - if (!chat->ignore && ((DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_DEFAULT, GG_KEYDEF_GC_POLICY_DEFAULT) == 1) || - (DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL) == 1 && - recipients_count >= DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) || - (DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN) == 1 && - unknown >= DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN)))) - { - char *senderName = unknownSender ? - Translate("Unknown") : ((char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) gg_getcontact(gg, sender, 0, 0, NULL), 0)); - char error[256]; - mir_snprintf(error, sizeof(error), Translate("%s has initiated conference with %d participants (%d unknowns).\nDo you want do participate ?"), - senderName, recipients_count + 1, unknown); - chat->ignore = (MessageBox( - NULL, - error, - GG_PROTONAME, - MB_OKCANCEL | MB_ICONEXCLAMATION - ) != IDOK); - } - if(chat->ignore) - { - // Copy recipient list - chat->recipients_count = recipients_count + (sender ? 1 : 0); - chat->recipients = (uin_t *)calloc(chat->recipients_count, sizeof(uin_t)); - for(i = 0; i < recipients_count; i++) - chat->recipients[i] = recipients[i]; - if(sender) chat->recipients[i] = sender; - gg_netlog(gg, "gg_gc_getchat(): Ignoring new chat %s, count %d.", chat->id, chat->recipients_count); - list_add(&gg->chats, chat, 0); - return NULL; - } - } - - // Create new chat window - senderName = sender ? (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) gg_getcontact(gg, sender, 1, 0, NULL), 0) : NULL; - mir_snprintf(status, 255, sender ? Translate("%s initiated the conference.") : Translate("This is my own conference."), senderName); - gcwindow.cbSize = sizeof(GCSESSION); - gcwindow.iType = GCW_CHATROOM; - gcwindow.pszModule = GG_PROTO; - gcwindow.pszName = sender ? senderName : Translate("Conference"); - gcwindow.pszID = chat->id; - gcwindow.pszStatusbarText = status; - gcwindow.dwFlags = 0; - gcwindow.dwItemData = (DWORD)chat; - - // Here we put nice new hash sign - name = calloc(strlen(gcwindow.pszName) + 2, sizeof(char)); - *name = '#'; strcpy(name + 1, gcwindow.pszName); - gcwindow.pszName = name; - // Create new room - if(CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM) &gcwindow)) - { - gg_netlog(gg, "gg_gc_getchat(): Cannot create new chat window %s.", chat->id); - free(name); - free(chat); - return NULL; - } - free(name); - - gcdest.pszID = chat->id; - gcevent.pszUID = id; - gcevent.dwFlags = GCEF_ADDTOLOG; - gcevent.time = 0; - - // Add normal group - gcevent.pszStatus = Translate("Participants"); - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); - gcdest.iType = GC_EVENT_JOIN; - - // Add myself - if(uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0)) - { - UIN2ID(uin, id); - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_NICK, &dbv)) - gcevent.pszNick = dbv.pszVal; - else - gcevent.pszNick = Translate("Me"); - gcevent.bIsMe = 1; - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); - gg_netlog(gg, "gg_gc_getchat(): Myself %s: %s (%s) to the list...", gcevent.pszUID, gcevent.pszNick, gcevent.pszStatus); - if(gcevent.pszNick == dbv.pszVal) DBFreeVariant(&dbv); - } - else gg_netlog(gg, "gg_gc_getchat(): Myself adding failed with uin %d !!!", uin); - - // Copy recipient list - chat->recipients_count = recipients_count + (sender ? 1 : 0); - chat->recipients = (uin_t *)calloc(chat->recipients_count, sizeof(uin_t)); - for(i = 0; i < recipients_count; i++) - chat->recipients[i] = recipients[i]; - if(sender) chat->recipients[i] = sender; - - // Add contacts - for(i = 0; i < chat->recipients_count; i++) - { - HANDLE hContact = gg_getcontact(gg, chat->recipients[i], 1, 0, NULL); - UIN2ID(chat->recipients[i], id); - if(hContact && (name = (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, 0))) - gcevent.pszNick = name; - else - gcevent.pszNick = Translate("'Unknown'"); - gcevent.bIsMe = 0; - gg_netlog(gg, "gg_gc_getchat(): Added %s: %s (%s) to the list...", gcevent.pszUID, gcevent.pszNick, gcevent.pszStatus); - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); - } - gcdest.iType = GC_EVENT_CONTROL; - CallServiceSync(MS_GC_EVENT, SESSION_INITDONE, (LPARAM)&gcevent); - CallServiceSync(MS_GC_EVENT, SESSION_ONLINE, (LPARAM)&gcevent); - - gg_netlog(gg, "gg_gc_getchat(): Returning new chat window %s, count %d.", chat->id, chat->recipients_count); - list_add(&gg->chats, chat, 0); - return chat->id; -} - -static HANDLE gg_getsubcontact(GGPROTO* gg, HANDLE hContact) -{ - char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); - char* szMetaProto = (char*)CallService(MS_MC_GETPROTOCOLNAME, 0, 0); - - if (szProto && szMetaProto && (INT_PTR)szMetaProto != CALLSERVICE_NOTFOUND && !lstrcmp(szProto, szMetaProto)) - { - int nSubContacts = (int)CallService(MS_MC_GETNUMCONTACTS, (WPARAM)hContact, 0), i; - HANDLE hMetaContact; - for (i = 0; i < nSubContacts; i++) - { - hMetaContact = (HANDLE)CallService(MS_MC_GETSUBCONTACT, (WPARAM)hContact, i); - szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hMetaContact, 0); - if (szProto && !lstrcmp(szProto, GG_PROTO)) - return hMetaContact; - } - } - return NULL; -} - -static void gg_gc_resetclistopts(HWND hwndList) -{ - int i; - SendMessage(hwndList, CLM_SETLEFTMARGIN, 2, 0); - 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_SETINDENT, 10, 0); - SendMessage(hwndList, CLM_SETHIDEEMPTYGROUPS, (WPARAM)TRUE, 0); - for (i = 0; i <= FONTID_MAX; i++) - SendMessage(hwndList, CLM_SETTEXTCOLOR, i, GetSysColor(COLOR_WINDOWTEXT)); -} - -static int gg_gc_countcheckmarks(HWND hwndList) -{ - int count = 0; - HANDLE hItem, hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); - while (hContact) - { - hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0); - if (hItem && SendMessage(hwndList, CLM_GETCHECKMARK, (WPARAM)hItem, 0)) - count++; - hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); - } - return count; -} - -#define HM_SUBCONTACTSCHANGED (WM_USER + 100) - -static INT_PTR CALLBACK gg_gc_openconfdlg(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch(message) - { - case WM_INITDIALOG: - { - CLCINFOITEM cii = {0}; - HANDLE hMetaContactsEvent; - - SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)lParam); - TranslateDialogDefault(hwndDlg); - WindowSetIcon(hwndDlg, "conference"); - gg_gc_resetclistopts(GetDlgItem(hwndDlg, IDC_CLIST)); - - // Hook MetaContacts event (if available) - hMetaContactsEvent = HookEventMessage(ME_MC_SUBCONTACTSCHANGED, hwndDlg, HM_SUBCONTACTSCHANGED); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)hMetaContactsEvent); - } - return TRUE; - - case WM_COMMAND: - { - switch (LOWORD(wParam)) - { - case IDOK: - { - HWND hwndList = GetDlgItem(hwndDlg, IDC_CLIST); - GGPROTO* gg = (GGPROTO*)GetWindowLongPtr(hwndDlg, DWLP_USER); - int count = 0, i = 0; - // Check if connected - if (!gg_isonline(gg)) - { - MessageBox(NULL, - Translate("You have to be connected to open new conference."), - GG_PROTONAME, MB_OK | MB_ICONSTOP - ); - } - else if (hwndList && (count = gg_gc_countcheckmarks(hwndList)) >= 2) - { - // Create new participiants table - char* chat; - uin_t* participants = calloc(count, sizeof(uin_t)); - HANDLE hItem, hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); - gg_netlog(gg, "gg_gc_getchat(): Opening new conference for %d contacts.", count); - while (hContact && i < count) - { - hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0); - if (hItem && SendMessage(hwndList, CLM_GETCHECKMARK, (WPARAM)hItem, 0)) - { - HANDLE hMetaContact = gg_getsubcontact(gg, hContact); // MetaContacts support - participants[i++] = DBGetContactSettingDword(hMetaContact ? hMetaContact : hContact, GG_PROTO, GG_KEY_UIN, 0); - } - hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); - } - if (count > i) i = count; - chat = gg_gc_getchat(gg, 0, participants, count); - if (chat) - { - GCDEST gcdest = {GG_PROTO, chat, GC_EVENT_CONTROL}; - GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; - CallServiceSync(MS_GC_EVENT, WINDOW_VISIBLE, (LPARAM)&gcevent); - } - free(participants); - } - } - - case IDCANCEL: - DestroyWindow(hwndDlg); - break; - } - break; - } - - case WM_NOTIFY: - { - switch(((NMHDR*)lParam)->idFrom) - { - case IDC_CLIST: - { - switch(((NMHDR*)lParam)->code) - { - case CLN_OPTIONSCHANGED: - gg_gc_resetclistopts(GetDlgItem(hwndDlg, IDC_CLIST)); - break; - - case CLN_NEWCONTACT: - case CLN_CONTACTMOVED: - case CLN_LISTREBUILT: - { - HANDLE hContact; - HANDLE hItem; - char* szProto; - uin_t uin; - GGPROTO* gg = (GGPROTO*)GetWindowLongPtr(hwndDlg, DWLP_USER); - - if (!gg) break; - - // Delete non-gg contacts - hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); - while (hContact) - { - hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_FINDCONTACT, (WPARAM)hContact, 0); - if (hItem) - { - HANDLE hMetaContact = gg_getsubcontact(gg, hContact); // MetaContacts support - if (hMetaContact) - { - szProto = GG_PROTO; - uin = (uin_t)DBGetContactSettingDword(hMetaContact, GG_PROTO, GG_KEY_UIN, 0); - } - else - { - szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); - uin = (uin_t)DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0); - } - - if (szProto == NULL || lstrcmp(szProto, GG_PROTO) || !uin || uin == DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0)) - SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_DELETEITEM, (WPARAM)hItem, 0); - } - hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); - } - } - break; - - case CLN_CHECKCHANGED: - EnableWindow(GetDlgItem(hwndDlg, IDOK), gg_gc_countcheckmarks(GetDlgItem(hwndDlg, IDC_CLIST)) >= 2); - break; - } - break; - } - } - break; - } - - case HM_SUBCONTACTSCHANGED: - { - HWND hwndList = GetDlgItem(hwndDlg, IDC_CLIST); - SendMessage(hwndList, CLM_AUTOREBUILD, 0, 0); - EnableWindow(GetDlgItem(hwndDlg, IDOK), gg_gc_countcheckmarks(hwndList) >= 2); - break; - } - - case WM_CLOSE: - DestroyWindow(hwndDlg); - break; - - case WM_DESTROY: - { - HANDLE hMetaContactsEvent = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - if (hMetaContactsEvent) UnhookEvent(hMetaContactsEvent); - WindowFreeIcon(hwndDlg); - break; - } - } - - return FALSE; -} - -INT_PTR gg_gc_clearignored(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - list_t l = gg->chats; BOOL cleared = FALSE; - while(l) - { - GGGC *chat = (GGGC *)l->data; - l = l->next; - if(chat->ignore) - { - if(chat->recipients) free(chat->recipients); - list_remove(&gg->chats, chat, 1); - cleared = TRUE; - } - } - MessageBox( - NULL, - cleared ? - Translate("All ignored conferences are now unignored and the conference policy will act again.") : - Translate("There are no ignored conferences."), - GG_PROTONAME, - MB_OK | MB_ICONINFORMATION - ); - - return 0; -} - -INT_PTR gg_gc_openconf(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - // Check if connected - if (!gg_isonline(gg)) - { - MessageBox(NULL, - Translate("You have to be connected to open new conference."), - GG_PROTONAME, MB_OK | MB_ICONSTOP - ); - return 0; - } - - CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_CONFERENCE), NULL, gg_gc_openconfdlg, (LPARAM)gg); - return 1; -} - -int gg_gc_changenick(GGPROTO *gg, HANDLE hContact, char *pszNick) -{ - list_t l; - uin_t uin = DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0); - if (!uin || !pszNick) return 0; - - gg_netlog(gg, "gg_gc_changenick(): Nickname for uin %d changed to %s.", uin, pszNick); - // Lookup for chats having this nick - for(l = gg->chats; l; l = l->next) - { - int i; - GGGC *chat = (GGGC *)l->data; - if(chat->recipients && chat->recipients_count) - for(i = 0; i < chat->recipients_count; i++) - // Rename this window if it's exising in the chat - if(chat->recipients[i] == uin) - { - char id[32]; - GCEVENT gce = {sizeof(GCEVENT)}; - GCDEST gcd; - - UIN2ID(uin, id); - gcd.iType = GC_EVENT_NICK; - gcd.pszModule = GG_PROTO; - gce.pDest = &gcd; - gcd.pszID = chat->id; - gce.pszUID = id; - gce.pszText = pszNick; - gg_netlog(gg, "gg_gc_changenick(): Found room %s with uin %d, sending nick change %s.", chat->id, uin, id); - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); - - break; - } - } - - return 1; -} diff --git a/protocols/Gadu-Gadu/groupchat.cpp b/protocols/Gadu-Gadu/groupchat.cpp new file mode 100644 index 0000000000..31fe5d37e8 --- /dev/null +++ b/protocols/Gadu-Gadu/groupchat.cpp @@ -0,0 +1,669 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 Adam Strzelecki +// +// 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 "gg.h" +#include "m_metacontacts.h" + +#define GG_GC_GETCHAT "%s/GCGetChat" +#define GGS_OPEN_CONF "%s/OpenConf" +#define GGS_CLEAR_IGNORED "%s/ClearIgnored" + +//////////////////////////////////////////////////////////////////////////////// +// Inits Gadu-Gadu groupchat module using chat.dll + +int GGPROTO::gc_init() +{ + if (ServiceExists(MS_GC_REGISTER)) + { + char service[64]; + GCREGISTER gcr = {0}; + + // Register Gadu-Gadu proto + gcr.cbSize = sizeof(GCREGISTER); + gcr.dwFlags = GC_TCHAR; + gcr.iMaxText = 0; + gcr.nColors = 0; + gcr.pColors = 0; + gcr.ptszModuleDispName = m_tszUserName; + gcr.pszModule = m_szModuleName; + CallServiceSync(MS_GC_REGISTER, 0, (LPARAM)&gcr); + hookProtoEvent(ME_GC_EVENT, &GGPROTO::gc_event); + gc_enabled = TRUE; + // create & hook event + mir_snprintf(service, 64, GG_GC_GETCHAT, m_szModuleName); + netlog("gg_gc_init(): Registered with groupchat plugin."); + } + else + netlog("gg_gc_init(): Cannot register with groupchat plugin !!!"); + + return 1; +} + +//////////////////////////////////////////////////////////////////////////////// +// Groupchat menus initialization + +void GGPROTO::gc_menus_init(HGENMENU hRoot) +{ + if (gc_enabled) + { + CLISTMENUITEM mi = {0}; + char service[64]; + + mi.cbSize = sizeof(mi); + mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE; + mi.hParentMenu = hRoot; + + // Conferencing + mir_snprintf(service, sizeof(service), GGS_OPEN_CONF, m_szModuleName); + createProtoService(service, &GGPROTO::gc_openconf); + mi.position = 2000050001; + mi.icolibItem = GetIconHandle(IDI_CONFERENCE); + mi.pszName = LPGEN("Open &conference..."); + mi.pszService = service; + hMainMenu[0] = Menu_AddProtoMenuItem(&mi); + + // Clear ignored conferences + mir_snprintf(service, sizeof(service), GGS_CLEAR_IGNORED, m_szModuleName); + createProtoService(service, &GGPROTO::gc_clearignored); + mi.position = 2000050002; + mi.icolibItem = GetIconHandle(IDI_CLEAR_CONFERENCE); + mi.pszName = LPGEN("&Clear ignored conferences"); + mi.pszService = service; + hMainMenu[1] = Menu_AddProtoMenuItem(&mi); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Releases Gadu-Gadu groupchat module using chat.dll + +int GGPROTO::gc_destroy() +{ + list_t l; + for(l = chats; l; l = l->next) + { + GGGC *chat = (GGGC *)l->data; + if (chat->recipients) free(chat->recipients); + } + list_destroy(chats, 1); chats = NULL; + return 1; +} + +GGGC* GGPROTO::gc_lookup(char *id) +{ + GGGC *chat; + list_t l; + + for(l = chats; l; l = l->next) + { + chat = (GGGC *)l->data; + if (chat && !strcmp(chat->id, id)) + return chat; + } + + return NULL; +} + +int GGPROTO::gc_event(WPARAM wParam, LPARAM lParam) +{ + GCHOOK *gch = (GCHOOK *)lParam; + GGGC *chat = NULL; + uin_t uin; + + // Check if we got our protocol, and fields are set + if (!gch + || !gch->pDest + || !gch->pDest->pszID + || !gch->pDest->pszModule + || lstrcmpiA(gch->pDest->pszModule, m_szModuleName) + || !(uin = db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0)) + || !(chat = gc_lookup(gch->pDest->pszID))) + return 0; + + // Window terminated + if (gch->pDest->iType == SESSION_TERMINATE) + { + HANDLE hContact = NULL; + netlog("gg_gc_event(): Terminating chat %x, id %s from chat window...", chat, gch->pDest->pszID); + // Destroy chat entry + free(chat->recipients); + list_remove(&chats, chat, 1); + // Remove contact from contact list (duh!) should be done by chat.dll !! + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while (hContact) + { + DBVARIANT dbv; + if (!db_get_s(hContact, m_szModuleName, "ChatRoomID", &dbv, DBVT_ASCIIZ)) + { + if (dbv.pszVal && !strcmp(gch->pDest->pszID, dbv.pszVal)) + CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0); + DBFreeVariant(&dbv); + } + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); + } + return 1; + } + + // Message typed / send only if online + if (isonline() && (gch->pDest->iType == GC_USER_MESSAGE) && gch->pszText) + { + char id[32]; + DBVARIANT dbv; + GCDEST gcdest = {m_szModuleName, gch->pDest->pszID, GC_EVENT_MESSAGE}; + GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; + int lc; + + UIN2ID(uin, id); + + gcevent.pszUID = id; + gcevent.pszText = gch->pszText; + if (!db_get_s(NULL, m_szModuleName, GG_KEY_NICK, &dbv, DBVT_ASCIIZ)) + gcevent.pszNick = dbv.pszVal; + else + gcevent.pszNick = Translate("Me"); + + // Get rid of CRLF at back + lc = (int)strlen(gch->pszText) - 1; + while(lc >= 0 && (gch->pszText[lc] == '\n' || gch->pszText[lc] == '\r')) gch->pszText[lc --] = 0; + gcevent.time = time(NULL); + gcevent.bIsMe = 1; + gcevent.dwFlags = GCEF_ADDTOLOG; + netlog("gg_gc_event(): Sending conference message to room %s, \"%s\".", gch->pDest->pszID, gch->pszText); + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); + if (gcevent.pszNick == dbv.pszVal) DBFreeVariant(&dbv); + EnterCriticalSection(&sess_mutex); + gg_send_message_confer(sess, GG_CLASS_CHAT, chat->recipients_count, chat->recipients, (BYTE*)gch->pszText); + LeaveCriticalSection(&sess_mutex); + return 1; + } + + // Privmessage selected + if (gch->pDest->iType == GC_USER_PRIVMESS) + { + HANDLE hContact = NULL; + if ((uin = atoi(gch->pszUID)) && (hContact = getcontact(uin, 1, 0, NULL))) + CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, (LPARAM)0); + } + netlog("gg_gc_event(): Unhandled event %d, chat %x, uin %d, text \"%s\".", gch->pDest->iType, chat, uin, gch->pszText); + + return 0; +} + +typedef struct _gg_gc_echat +{ + uin_t sender; + uin_t *recipients; + int recipients_count; + char * chat_id; +} gg_gc_echat; + +//////////////////////////////////////////////////////////////////////////////// +// This is main groupchat initialization routine + +char* GGPROTO::gc_getchat(uin_t sender, uin_t *recipients, int recipients_count) +{ + list_t l; int i; + GGGC *chat; + char id[32]; + uin_t uin; DBVARIANT dbv; + GCDEST gcdest = {m_szModuleName, 0, GC_EVENT_ADDGROUP}; + GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; + + netlog("gg_gc_getchat(): Count %d.", recipients_count); + if (!recipients) return NULL; + + // Look for existing chat + for(l = chats; l; l = l->next) + { + GGGC *chat = (GGGC *)l->data; + if (!chat) continue; + + if (chat->recipients_count == recipients_count + (sender ? 1 : 0)) + { + int i, j, found = 0, sok = (sender == 0); + if (!sok) for(i = 0; i < chat->recipients_count; i++) + if (sender == chat->recipients[i]) + { + sok = 1; + break; + } + if (sok) + for(i = 0; i < chat->recipients_count; i++) + for(j = 0; j < recipients_count; j++) + if (recipients[j] == chat->recipients[i]) found++; + // Found all recipients + if (found == recipients_count) + { + if (chat->ignore) + netlog("gg_gc_getchat(): Ignoring existing id %s, size %d.", chat->id, chat->recipients_count); + else + netlog("gg_gc_getchat(): Returning existing id %s, size %d.", chat->id, chat->recipients_count); + return !(chat->ignore) ? chat->id : NULL; + } + } + } + + // Make new uin list to chat mapping + chat = (GGGC *)malloc(sizeof(GGGC)); + UIN2ID(gc_id ++, chat->id); chat->ignore = FALSE; + + // Check groupchat policy (new) / only for incoming + if (sender) + { + int unknown = (getcontact(sender, 0, 0, NULL) == NULL), + unknownSender = unknown; + for(i = 0; i < recipients_count; i++) + if (!getcontact(recipients[i], 0, 0, NULL)) + unknown ++; + if ((db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_DEFAULT, GG_KEYDEF_GC_POLICY_DEFAULT) == 2) || + (db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL) == 2 && + recipients_count >= db_get_w(NULL, m_szModuleName, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) || + (db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN) == 2 && + unknown >= db_get_w(NULL, m_szModuleName, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN))) + chat->ignore = TRUE; + if (!chat->ignore && ((db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_DEFAULT, GG_KEYDEF_GC_POLICY_DEFAULT) == 1) || + (db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL) == 1 && + recipients_count >= db_get_w(NULL, m_szModuleName, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) || + (db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN) == 1 && + unknown >= db_get_w(NULL, m_szModuleName, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN)))) + { + TCHAR *senderName = unknownSender ? + TranslateT("Unknown") : pcli->pfnGetContactDisplayName(getcontact(sender, 0, 0, NULL), 0); + TCHAR error[256]; + mir_sntprintf(error, SIZEOF(error), TranslateT("%s has initiated conference with %d participants (%d unknowns).\nDo you want do participate ?"), + senderName, recipients_count + 1, unknown); + chat->ignore = MessageBox(NULL, error, m_tszUserName, MB_OKCANCEL | MB_ICONEXCLAMATION) != IDOK; + } + if (chat->ignore) + { + // Copy recipient list + chat->recipients_count = recipients_count + (sender ? 1 : 0); + chat->recipients = (uin_t *)calloc(chat->recipients_count, sizeof(uin_t)); + for(i = 0; i < recipients_count; i++) + chat->recipients[i] = recipients[i]; + if (sender) chat->recipients[i] = sender; + netlog("gg_gc_getchat(): Ignoring new chat %s, count %d.", chat->id, chat->recipients_count); + list_add(&chats, chat, 0); + return NULL; + } + } + + // Create new chat window + TCHAR status[256]; + TCHAR *senderName = sender ? pcli->pfnGetContactDisplayName(getcontact(sender, 1, 0, NULL), 0) : NULL; + mir_sntprintf(status, 255, (sender) ? TranslateT("%s initiated the conference.") : TranslateT("This is my own conference."), senderName); + GCSESSION gcwindow = { 0 }; + gcwindow.cbSize = sizeof(GCSESSION); + gcwindow.iType = GCW_CHATROOM; + gcwindow.pszModule = m_szModuleName; + gcwindow.ptszName = sender ? senderName : TranslateT("Conference"); + gcwindow.pszID = chat->id; + gcwindow.dwFlags = GC_TCHAR; + gcwindow.dwItemData = (DWORD)chat; + gcwindow.ptszStatusbarText = status; + + // Here we put nice new hash sign + TCHAR *name = (TCHAR*)calloc(_tcslen(gcwindow.ptszName) + 2, sizeof(TCHAR)); + *name = '#'; _tcscpy(name + 1, gcwindow.ptszName); + gcwindow.ptszName = name; + // Create new room + if (CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM) &gcwindow)) + { + netlog("gg_gc_getchat(): Cannot create new chat window %s.", chat->id); + free(name); + free(chat); + return NULL; + } + free(name); + + gcdest.pszID = chat->id; + gcevent.pszUID = id; + gcevent.dwFlags = GC_TCHAR | GCEF_ADDTOLOG; + gcevent.time = 0; + + // Add normal group + gcevent.ptszStatus = TranslateT("Participants"); + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); + gcdest.iType = GC_EVENT_JOIN; + + // Add myself + if (uin = db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0)) + { + UIN2ID(uin, id); + if (!db_get_s(NULL, m_szModuleName, GG_KEY_NICK, &dbv, DBVT_TCHAR)) { + gcevent.ptszNick = NEWTSTR_ALLOCA(dbv.ptszVal); + db_free(&dbv); + } + else gcevent.ptszNick = TranslateT("Me"); + gcevent.bIsMe = 1; + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); + netlog("gg_gc_getchat(): Myself %s: %S (%S) to the list...", gcevent.pszUID, gcevent.ptszNick, gcevent.ptszStatus); + } + else netlog("gg_gc_getchat(): Myself adding failed with uin %d !!!", uin); + + // Copy recipient list + chat->recipients_count = recipients_count + (sender ? 1 : 0); + chat->recipients = (uin_t *)calloc(chat->recipients_count, sizeof(uin_t)); + for(i = 0; i < recipients_count; i++) + chat->recipients[i] = recipients[i]; + if (sender) chat->recipients[i] = sender; + + // Add contacts + for(i = 0; i < chat->recipients_count; i++) { + HANDLE hContact = getcontact(chat->recipients[i], 1, 0, NULL); + UIN2ID(chat->recipients[i], id); + if (hContact && (name = pcli->pfnGetContactDisplayName(hContact, 0)) != NULL) + gcevent.ptszNick = name; + else + gcevent.ptszNick = TranslateT("'Unknown'"); + gcevent.bIsMe = 0; + gcevent.dwFlags = GC_TCHAR; + netlog("gg_gc_getchat(): Added %s: %S (%S) to the list...", gcevent.pszUID, gcevent.ptszNick, gcevent.pszStatus); + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); + } + gcdest.iType = GC_EVENT_CONTROL; + CallServiceSync(MS_GC_EVENT, SESSION_INITDONE, (LPARAM)&gcevent); + CallServiceSync(MS_GC_EVENT, SESSION_ONLINE, (LPARAM)&gcevent); + + netlog("gg_gc_getchat(): Returning new chat window %s, count %d.", chat->id, chat->recipients_count); + list_add(&chats, chat, 0); + return chat->id; +} + +static HANDLE gg_getsubcontact(GGPROTO* gg, HANDLE hContact) +{ + char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + char* szMetaProto = (char*)CallService(MS_MC_GETPROTOCOLNAME, 0, 0); + + if (szProto && szMetaProto && (INT_PTR)szMetaProto != CALLSERVICE_NOTFOUND && !lstrcmpA(szProto, szMetaProto)) + { + int nSubContacts = (int)CallService(MS_MC_GETNUMCONTACTS, (WPARAM)hContact, 0), i; + HANDLE hMetaContact; + for (i = 0; i < nSubContacts; i++) + { + hMetaContact = (HANDLE)CallService(MS_MC_GETSUBCONTACT, (WPARAM)hContact, i); + szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hMetaContact, 0); + if (szProto && !lstrcmpA(szProto, gg->m_szModuleName)) + return hMetaContact; + } + } + return NULL; +} + +static void gg_gc_resetclistopts(HWND hwndList) +{ + int i; + SendMessage(hwndList, CLM_SETLEFTMARGIN, 2, 0); + 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_SETINDENT, 10, 0); + SendMessage(hwndList, CLM_SETHIDEEMPTYGROUPS, (WPARAM)TRUE, 0); + for (i = 0; i <= FONTID_MAX; i++) + SendMessage(hwndList, CLM_SETTEXTCOLOR, i, GetSysColor(COLOR_WINDOWTEXT)); +} + +static int gg_gc_countcheckmarks(HWND hwndList) +{ + int count = 0; + HANDLE hItem, hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while (hContact) + { + hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0); + if (hItem && SendMessage(hwndList, CLM_GETCHECKMARK, (WPARAM)hItem, 0)) + count++; + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); + } + return count; +} + +#define HM_SUBCONTACTSCHANGED (WM_USER + 100) + +static INT_PTR CALLBACK gg_gc_openconfdlg(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) + { + case WM_INITDIALOG: + { + CLCINFOITEM cii = {0}; + HANDLE hMetaContactsEvent; + + SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)lParam); + TranslateDialogDefault(hwndDlg); + WindowSetIcon(hwndDlg, "conference"); + gg_gc_resetclistopts(GetDlgItem(hwndDlg, IDC_CLIST)); + + // Hook MetaContacts event (if available) + hMetaContactsEvent = HookEventMessage(ME_MC_SUBCONTACTSCHANGED, hwndDlg, HM_SUBCONTACTSCHANGED); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)hMetaContactsEvent); + } + return TRUE; + + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDOK: + { + HWND hwndList = GetDlgItem(hwndDlg, IDC_CLIST); + GGPROTO* gg = (GGPROTO*)GetWindowLongPtr(hwndDlg, DWLP_USER); + int count = 0, i = 0; + // Check if connected + if (!gg->isonline()) + { + MessageBox(NULL, + TranslateT("You have to be connected to open new conference."), + gg->m_tszUserName, MB_OK | MB_ICONSTOP); + } + else if (hwndList && (count = gg_gc_countcheckmarks(hwndList)) >= 2) + { + // Create new participiants table + char* chat; + uin_t* participants = (uin_t*)calloc(count, sizeof(uin_t)); + HANDLE hItem, hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + gg->netlog("gg_gc_getchat(): Opening new conference for %d contacts.", count); + while (hContact && i < count) + { + hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0); + if (hItem && SendMessage(hwndList, CLM_GETCHECKMARK, (WPARAM)hItem, 0)) + { + HANDLE hMetaContact = gg_getsubcontact(gg, hContact); // MetaContacts support + participants[i++] = db_get_b(hMetaContact ? hMetaContact : hContact, gg->m_szModuleName, GG_KEY_UIN, 0); + } + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); + } + if (count > i) i = count; + chat = gg->gc_getchat(0, participants, count); + if (chat) + { + GCDEST gcdest = {gg->m_szModuleName, chat, GC_EVENT_CONTROL}; + GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; + CallServiceSync(MS_GC_EVENT, WINDOW_VISIBLE, (LPARAM)&gcevent); + } + free(participants); + } + } + + case IDCANCEL: + DestroyWindow(hwndDlg); + break; + } + break; + } + + case WM_NOTIFY: + { + switch(((NMHDR*)lParam)->idFrom) + { + case IDC_CLIST: + { + switch(((NMHDR*)lParam)->code) + { + case CLN_OPTIONSCHANGED: + gg_gc_resetclistopts(GetDlgItem(hwndDlg, IDC_CLIST)); + break; + + case CLN_NEWCONTACT: + case CLN_CONTACTMOVED: + case CLN_LISTREBUILT: + { + HANDLE hContact; + HANDLE hItem; + char* szProto; + uin_t uin; + GGPROTO* gg = (GGPROTO*)GetWindowLongPtr(hwndDlg, DWLP_USER); + + if (!gg) break; + + // Delete non-gg contacts + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while (hContact) + { + hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_FINDCONTACT, (WPARAM)hContact, 0); + if (hItem) + { + HANDLE hMetaContact = gg_getsubcontact(gg, hContact); // MetaContacts support + if (hMetaContact) + { + szProto = gg->m_szModuleName; + uin = (uin_t)db_get_b(hMetaContact, gg->m_szModuleName, GG_KEY_UIN, 0); + } + else + { + szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + uin = (uin_t)db_get_b(hContact, gg->m_szModuleName, GG_KEY_UIN, 0); + } + + if (szProto == NULL || lstrcmpA(szProto, gg->m_szModuleName) || !uin || uin == db_get_b(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) + SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_DELETEITEM, (WPARAM)hItem, 0); + } + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); + } + } + break; + + case CLN_CHECKCHANGED: + EnableWindow(GetDlgItem(hwndDlg, IDOK), gg_gc_countcheckmarks(GetDlgItem(hwndDlg, IDC_CLIST)) >= 2); + break; + } + break; + } + } + break; + } + + case HM_SUBCONTACTSCHANGED: + { + HWND hwndList = GetDlgItem(hwndDlg, IDC_CLIST); + SendMessage(hwndList, CLM_AUTOREBUILD, 0, 0); + EnableWindow(GetDlgItem(hwndDlg, IDOK), gg_gc_countcheckmarks(hwndList) >= 2); + break; + } + + case WM_CLOSE: + DestroyWindow(hwndDlg); + break; + + case WM_DESTROY: + { + HANDLE hMetaContactsEvent = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + if (hMetaContactsEvent) UnhookEvent(hMetaContactsEvent); + WindowFreeIcon(hwndDlg); + break; + } + } + + return FALSE; +} + +INT_PTR GGPROTO::gc_clearignored(WPARAM wParam, LPARAM lParam) +{ + list_t l = chats; BOOL cleared = FALSE; + while(l) + { + GGGC *chat = (GGGC *)l->data; + l = l->next; + if (chat->ignore) + { + if (chat->recipients) free(chat->recipients); + list_remove(&chats, chat, 1); + cleared = TRUE; + } + } + MessageBox( NULL, + cleared ? + TranslateT("All ignored conferences are now unignored and the conference policy will act again.") : + TranslateT("There are no ignored conferences."), + m_tszUserName, MB_OK | MB_ICONINFORMATION + ); + + return 0; +} + +INT_PTR GGPROTO::gc_openconf(WPARAM wParam, LPARAM lParam) +{ + // Check if connected + if (!isonline()) + { + MessageBox(NULL, + TranslateT("You have to be connected to open new conference."), + m_tszUserName, MB_OK | MB_ICONSTOP + ); + return 0; + } + + CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_CONFERENCE), NULL, gg_gc_openconfdlg, (LPARAM)this); + return 1; +} + +int GGPROTO::gc_changenick(HANDLE hContact, char *pszNick) +{ + list_t l; + uin_t uin = db_get_b(hContact, m_szModuleName, GG_KEY_UIN, 0); + if (!uin || !pszNick) return 0; + + netlog("gg_gc_changenick(): Nickname for uin %d changed to %s.", uin, pszNick); + // Lookup for chats having this nick + for(l = chats; l; l = l->next) { + GGGC *chat = (GGGC *)l->data; + if (chat->recipients && chat->recipients_count) + for(int i = 0; i < chat->recipients_count; i++) + // Rename this window if it's exising in the chat + if (chat->recipients[i] == uin) + { + char id[32]; + GCEVENT gce = {sizeof(GCEVENT)}; + GCDEST gcd; + + UIN2ID(uin, id); + gcd.iType = GC_EVENT_NICK; + gcd.pszModule = m_szModuleName; + gce.pDest = &gcd; + gcd.pszID = chat->id; + gce.pszUID = id; + gce.pszText = pszNick; + netlog("gg_gc_changenick(): Found room %s with uin %d, sending nick change %s.", chat->id, uin, id); + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); + + break; + } + } + + return 1; +} diff --git a/protocols/Gadu-Gadu/icolib.c b/protocols/Gadu-Gadu/icolib.c deleted file mode 100644 index 58d5c2fe2a..0000000000 --- a/protocols/Gadu-Gadu/icolib.c +++ /dev/null @@ -1,109 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2007 Adam Strzelecki -// -// 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 "gg.h" - -struct tagiconList -{ - const char* szDescr; - const char* szName; - int defIconID; -} -static const iconList[] = -{ - { LPGEN("Protocol icon"), "main", IDI_GG }, - { LPGEN("Import list from server"), "importserver", IDI_IMPORT_SERVER }, - { LPGEN("Import list from text file"), "importtext", IDI_IMPORT_TEXT }, - { LPGEN("Remove list from server"), "removeserver", IDI_REMOVE_SERVER }, - { LPGEN("Export list to server"), "exportserver", IDI_EXPORT_SERVER }, - { LPGEN("Export list to text file"), "exporttext", IDI_EXPORT_TEXT }, - { LPGEN("Account settings"), "settings", IDI_SETTINGS }, - { LPGEN("Contact list"), "list", IDI_LIST }, - { LPGEN("Block user"), "block", IDI_BLOCK }, - { LPGEN("Previous image"), "previous", IDI_PREV }, - { LPGEN("Next image"), "next", IDI_NEXT }, - { LPGEN("Send image"), "image", IDI_IMAGE }, - { LPGEN("Save image"), "save", IDI_SAVE }, - { LPGEN("Delete image"), "delete", IDI_DELETE }, - { LPGEN("Open new conference"), "conference", IDI_CONFERENCE }, - { LPGEN("Clear ignored conferences"), "clearignored", IDI_CLEAR_CONFERENCE }, - { LPGEN("Concurrent sessions"), "sessions", IDI_SESSIONS } -}; - -HANDLE hIconLibItem[SIZEOF(iconList)]; - -void gg_icolib_init() -{ - SKINICONDESC sid = {0}; - char szFile[MAX_PATH]; - char szSectionName[100]; - int i; - - mir_snprintf(szSectionName, sizeof( szSectionName ), "%s/%s", LPGEN("Protocols"), LPGEN(GGDEF_PROTO)); - GetModuleFileNameA(hInstance, szFile, MAX_PATH); - - sid.cbSize = sizeof(SKINICONDESC); - sid.pszDefaultFile = szFile; - sid.pszSection = szSectionName; - - for(i = 0; i < SIZEOF(iconList); i++) { - char szSettingName[100]; - mir_snprintf(szSettingName, sizeof(szSettingName), "%s_%s", GGDEF_PROTO, iconList[i].szName); - sid.pszName = szSettingName; - sid.pszDescription = (char*)iconList[i].szDescr; - sid.iDefaultIndex = -iconList[i].defIconID; - hIconLibItem[i] = Skin_AddIcon(&sid); - } -} - -HICON LoadIconEx(const char* name, BOOL big) -{ - char szSettingName[100]; - mir_snprintf(szSettingName, sizeof(szSettingName), "%s_%s", GGDEF_PROTO, name); - return (HICON)CallService(MS_SKIN2_GETICON, big, (LPARAM)szSettingName); -} - -HANDLE GetIconHandle(int iconId) -{ - int i; - for(i = 0; i < SIZEOF(iconList); i++) - if (iconList[i].defIconID == iconId) - return hIconLibItem[i]; - return NULL; -} - -void ReleaseIconEx(const char* name, BOOL big) -{ - char szSettingName[100]; - mir_snprintf(szSettingName, sizeof(szSettingName), "%s_%s", GGDEF_PROTO, name); - CallService(big ? MS_SKIN2_RELEASEICONBIG : MS_SKIN2_RELEASEICON, 0, (LPARAM)szSettingName); -} - -void WindowSetIcon(HWND hWnd, const char* name) -{ - SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)LoadIconEx(name, TRUE)); - SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadIconEx(name, FALSE)); -} - -void WindowFreeIcon(HWND hWnd) -{ - CallService(MS_SKIN2_RELEASEICONBIG, SendMessage(hWnd, WM_SETICON, ICON_BIG, 0), 0); - CallService(MS_SKIN2_RELEASEICON, SendMessage(hWnd, WM_SETICON, ICON_SMALL, 0), 0); -} diff --git a/protocols/Gadu-Gadu/icolib.cpp b/protocols/Gadu-Gadu/icolib.cpp new file mode 100644 index 0000000000..58d5c2fe2a --- /dev/null +++ b/protocols/Gadu-Gadu/icolib.cpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2007 Adam Strzelecki +// +// 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 "gg.h" + +struct tagiconList +{ + const char* szDescr; + const char* szName; + int defIconID; +} +static const iconList[] = +{ + { LPGEN("Protocol icon"), "main", IDI_GG }, + { LPGEN("Import list from server"), "importserver", IDI_IMPORT_SERVER }, + { LPGEN("Import list from text file"), "importtext", IDI_IMPORT_TEXT }, + { LPGEN("Remove list from server"), "removeserver", IDI_REMOVE_SERVER }, + { LPGEN("Export list to server"), "exportserver", IDI_EXPORT_SERVER }, + { LPGEN("Export list to text file"), "exporttext", IDI_EXPORT_TEXT }, + { LPGEN("Account settings"), "settings", IDI_SETTINGS }, + { LPGEN("Contact list"), "list", IDI_LIST }, + { LPGEN("Block user"), "block", IDI_BLOCK }, + { LPGEN("Previous image"), "previous", IDI_PREV }, + { LPGEN("Next image"), "next", IDI_NEXT }, + { LPGEN("Send image"), "image", IDI_IMAGE }, + { LPGEN("Save image"), "save", IDI_SAVE }, + { LPGEN("Delete image"), "delete", IDI_DELETE }, + { LPGEN("Open new conference"), "conference", IDI_CONFERENCE }, + { LPGEN("Clear ignored conferences"), "clearignored", IDI_CLEAR_CONFERENCE }, + { LPGEN("Concurrent sessions"), "sessions", IDI_SESSIONS } +}; + +HANDLE hIconLibItem[SIZEOF(iconList)]; + +void gg_icolib_init() +{ + SKINICONDESC sid = {0}; + char szFile[MAX_PATH]; + char szSectionName[100]; + int i; + + mir_snprintf(szSectionName, sizeof( szSectionName ), "%s/%s", LPGEN("Protocols"), LPGEN(GGDEF_PROTO)); + GetModuleFileNameA(hInstance, szFile, MAX_PATH); + + sid.cbSize = sizeof(SKINICONDESC); + sid.pszDefaultFile = szFile; + sid.pszSection = szSectionName; + + for(i = 0; i < SIZEOF(iconList); i++) { + char szSettingName[100]; + mir_snprintf(szSettingName, sizeof(szSettingName), "%s_%s", GGDEF_PROTO, iconList[i].szName); + sid.pszName = szSettingName; + sid.pszDescription = (char*)iconList[i].szDescr; + sid.iDefaultIndex = -iconList[i].defIconID; + hIconLibItem[i] = Skin_AddIcon(&sid); + } +} + +HICON LoadIconEx(const char* name, BOOL big) +{ + char szSettingName[100]; + mir_snprintf(szSettingName, sizeof(szSettingName), "%s_%s", GGDEF_PROTO, name); + return (HICON)CallService(MS_SKIN2_GETICON, big, (LPARAM)szSettingName); +} + +HANDLE GetIconHandle(int iconId) +{ + int i; + for(i = 0; i < SIZEOF(iconList); i++) + if (iconList[i].defIconID == iconId) + return hIconLibItem[i]; + return NULL; +} + +void ReleaseIconEx(const char* name, BOOL big) +{ + char szSettingName[100]; + mir_snprintf(szSettingName, sizeof(szSettingName), "%s_%s", GGDEF_PROTO, name); + CallService(big ? MS_SKIN2_RELEASEICONBIG : MS_SKIN2_RELEASEICON, 0, (LPARAM)szSettingName); +} + +void WindowSetIcon(HWND hWnd, const char* name) +{ + SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)LoadIconEx(name, TRUE)); + SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadIconEx(name, FALSE)); +} + +void WindowFreeIcon(HWND hWnd) +{ + CallService(MS_SKIN2_RELEASEICONBIG, SendMessage(hWnd, WM_SETICON, ICON_BIG, 0), 0); + CallService(MS_SKIN2_RELEASEICON, SendMessage(hWnd, WM_SETICON, ICON_SMALL, 0), 0); +} diff --git a/protocols/Gadu-Gadu/image.c b/protocols/Gadu-Gadu/image.c deleted file mode 100644 index a602bf02d8..0000000000 --- a/protocols/Gadu-Gadu/image.c +++ /dev/null @@ -1,1186 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2009 Adam Strzelecki -// Copyright (c) 2009-2012 Bartosz Białek -// -// 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 "gg.h" -#include - -#define WM_ADDIMAGE WM_USER + 1 -#define WM_SENDIMG WM_USER + 2 -#define WM_CHOOSEIMG WM_USER + 3 -#define TIMERID_FLASHWND WM_USER + 4 - -//////////////////////////////////////////////////////////////////////////// -// Image Window : Data - -// tablica zawiera uin kolesia i uchwyt do okna, okno zawiera GGIMAGEDLGDATA -// ktore przechowuje handle kontaktu, i wskaznik na pierwszy obrazek -// obrazki sa poukladane jako lista jednokierunkowa. -// przy tworzeniu okna podaje sie handle do kontaktu -// dodajac obrazek tworzy sie element listy i wysyla do okna -// wyswietlajac obrazek idzie po liscie jednokierunkowej - -typedef struct _GGIMAGEENTRY -{ - HBITMAP hBitmap; - char *lpszFileName; - char *lpData; - unsigned long nSize; - struct _GGIMAGEENTRY *lpNext; - uint32_t crc32; -} GGIMAGEENTRY; - -typedef struct -{ - HANDLE hContact; - HANDLE hEvent; - HWND hWnd; - uin_t uin; - int nImg, nImgTotal; - GGIMAGEENTRY *lpImages; - SIZE minSize; - BOOL bReceiving; - GGPROTO *gg; -} GGIMAGEDLGDATA; - -// Prototypes -int gg_img_remove(GGIMAGEDLGDATA *dat); -INT_PTR gg_img_sendimg(GGPROTO *gg, WPARAM wParam, LPARAM lParam); - -//////////////////////////////////////////////////////////////////////////// -// Image Module : Adding item to contact menu, creating sync objects -int gg_img_init(GGPROTO *gg) -{ - CLISTMENUITEM mi = {0}; - char service[64]; - - mi.cbSize = sizeof(mi); - mi.flags = CMIF_ICONFROMICOLIB; - - // Send image contact menu item - mir_snprintf(service, sizeof(service), GGS_SENDIMAGE, GG_PROTO); - CreateProtoServiceFunction(service, gg_img_sendimg, gg); - mi.position = -2000010000; - mi.icolibItem = GetIconHandle(IDI_IMAGE); - mi.pszName = LPGEN("&Image"); - mi.pszService = service; - mi.pszContactOwner = GG_PROTO; - gg->hImageMenuItem = Menu_AddContactMenuItem(&mi); - - // Receive image - mir_snprintf(service, sizeof(service), GGS_RECVIMAGE, GG_PROTO); - CreateProtoServiceFunction(service, gg_img_recvimage, gg); - - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////// -// Image Module : closing dialogs, sync objects -int gg_img_shutdown(GGPROTO *gg) -{ - list_t l; -#ifdef DEBUGMODE - gg_netlog(gg, "gg_img_shutdown(): Closing all dialogs..."); -#endif - // Rather destroy window instead of just removing structures - for (l = gg->imagedlgs; l;) - { - GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)l->data; - l = l->next; - - if (dat && dat->hWnd) - { - if (IsWindow(dat->hWnd)) - { - // Post message async, since it maybe be different thread - if (!PostMessage(dat->hWnd, WM_CLOSE, 0, 0)) - gg_netlog(gg, "gg_img_shutdown(): Image dlg %x cannot be released !!", dat->hWnd); - } - else - gg_netlog(gg, "gg_img_shutdown(): Image dlg %x not exists, but structure does !!", dat->hWnd); - } - } - - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////// -// Image Module : destroying list -int gg_img_destroy(GGPROTO *gg) -{ - // Release all dialogs - while (gg->imagedlgs && gg_img_remove((GGIMAGEDLGDATA *)gg->imagedlgs->data)); - - // Destroy list - list_destroy(gg->imagedlgs, 1); - CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)gg->hImageMenuItem, (LPARAM) 0); - - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////// -// Painting image -int gg_img_paint(HWND hwnd, GGIMAGEENTRY *dat) -{ - PAINTSTRUCT paintStruct; - HDC hdc = BeginPaint(hwnd, &paintStruct); - RECT rc; - - GetWindowRect(GetDlgItem(hwnd, IDC_IMG_IMAGE), &rc); - ScreenToClient(hwnd, (POINT *)&rc.left); - ScreenToClient(hwnd, (POINT *)&rc.right); - FillRect(hdc, &rc, (HBRUSH)GetSysColorBrush(COLOR_WINDOW)); - - if (dat->hBitmap) - { - HDC hdcBmp = NULL; - int nWidth, nHeight; - BITMAP bmp; - - GetObject(dat->hBitmap, sizeof(bmp), &bmp); - nWidth = bmp.bmWidth; nHeight = bmp.bmHeight; - - hdcBmp = CreateCompatibleDC(hdc); - SelectObject(hdcBmp, dat->hBitmap); - if (hdcBmp) - { - SetStretchBltMode(hdc, HALFTONE); - // Draw bitmap - if (nWidth > (rc.right-rc.left) || nHeight > (rc.bottom-rc.top)) - { - if ((double)nWidth / (double)nHeight > (double) (rc.right-rc.left) / (double)(rc.bottom-rc.top)) - { - StretchBlt(hdc, - rc.left, - ((rc.top + rc.bottom) - (rc.right - rc.left) * nHeight / nWidth) / 2, - (rc.right - rc.left), - (rc.right - rc.left) * nHeight / nWidth, - hdcBmp, 0, 0, nWidth, nHeight, SRCCOPY); - } - else - { - StretchBlt(hdc, - ((rc.left + rc.right) - (rc.bottom - rc.top) * nWidth / nHeight) / 2, - rc.top, - (rc.bottom - rc.top) * nWidth / nHeight, - (rc.bottom - rc.top), - hdcBmp, 0, 0, nWidth, nHeight, SRCCOPY); - } - } - else - { - BitBlt(hdc, - (rc.left + rc.right - nWidth) / 2, - (rc.top + rc.bottom - nHeight) / 2, - nWidth, nHeight, - hdcBmp, 0, 0, SRCCOPY); - } - DeleteDC(hdcBmp); - } - } - EndPaint(hwnd, &paintStruct); - - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////////// -// Returns supported image filters -char *gg_img_getfilter(char *szFilter, int nSize) -{ - char *szFilterName, *szFilterMask; - char *pFilter = szFilter; - - // Match relative to ImgDecoder presence - szFilterName = Translate("Image files (*.bmp,*.gif,*.jpeg,*.jpg,*.png)"); - szFilterMask = "*.bmp;*.gif;*.jpeg;*.jpg;*.png"; - - // Make up filter - strncpy(pFilter, szFilterName, nSize); - pFilter += strlen(pFilter) + 1; - if (pFilter >= szFilter + nSize) return NULL; - strncpy(pFilter, szFilterMask, nSize - (pFilter - szFilter)); - pFilter += strlen(pFilter) + 1; - if (pFilter >= szFilter + nSize) return NULL; - *pFilter = 0; - - return szFilter; -} - -//////////////////////////////////////////////////////////////////////////////// -// Save specified image entry -int gg_img_saveimage(HWND hwnd, GGIMAGEENTRY *dat) -{ - OPENFILENAME ofn = {0}; - char szFileName[MAX_PATH]; - char szFilter[128]; - - if (!dat) return FALSE; - - gg_img_getfilter(szFilter, sizeof(szFilter)); - strncpy(szFileName, dat->lpszFileName, sizeof(szFileName)); - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = hwnd; - ofn.hInstance = hInstance; - ofn.lpstrFile = szFileName; - ofn.lpstrFilter = szFilter; - ofn.nMaxFile = MAX_PATH; - ofn.Flags = OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT; - if (GetSaveFileName(&ofn)) - { - FILE *fp = fopen(szFileName, "w+b"); - if (fp) - { - fwrite(dat->lpData, dat->nSize, 1, fp); - fclose(fp); - gg_netlog(((GGIMAGEDLGDATA *)GetWindowLongPtr(hwnd, GWLP_USERDATA))->gg, "gg_img_saveimage(): Image saved to %s.", szFileName); - } - else - { - gg_netlog(((GGIMAGEDLGDATA *)GetWindowLongPtr(hwnd, GWLP_USERDATA))->gg, "gg_img_saveimage(): Cannot save image to %s.", szFileName); - MessageBox(hwnd, Translate("Image cannot be written to disk."), ((GGIMAGEDLGDATA *)GetWindowLongPtr(hwnd, GWLP_USERDATA))->gg->name, MB_OK | MB_ICONERROR); - } - } - - return 0; -} - -//////////////////////////////////////////////////////////////////////////// -// Fit window size to image size -BOOL gg_img_fit(HWND hwndDlg) -{ - GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - RECT dlgRect, imgRect, wrkRect; - int nWidth, nHeight; - int rWidth = 0, rHeight = 0; - int oWidth = 0, oHeight = 0; - BITMAP bmp; - GGIMAGEENTRY *img = NULL; - HDC hdc; - - // Check if image is loaded - if (!dat || !dat->lpImages || !dat->lpImages->hBitmap) - return FALSE; - - img = dat->lpImages; - - // Go to last image - while (img->lpNext && dat->lpImages->hBitmap) - img = img->lpNext; - - // Get rects of display - GetWindowRect(hwndDlg, &dlgRect); - GetClientRect(GetDlgItem(hwndDlg, IDC_IMG_IMAGE), &imgRect); - - hdc = GetDC(hwndDlg); - - GetObject(img->hBitmap, sizeof(bmp), &bmp); - nWidth = bmp.bmWidth; nHeight = bmp.bmHeight; - SystemParametersInfo(SPI_GETWORKAREA, 0, &wrkRect, 0); - - ReleaseDC(hwndDlg, hdc); - - if ((imgRect.right - imgRect.left) < nWidth) - rWidth = nWidth - imgRect.right + imgRect.left; - if ((imgRect.bottom - imgRect.top) < nWidth) - rHeight = nHeight - imgRect.bottom + imgRect.top; - - // Check if anything needs resize - if (!rWidth && !rHeight) - return FALSE; - - oWidth = dlgRect.right - dlgRect.left + rWidth; - oHeight = dlgRect.bottom - dlgRect.top + rHeight; - - if (oHeight > wrkRect.bottom - wrkRect.top) - { - oWidth = (int)((double)(wrkRect.bottom - wrkRect.top + imgRect.bottom - imgRect.top - dlgRect.bottom + dlgRect.top) * nWidth / nHeight) - - imgRect.right + imgRect.left + dlgRect.right - dlgRect.left; - if (oWidth < dlgRect.right - dlgRect.left) - oWidth = dlgRect.right - dlgRect.left; - oHeight = wrkRect.bottom - wrkRect.top; - } - if (oWidth > wrkRect.right - wrkRect.left) - { - oHeight = (int)((double)(wrkRect.right - wrkRect.left + imgRect.right - imgRect.left - dlgRect.right + dlgRect.left) * nHeight / nWidth) - - imgRect.bottom + imgRect.top + dlgRect.bottom - dlgRect.top; - if (oHeight < dlgRect.bottom - dlgRect.top) - oHeight = dlgRect.bottom - dlgRect.top; - oWidth = wrkRect.right - wrkRect.left; - } - SetWindowPos(hwndDlg, NULL, - (wrkRect.left + wrkRect.right - oWidth) / 2, - (wrkRect.top + wrkRect.bottom - oHeight) / 2, - oWidth, oHeight, - SWP_SHOWWINDOW | SWP_NOZORDER /* | SWP_NOACTIVATE */); - - return TRUE; -} - -//////////////////////////////////////////////////////////////////////////// -// Dialog resizer procedure -static int sttImageDlgResizer(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL* urc) -{ - switch (urc->wId) - { - case IDC_IMG_PREV: - case IDC_IMG_NEXT: - case IDC_IMG_DELETE: - case IDC_IMG_SAVE: - return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; - case IDC_IMG_IMAGE: - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH; - case IDC_IMG_SEND: - case IDC_IMG_CANCEL: - return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -//////////////////////////////////////////////////////////////////////////// -// Send / Recv main dialog procedure -static INT_PTR CALLBACK gg_img_dlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch (msg) - { - case WM_INITDIALOG: - { - char *szName, szTitle[128]; - RECT rect; - - TranslateDialogDefault(hwndDlg); - // This should be already initialized - // InitCommonControls(); - - // Get dialog data - dat = (GGIMAGEDLGDATA *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); - - // Save dialog handle - dat->hWnd = hwndDlg; - - // Send event if someone's waiting - if (dat->hEvent) SetEvent(dat->hEvent); - else gg_netlog(dat->gg, "gg_img_dlgproc(): Creation event not found, but someone might be waiting."); - - // Making buttons flat - SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BUTTONSETASFLATBTN, TRUE, 0); - SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BUTTONSETASFLATBTN, TRUE, 0); - SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BUTTONSETASFLATBTN, TRUE, 0); - SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BUTTONSETASFLATBTN, TRUE, 0); - - // Setting images for buttons - SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("previous", FALSE)); - SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("next", FALSE)); - SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("delete", FALSE)); - SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("save", FALSE)); - - // Setting tooltips for buttons - SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Previous image"), BATF_TCHAR); - SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Next image"), BATF_TCHAR); - SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Delete image from the list"), BATF_TCHAR); - SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Save image to disk"), BATF_TCHAR); - - // Set main window image - WindowSetIcon(hwndDlg, "image"); - - szName = (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)dat->hContact, 0); - if (dat->bReceiving) - mir_snprintf(szTitle, sizeof(szTitle), Translate("Image from %s"), szName); - else - mir_snprintf(szTitle, sizeof(szTitle), Translate("Image for %s"), szName); - SetWindowText(hwndDlg, szTitle); - - // Store client extents - GetClientRect(hwndDlg, &rect); - dat->minSize.cx = rect.right - rect.left; - dat->minSize.cy = rect.bottom - rect.top; - } - return TRUE; - - case WM_SIZE: - { - UTILRESIZEDIALOG urd = {0}; - urd.cbSize = sizeof(urd); - urd.hInstance = hInstance; - urd.hwndDlg = hwndDlg; - urd.lpTemplate = dat->bReceiving ? MAKEINTRESOURCEA(IDD_IMAGE_RECV) : MAKEINTRESOURCEA(IDD_IMAGE_SEND); - urd.pfnResizer = sttImageDlgResizer; - CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd); - if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) - InvalidateRect(hwndDlg, NULL, FALSE); - return 0; - } - - case WM_SIZING: - { - RECT *pRect = (RECT *)lParam; - if (pRect->right - pRect->left < dat->minSize.cx) - { - if (wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT) - pRect->left = pRect->right - dat->minSize.cx; - else - pRect->right = pRect->left + dat->minSize.cx; - } - if (pRect->bottom - pRect->top < dat->minSize.cy) - { - if (wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOP || wParam == WMSZ_TOPRIGHT) - pRect->top = pRect->bottom - dat->minSize.cy; - else - pRect->bottom = pRect->top + dat->minSize.cy; - } - } - return TRUE; - - case WM_CLOSE: - EndDialog(hwndDlg, 0); - break; - - // Flash the window - case WM_TIMER: - if (wParam == TIMERID_FLASHWND) - FlashWindow(hwndDlg, TRUE); - break; - - // Kill the timer - case WM_ACTIVATE: - if (LOWORD(wParam) != WA_ACTIVE) - break; - case WM_MOUSEACTIVATE: - if (KillTimer(hwndDlg, TIMERID_FLASHWND)) - FlashWindow(hwndDlg, FALSE); - break; - - case WM_PAINT: - if (dat->lpImages) - { - GGIMAGEENTRY *img = dat->lpImages; - int i; - - for (i = 1; img && (i < dat->nImg); i++) - img = img->lpNext; - - if (!img) - { - gg_netlog(dat->gg, "gg_img_dlgproc(): Image was not found on the list. Cannot paint the window."); - return FALSE; - } - - if (dat->bReceiving) - { - char szTitle[128]; - mir_snprintf(szTitle, sizeof(szTitle), - "%s (%d / %d)", img->lpszFileName, dat->nImg, dat->nImgTotal); - SetDlgItemText(hwndDlg, IDC_IMG_NAME, szTitle); - } - else - SetDlgItemText(hwndDlg, IDC_IMG_NAME, img->lpszFileName); - gg_img_paint(hwndDlg, img); - } - break; - - case WM_DESTROY: - if (dat) - { - // Deleting all image entries - GGIMAGEENTRY *temp, *img = dat->lpImages; - GGPROTO *gg = dat->gg; - while (temp = img) - { - img = img->lpNext; - gg_img_releasepicture(temp); - } - ReleaseIconEx("previous", FALSE); - ReleaseIconEx("next", FALSE); - ReleaseIconEx("delete", FALSE); - ReleaseIconEx("save", FALSE); - WindowFreeIcon(hwndDlg); - EnterCriticalSection(&gg->img_mutex); - list_remove(&gg->imagedlgs, dat, 1); - LeaveCriticalSection(&gg->img_mutex); - } - return TRUE; - - case WM_COMMAND: - switch (LOWORD(wParam)) - { - case IDC_IMG_CANCEL: - EndDialog(hwndDlg, 0); - return TRUE; - - case IDC_IMG_PREV: - if (dat->nImg > 1) - { - dat->nImg--; - InvalidateRect(hwndDlg, NULL, FALSE); - } - return TRUE; - - case IDC_IMG_NEXT: - if (dat->nImg < dat->nImgTotal) - { - dat->nImg++; - InvalidateRect(hwndDlg, NULL, FALSE); - } - return TRUE; - - case IDC_IMG_DELETE: - { - GGIMAGEENTRY *del, *img = dat->lpImages; - if (dat->nImg == 1) - { - del = dat->lpImages; - dat->lpImages = img->lpNext; - } - else - { - int i; - for (i = 1; img && (i < dat->nImg - 1); i++) - img = img->lpNext; - if (!img) - { - gg_netlog(dat->gg, "gg_img_dlgproc(): Image was not found on the list. Cannot delete it from the list."); - return FALSE; - } - del = img->lpNext; - img->lpNext = del->lpNext; - dat->nImg --; - } - - if ((-- dat->nImgTotal) == 0) - EndDialog(hwndDlg, 0); - else - InvalidateRect(hwndDlg, NULL, FALSE); - - gg_img_releasepicture(del); - } - return TRUE; - - case IDC_IMG_SAVE: - { - GGIMAGEENTRY *img = dat->lpImages; - int i; - - for (i = 1; img && (i < dat->nImg); i++) - img = img->lpNext; - if (!img) - { - gg_netlog(dat->gg, "gg_img_dlgproc(): Image was not found on the list. Cannot launch saving."); - return FALSE; - } - gg_img_saveimage(hwndDlg, img); - } - return TRUE; - - case IDC_IMG_SEND: - { - unsigned char format[20]; - char *msg = "\xA0\0"; - GGPROTO *gg = dat->gg; - - if (dat->lpImages && gg_isonline(gg)) - { - uin_t uin = (uin_t)DBGetContactSettingDword(dat->hContact, gg->proto.m_szModuleName, GG_KEY_UIN, 0); - struct gg_msg_richtext_format *r = NULL; - struct gg_msg_richtext_image *p = NULL; - LPVOID pvData = NULL; - int len; - - ((struct gg_msg_richtext*)format)->flag = 2; - - r = (struct gg_msg_richtext_format *)(format + sizeof(struct gg_msg_richtext)); - r->position = 0; - r->font = GG_FONT_IMAGE; - - p = (struct gg_msg_richtext_image *)(format + sizeof(struct gg_msg_richtext) + sizeof(struct gg_msg_richtext_format)); - p->unknown1 = 0x109; - p->size = dat->lpImages->nSize; - - dat->lpImages->crc32 = p->crc32 = gg_fix32(gg_crc32(0, dat->lpImages->lpData, dat->lpImages->nSize)); - - len = sizeof(struct gg_msg_richtext_format) + sizeof(struct gg_msg_richtext_image); - ((struct gg_msg_richtext*)format)->length = len; - - EnterCriticalSection(&gg->sess_mutex); - gg_send_message_richtext(gg->sess, GG_CLASS_CHAT, (uin_t)uin, (unsigned char*)msg, format, len + sizeof(struct gg_msg_richtext)); - LeaveCriticalSection(&gg->sess_mutex); - - // Protect dat from releasing - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)0); - - EndDialog(hwndDlg, 0); - } - return TRUE; - } - break; - } - break; - - case WM_ADDIMAGE: // lParam == GGIMAGEENTRY *dat - { - GGIMAGEENTRY *lpImage = (GGIMAGEENTRY *)lParam; - GGIMAGEENTRY *lpImages = dat->lpImages; - - if (!dat->lpImages) // first image entry - dat->lpImages = lpImage; - else // adding at the end of the list - { - while (lpImages->lpNext) - lpImages = lpImages->lpNext; - lpImages->lpNext = lpImage; - } - dat->nImg = ++ dat->nImgTotal; - } - // Fit window to image - if (!gg_img_fit(hwndDlg)) - InvalidateRect(hwndDlg, NULL, FALSE); - return TRUE; - - case WM_CHOOSEIMG: - { - char szFilter[128]; - char szFileName[MAX_PATH]; - OPENFILENAME ofn = {0}; - - gg_img_getfilter(szFilter, sizeof(szFilter)); - *szFileName = 0; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = hwndDlg; - ofn.hInstance = hInstance; - ofn.lpstrFilter = szFilter; - ofn.lpstrFile = szFileName; - ofn.nMaxFile = MAX_PATH; - ofn.lpstrTitle = Translate("Select picture to send"); - ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY; - if (GetOpenFileName(&ofn)) - { - if (dat->lpImages) - gg_img_releasepicture(dat->lpImages); - if (!(dat->lpImages = (GGIMAGEENTRY *)gg_img_loadpicture(dat->gg, 0, szFileName))) - { - EndDialog(hwndDlg, 0); - return FALSE; - } - if (!gg_img_fit(hwndDlg)) - InvalidateRect(hwndDlg, NULL, FALSE); - } - else - { - EndDialog(hwndDlg, 0); - return FALSE; - } - return TRUE; - } - } - - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////// -// Image dialog call thread -void __cdecl gg_img_dlgcallthread(GGPROTO *gg, void *param) -{ - HWND hMIWnd = 0; //(HWND) CallService(MS_CLUI_GETHWND, 0, 0); - - GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)param; - DialogBoxParam(hInstance, dat->bReceiving ? MAKEINTRESOURCE(IDD_IMAGE_RECV) : MAKEINTRESOURCE(IDD_IMAGE_SEND), - hMIWnd, gg_img_dlgproc, (LPARAM) dat); -} - -//////////////////////////////////////////////////////////////////////////// -// Open dialog receive for specified contact -GGIMAGEDLGDATA *gg_img_recvdlg(GGPROTO *gg, HANDLE hContact) -{ - // Create dialog data - GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)calloc(1, sizeof(GGIMAGEDLGDATA)); - dat->hContact = hContact; - dat->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - dat->bReceiving = TRUE; - dat->gg = gg; - ResetEvent(dat->hEvent); - gg_forkthread(gg, gg_img_dlgcallthread, dat); - return dat; -} - -//////////////////////////////////////////////////////////////////////////// -// Checks if an image is already saved to the specified path -// Returns 1 if yes, 0 if no or -1 if different image on this path is found -int gg_img_isexists(char *szPath, GGIMAGEENTRY *dat) -{ - struct _stat st; - - if (_stat(szPath, &st) != 0) - return 0; - - if (st.st_size == dat->nSize) - { - char *lpData; - FILE *fp = fopen(szPath, "rb"); - if (!fp) return 0; - lpData = mir_alloc(dat->nSize); - if (fread(lpData, 1, dat->nSize, fp) == dat->nSize) - { - if (dat->crc32 == gg_fix32(gg_crc32(0, lpData, dat->nSize)) || - memcmp(lpData, dat->lpData, dat->nSize) == 0) - { - mir_free(lpData); - fclose(fp); - return 1; - } - } - mir_free(lpData); - fclose(fp); - } - - return -1; -} - -//////////////////////////////////////////////////////////////////////////// -// Determine if image's file name has the proper extension -char *gg_img_hasextension(const char *filename) -{ - if (filename != NULL && *filename != '\0') - { - char *imgtype = strrchr(filename, '.'); - if (imgtype != NULL) - { - size_t len = strlen(imgtype); - imgtype++; - if (len == 4 && (_stricmp(imgtype, "bmp") == 0 || - _stricmp(imgtype, "gif") == 0 || - _stricmp(imgtype, "jpg") == 0 || - _stricmp(imgtype, "png") == 0)) - return --imgtype; - if (len == 5 && _stricmp(imgtype, "jpeg") == 0) - return --imgtype; - } - } - return NULL; -} - -//////////////////////////////////////////////////////////////////////////////// -// Display received image using message with [img] BBCode -int gg_img_displayasmsg(GGPROTO *gg, HANDLE hContact, void *img) -{ - GGIMAGEENTRY *dat = (GGIMAGEENTRY *)img; - char szPath[MAX_PATH], *path = (char*)alloca(MAX_PATH), *pImgext, imgext[6]; - size_t tPathLen; - int i, res; - - if (gg->hImagesFolder == NULL || FoldersGetCustomPath(gg->hImagesFolder, path, MAX_PATH, "")) - { - char *tmpPath = Utils_ReplaceVars("%miranda_userdata%"); - tPathLen = mir_snprintf(szPath, MAX_PATH, "%s\\%s\\ImageCache", tmpPath, GG_PROTO); - mir_free(tmpPath); - } - else - { - strcpy(szPath, path); - tPathLen = strlen(szPath); - } - - if (_access(szPath, 0)) - CallService(MS_UTILS_CREATEDIRTREE, 0, (LPARAM)szPath); - - mir_snprintf(szPath + tPathLen, MAX_PATH - tPathLen, "\\%s", dat->lpszFileName); - if ((pImgext = gg_img_hasextension(szPath)) == NULL) - pImgext = szPath + strlen(szPath); - mir_snprintf(imgext, SIZEOF(imgext), "%s", pImgext); - for (i = 1; ; ++i) - { - if ((res = gg_img_isexists(szPath, dat)) != -1) break; - mir_snprintf(szPath, MAX_PATH, "%.*s (%u)%s", pImgext - szPath, szPath, i, imgext); - } - - if (res == 0) - { - // Image file not found, thus create it - FILE *fp = fopen(szPath, "w+b"); - if (fp) - { - res = fwrite(dat->lpData, dat->nSize, 1, fp) > 0; - fclose(fp); - } - } - - if (res != 0) - { - char image_msg[MAX_PATH + 11]; - CCSDATA ccs = {0}; - PROTORECVEVENT pre = {0}; - - ccs.szProtoService = PSR_MESSAGE; - ccs.hContact = hContact; - ccs.lParam = (LPARAM)⪯ - mir_snprintf(image_msg, SIZEOF(image_msg), "[img]%s[/img]", szPath); - pre.timestamp = time(NULL); - pre.szMessage = image_msg; - CallService(MS_PROTO_CHAINRECV, 0, (LPARAM) &ccs); - gg_netlog(gg, "gg_img_displayasmsg: Image saved to %s.", szPath); - } - else - { - gg_netlog(gg, "gg_img_displayasmsg: Cannot save image to %s.", szPath); - } - - return 0; -} - -//////////////////////////////////////////////////////////////////////////// -// Return if uin has it's window already opened -BOOL gg_img_opened(GGPROTO *gg, uin_t uin) -{ - list_t l = gg->imagedlgs; - while (l) - { - GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)l->data; - if (dat->uin == uin) - return TRUE; - l = l->next; - } - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////// -// Image Module : Looking for window entry, create if not found -gg_img_display(GGPROTO *gg, HANDLE hContact, void *img) -{ - list_t l = gg->imagedlgs; - GGIMAGEDLGDATA *dat; - - if (!img) return FALSE; - - // Look for already open dialog - EnterCriticalSection(&gg->img_mutex); - while (l) - { - dat = (GGIMAGEDLGDATA *)l->data; - if (dat->bReceiving && dat->hContact == hContact) - break; - l = l->next; - } - - if (!l) dat = NULL; - - if (!dat) - { - dat = gg_img_recvdlg(gg, hContact); - dat->uin = DBGetContactSettingDword(hContact, gg->proto.m_szModuleName, GG_KEY_UIN, 0); - - while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0); - CloseHandle(dat->hEvent); - dat->hEvent = NULL; - - list_add(&gg->imagedlgs, dat, 0); - } - LeaveCriticalSection(&gg->img_mutex); - - SendMessage(dat->hWnd, WM_ADDIMAGE, 0, (LPARAM)img); - if (/*DBGetContactSettingByte(NULL, "Chat", "FlashWindowHighlight", 0) != 0 && */ - GetActiveWindow() != dat->hWnd && GetForegroundWindow() != dat->hWnd) - SetTimer(dat->hWnd, TIMERID_FLASHWND, 900, NULL); - - /* DEPRECATED: No more grabbing the focus... just flashing - SetForegroundWindow(dat->hWnd); - SetFocus(dat->hWnd); - */ - - return TRUE; -} - -//////////////////////////////////////////////////////////////////////////// -// Image Window : Frees image entry structure -gg_img_releasepicture(void *img) -{ - if (!img) - return FALSE; - if (((GGIMAGEENTRY *)img)->lpszFileName) - free(((GGIMAGEENTRY *)img)->lpszFileName); - if (((GGIMAGEENTRY *)img)->hBitmap) - DeleteObject(((GGIMAGEENTRY *)img)->hBitmap); - if (((GGIMAGEENTRY *)img)->lpData) - free(((GGIMAGEENTRY *)img)->lpData); - free(img); - - return TRUE; -} - -//////////////////////////////////////////////////////////////////////////// -// Helper function to determine image file format and the right extension -const char *gg_img_guessfileextension(const char *lpData) -{ - if (lpData != NULL) - { - if (memcmp(lpData, "BM", 2) == 0) - return ".bmp"; - if (memcmp(lpData, "GIF8", 4) == 0) - return ".gif"; - if (memcmp(lpData, "\xFF\xD8", 2) == 0) - return ".jpg"; - if (memcmp(lpData, "\x89PNG", 4) == 0) - return ".png"; - } - return ""; -} - -//////////////////////////////////////////////////////////////////////////// -// Image Window : Loading picture and sending for display -void *gg_img_loadpicture(GGPROTO *gg, struct gg_event* e, char *szFileName) -{ - GGIMAGEENTRY *dat; - - if (!szFileName && - (!e || !e->event.image_reply.size || !e->event.image_reply.image || !e->event.image_reply.filename)) - return NULL; - - dat = (GGIMAGEENTRY *)calloc(1, sizeof(GGIMAGEENTRY)); - if (dat == NULL) - return NULL; - - // Copy the file name - if (szFileName) - { - FILE *fp = fopen(szFileName, "rb"); - if (!fp) - { - free(dat); - gg_netlog(gg, "gg_img_loadpicture(): fopen(\"%s\", \"rb\") failed.", szFileName); - return NULL; - } - fseek(fp, 0, SEEK_END); - dat->nSize = ftell(fp); - if (dat->nSize <= 0) - { - fclose(fp); - free(dat); - gg_netlog(gg, "gg_img_loadpicture(): Zero file size \"%s\" failed.", szFileName); - return NULL; - } - // Maximum acceptable image size - if (dat->nSize > 255 * 1024) - { - fclose(fp); - free(dat); - gg_netlog(gg, "gg_img_loadpicture(): Image size of \"%s\" exceeds 255 KB.", szFileName); - MessageBox(NULL, Translate("Image exceeds maximum allowed size of 255 KB."), GG_PROTONAME, MB_OK | MB_ICONEXCLAMATION); - return NULL; - } - fseek(fp, 0, SEEK_SET); - dat->lpData = malloc(dat->nSize); - if (fread(dat->lpData, 1, dat->nSize, fp) < dat->nSize) - { - free(dat->lpData); - fclose(fp); - free(dat); - gg_netlog(gg, "gg_img_loadpicture(): Reading file \"%s\" failed.", szFileName); - return NULL; - } - fclose(fp); - dat->lpszFileName = _strdup(szFileName); - } - // Copy picture from packet - else if (e && e->event.image_reply.filename) - { - dat->nSize = e->event.image_reply.size; - dat->lpData = malloc(dat->nSize); - memcpy(dat->lpData, e->event.image_reply.image, dat->nSize); - - if (!gg_img_hasextension(e->event.image_reply.filename)) - { - // Add missing file extension - const char *szImgType = gg_img_guessfileextension(dat->lpData); - if (*szImgType) - { - dat->lpszFileName = malloc(strlen(e->event.image_reply.filename) + strlen(szImgType) + 1); - if (dat->lpszFileName != NULL) - { - strcpy(dat->lpszFileName, e->event.image_reply.filename); - strcat(dat->lpszFileName, szImgType); - } - } - } - - if (dat->lpszFileName == NULL) - dat->lpszFileName = _strdup(e->event.image_reply.filename); - } - - //////////////////////////////////////////////////////////////////// - // Loading picture using Miranda Image services - - // Load image from memory - if (!szFileName) - { - IMGSRVC_MEMIO memio; - memio.iLen = dat->nSize; - memio.pBuf = (void *)dat->lpData; - memio.fif = FIF_UNKNOWN; /* detect */ - memio.flags = 0; - dat->hBitmap = (HBITMAP) CallService(MS_IMG_LOADFROMMEM, (WPARAM) &memio, 0); - } - // Load image from file - else - dat->hBitmap = (HBITMAP) CallService(MS_IMG_LOAD, (WPARAM) szFileName, 0); - - // If everything is fine return the handle - if (dat->hBitmap) return dat; - - gg_netlog(gg, "gg_img_loadpicture(): MS_IMG_LOAD(MEM) failed."); - if (dat) - { - if (dat->lpData) - free(dat->lpData); - if (dat->lpszFileName) - free(dat->lpszFileName); - free(dat); - } - return NULL; -} - -//////////////////////////////////////////////////////////////////////////// -// Image Recv : AddEvent proc -INT_PTR gg_img_recvimage(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - CLISTEVENT *cle = (CLISTEVENT *)lParam; - GGIMAGEENTRY *img = (GGIMAGEENTRY *)cle->lParam; - - gg_netlog(gg, "gg_img_recvimage(%x, %x): Popup new image.", wParam, lParam); - if (!img) return FALSE; - - gg_img_display(gg, cle->hContact, img); - - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////// -// Windows queue management -//////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////// -// Removes dat structure -int gg_img_remove(GGIMAGEDLGDATA *dat) -{ - GGIMAGEENTRY *temp = NULL, *img = NULL; - GGPROTO *gg; - - if (!dat) return FALSE; - gg = dat->gg; - - EnterCriticalSection(&gg->img_mutex); - - // Remove the structure - img = dat->lpImages; - - // Destroy picture handle - while (temp = img) - { - img = img->lpNext; - gg_img_releasepicture(temp); - } - - // Remove from list - list_remove(&gg->imagedlgs, dat, 1); - LeaveCriticalSection(&gg->img_mutex); - - return TRUE; -} - -//////////////////////////////////////////////////////////////////////////// -// -GGIMAGEDLGDATA *gg_img_find(GGPROTO *gg, uin_t uin, uint32_t crc32) -{ - int res = 0; - list_t l = gg->imagedlgs; - GGIMAGEDLGDATA *dat; - - EnterCriticalSection(&gg->img_mutex); - while (l) - { - uin_t c_uin; - - dat = (GGIMAGEDLGDATA *)l->data; - if (!dat) break; - - c_uin = DBGetContactSettingDword(dat->hContact, dat->gg->proto.m_szModuleName, GG_KEY_UIN, 0); - - if (!dat->bReceiving && dat->lpImages && dat->lpImages->crc32 == crc32 && c_uin == uin) - { - LeaveCriticalSection(&gg->img_mutex); - return dat; - } - - l = l->next; - } - LeaveCriticalSection(&gg->img_mutex); - - gg_netlog(gg, "gg_img_find(): Image not found on the list. It might be released before calling this function."); - return NULL; -} - - -//////////////////////////////////////////////////////////////////////////// -// Image Module : Send on Request -BOOL gg_img_sendonrequest(GGPROTO *gg, struct gg_event* e) -{ - GGIMAGEDLGDATA *dat = gg_img_find(gg, e->event.image_request.sender, e->event.image_request.crc32); - - if (!gg || !dat || !gg_isonline(gg)) return FALSE; - - EnterCriticalSection(&gg->sess_mutex); - gg_image_reply(gg->sess, e->event.image_request.sender, dat->lpImages->lpszFileName, dat->lpImages->lpData, dat->lpImages->nSize); - LeaveCriticalSection(&gg->sess_mutex); - - gg_img_remove(dat); - - return TRUE; -} - -//////////////////////////////////////////////////////////////////////////// -// Send Image : Run (Thread and main) -INT_PTR gg_img_sendimg(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - HANDLE hContact = (HANDLE)wParam; - GGIMAGEDLGDATA *dat = NULL; - - EnterCriticalSection(&gg->img_mutex); - if (!dat) - { - dat = (GGIMAGEDLGDATA *)calloc(1, sizeof(GGIMAGEDLGDATA)); - dat->hContact = hContact; - dat->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - dat->gg = gg; - ResetEvent(dat->hEvent); - - // Create new dialog - gg_forkthread(gg, gg_img_dlgcallthread, dat); - - while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0); - CloseHandle(dat->hEvent); - dat->hEvent = NULL; - - list_add(&gg->imagedlgs, dat, 0); - } - - // Request choose dialog - SendMessage(dat->hWnd, WM_CHOOSEIMG, 0, 0); - LeaveCriticalSection(&gg->img_mutex); - - return 0; -} diff --git a/protocols/Gadu-Gadu/image.cpp b/protocols/Gadu-Gadu/image.cpp new file mode 100644 index 0000000000..8cf2db6a2d --- /dev/null +++ b/protocols/Gadu-Gadu/image.cpp @@ -0,0 +1,1191 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// Copyright (c) 2009-2012 Bartosz Białek +// +// 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 "gg.h" +#include + +#define WM_ADDIMAGE WM_USER + 1 +#define WM_SENDIMG WM_USER + 2 +#define WM_CHOOSEIMG WM_USER + 3 +#define TIMERID_FLASHWND WM_USER + 4 + +//////////////////////////////////////////////////////////////////////////// +// Image Window : Data + +// tablica zawiera uin kolesia i uchwyt do okna, okno zawiera GGIMAGEDLGDATA +// ktore przechowuje handle kontaktu, i wskaznik na pierwszy obrazek +// obrazki sa poukladane jako lista jednokierunkowa. +// przy tworzeniu okna podaje sie handle do kontaktu +// dodajac obrazek tworzy sie element listy i wysyla do okna +// wyswietlajac obrazek idzie po liscie jednokierunkowej + +typedef struct _GGIMAGEENTRY +{ + HBITMAP hBitmap; + TCHAR *lpszFileName; + char *lpData; + unsigned long nSize; + struct _GGIMAGEENTRY *lpNext; + uint32_t crc32; +} GGIMAGEENTRY; + +typedef struct +{ + HANDLE hContact; + HANDLE hEvent; + HWND hWnd; + uin_t uin; + int nImg, nImgTotal; + GGIMAGEENTRY *lpImages; + SIZE minSize; + BOOL bReceiving; + GGPROTO *gg; +} GGIMAGEDLGDATA; + +// Prototypes +int gg_img_remove(GGIMAGEDLGDATA *dat); + +//////////////////////////////////////////////////////////////////////////// +// Image Module : Adding item to contact menu, creating sync objects + +int GGPROTO::img_init() +{ + CLISTMENUITEM mi = {0}; + char service[64]; + + mi.cbSize = sizeof(mi); + mi.flags = CMIF_ICONFROMICOLIB; + + // Send image contact menu item + mir_snprintf(service, sizeof(service), GGS_SENDIMAGE, m_szModuleName); + createProtoService(service, &GGPROTO::img_sendimg); + mi.position = -2000010000; + mi.icolibItem = GetIconHandle(IDI_IMAGE); + mi.pszName = LPGEN("&Image"); + mi.pszService = service; + mi.pszContactOwner = m_szModuleName; + hImageMenuItem = Menu_AddContactMenuItem(&mi); + + // Receive image + mir_snprintf(service, sizeof(service), GGS_RECVIMAGE, m_szModuleName); + createProtoService(service, &GGPROTO::img_recvimage); + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Image Module : closing dialogs, sync objects +int GGPROTO::img_shutdown() +{ + list_t l; +#ifdef DEBUGMODE + netlog("gg_img_shutdown(): Closing all dialogs..."); +#endif + // Rather destroy window instead of just removing structures + for (l = imagedlgs; l;) + { + GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)l->data; + l = l->next; + + if (dat && dat->hWnd) + { + if (IsWindow(dat->hWnd)) + { + // Post message async, since it maybe be different thread + if (!PostMessage(dat->hWnd, WM_CLOSE, 0, 0)) + netlog("gg_img_shutdown(): Image dlg %x cannot be released !!", dat->hWnd); + } + else + netlog("gg_img_shutdown(): Image dlg %x not exists, but structure does !!", dat->hWnd); + } + } + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Image Module : destroying list + +int GGPROTO::img_destroy() +{ + // Release all dialogs + while (imagedlgs && gg_img_remove((GGIMAGEDLGDATA *)imagedlgs->data)); + + // Destroy list + list_destroy(imagedlgs, 1); + CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)hImageMenuItem, (LPARAM) 0); + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Image Window : Frees image entry structure + +static int gg_img_releasepicture(void *img) +{ + if (!img) + return FALSE; + if (((GGIMAGEENTRY *)img)->lpszFileName) + free(((GGIMAGEENTRY *)img)->lpszFileName); + if (((GGIMAGEENTRY *)img)->hBitmap) + DeleteObject(((GGIMAGEENTRY *)img)->hBitmap); + if (((GGIMAGEENTRY *)img)->lpData) + free(((GGIMAGEENTRY *)img)->lpData); + free(img); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////// +// Painting image +int gg_img_paint(HWND hwnd, GGIMAGEENTRY *dat) +{ + PAINTSTRUCT paintStruct; + HDC hdc = BeginPaint(hwnd, &paintStruct); + RECT rc; + + GetWindowRect(GetDlgItem(hwnd, IDC_IMG_IMAGE), &rc); + ScreenToClient(hwnd, (POINT *)&rc.left); + ScreenToClient(hwnd, (POINT *)&rc.right); + FillRect(hdc, &rc, (HBRUSH)GetSysColorBrush(COLOR_WINDOW)); + + if (dat->hBitmap) + { + HDC hdcBmp = NULL; + int nWidth, nHeight; + BITMAP bmp; + + GetObject(dat->hBitmap, sizeof(bmp), &bmp); + nWidth = bmp.bmWidth; nHeight = bmp.bmHeight; + + hdcBmp = CreateCompatibleDC(hdc); + SelectObject(hdcBmp, dat->hBitmap); + if (hdcBmp) + { + SetStretchBltMode(hdc, HALFTONE); + // Draw bitmap + if (nWidth > (rc.right-rc.left) || nHeight > (rc.bottom-rc.top)) + { + if ((double)nWidth / (double)nHeight > (double) (rc.right-rc.left) / (double)(rc.bottom-rc.top)) + { + StretchBlt(hdc, + rc.left, + ((rc.top + rc.bottom) - (rc.right - rc.left) * nHeight / nWidth) / 2, + (rc.right - rc.left), + (rc.right - rc.left) * nHeight / nWidth, + hdcBmp, 0, 0, nWidth, nHeight, SRCCOPY); + } + else + { + StretchBlt(hdc, + ((rc.left + rc.right) - (rc.bottom - rc.top) * nWidth / nHeight) / 2, + rc.top, + (rc.bottom - rc.top) * nWidth / nHeight, + (rc.bottom - rc.top), + hdcBmp, 0, 0, nWidth, nHeight, SRCCOPY); + } + } + else + { + BitBlt(hdc, + (rc.left + rc.right - nWidth) / 2, + (rc.top + rc.bottom - nHeight) / 2, + nWidth, nHeight, + hdcBmp, 0, 0, SRCCOPY); + } + DeleteDC(hdcBmp); + } + } + EndPaint(hwnd, &paintStruct); + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////////// +// Returns supported image filters + +TCHAR *gg_img_getfilter(TCHAR *szFilter, int nSize) +{ + TCHAR *szFilterName, *szFilterMask; + TCHAR *pFilter = szFilter; + + // Match relative to ImgDecoder presence + szFilterName = TranslateT("Image files (*.bmp,*.gif,*.jpeg,*.jpg,*.png)"); + szFilterMask = _T("*.bmp;*.gif;*.jpeg;*.jpg;*.png"); + + // Make up filter + _tcsncpy(pFilter, szFilterName, nSize); + pFilter += _tcslen(pFilter) + 1; + if (pFilter >= szFilter + nSize) return NULL; + _tcsncpy(pFilter, szFilterMask, nSize - (pFilter - szFilter)); + pFilter += _tcslen(pFilter) + 1; + if (pFilter >= szFilter + nSize) return NULL; + *pFilter = 0; + + return szFilter; +} + +//////////////////////////////////////////////////////////////////////////////// +// Save specified image entry + +int gg_img_saveimage(HWND hwnd, GGIMAGEENTRY *dat) +{ + if (!dat) + return FALSE; + + GGPROTO* gg = ((GGIMAGEDLGDATA *)GetWindowLongPtr(hwnd, GWLP_USERDATA))->gg; + + TCHAR szFilter[128]; + gg_img_getfilter(szFilter, SIZEOF(szFilter)); + + TCHAR szFileName[MAX_PATH]; + _tcsncpy(szFileName, dat->lpszFileName, SIZEOF(szFileName)); + + OPENFILENAME ofn = {0}; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = hwnd; + ofn.hInstance = hInstance; + ofn.lpstrFile = szFileName; + ofn.lpstrFilter = szFilter; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT; + if (GetSaveFileName(&ofn)) + { + FILE *fp = _tfopen(szFileName, _T("w+b")); + if (fp) + { + fwrite(dat->lpData, dat->nSize, 1, fp); + fclose(fp); + gg->netlog("gg_img_saveimage(): Image saved to %s.", szFileName); + } + else + { + gg->netlog("gg_img_saveimage(): Cannot save image to %s.", szFileName); + MessageBox(hwnd, TranslateT("Image cannot be written to disk."), gg->m_tszUserName, MB_OK | MB_ICONERROR); + } + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////// +// Fit window size to image size + +BOOL gg_img_fit(HWND hwndDlg) +{ + GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + RECT dlgRect, imgRect, wrkRect; + int nWidth, nHeight; + int rWidth = 0, rHeight = 0; + int oWidth = 0, oHeight = 0; + BITMAP bmp; + GGIMAGEENTRY *img = NULL; + HDC hdc; + + // Check if image is loaded + if (!dat || !dat->lpImages || !dat->lpImages->hBitmap) + return FALSE; + + img = dat->lpImages; + + // Go to last image + while (img->lpNext && dat->lpImages->hBitmap) + img = img->lpNext; + + // Get rects of display + GetWindowRect(hwndDlg, &dlgRect); + GetClientRect(GetDlgItem(hwndDlg, IDC_IMG_IMAGE), &imgRect); + + hdc = GetDC(hwndDlg); + + GetObject(img->hBitmap, sizeof(bmp), &bmp); + nWidth = bmp.bmWidth; nHeight = bmp.bmHeight; + SystemParametersInfo(SPI_GETWORKAREA, 0, &wrkRect, 0); + + ReleaseDC(hwndDlg, hdc); + + if ((imgRect.right - imgRect.left) < nWidth) + rWidth = nWidth - imgRect.right + imgRect.left; + if ((imgRect.bottom - imgRect.top) < nWidth) + rHeight = nHeight - imgRect.bottom + imgRect.top; + + // Check if anything needs resize + if (!rWidth && !rHeight) + return FALSE; + + oWidth = dlgRect.right - dlgRect.left + rWidth; + oHeight = dlgRect.bottom - dlgRect.top + rHeight; + + if (oHeight > wrkRect.bottom - wrkRect.top) + { + oWidth = (int)((double)(wrkRect.bottom - wrkRect.top + imgRect.bottom - imgRect.top - dlgRect.bottom + dlgRect.top) * nWidth / nHeight) + - imgRect.right + imgRect.left + dlgRect.right - dlgRect.left; + if (oWidth < dlgRect.right - dlgRect.left) + oWidth = dlgRect.right - dlgRect.left; + oHeight = wrkRect.bottom - wrkRect.top; + } + if (oWidth > wrkRect.right - wrkRect.left) + { + oHeight = (int)((double)(wrkRect.right - wrkRect.left + imgRect.right - imgRect.left - dlgRect.right + dlgRect.left) * nHeight / nWidth) + - imgRect.bottom + imgRect.top + dlgRect.bottom - dlgRect.top; + if (oHeight < dlgRect.bottom - dlgRect.top) + oHeight = dlgRect.bottom - dlgRect.top; + oWidth = wrkRect.right - wrkRect.left; + } + SetWindowPos(hwndDlg, NULL, + (wrkRect.left + wrkRect.right - oWidth) / 2, + (wrkRect.top + wrkRect.bottom - oHeight) / 2, + oWidth, oHeight, + SWP_SHOWWINDOW | SWP_NOZORDER /* | SWP_NOACTIVATE */); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////// +// Dialog resizer procedure +static int sttImageDlgResizer(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL* urc) +{ + switch (urc->wId) + { + case IDC_IMG_PREV: + case IDC_IMG_NEXT: + case IDC_IMG_DELETE: + case IDC_IMG_SAVE: + return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; + case IDC_IMG_IMAGE: + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH; + case IDC_IMG_SEND: + case IDC_IMG_CANCEL: + return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; + } + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; +} + +//////////////////////////////////////////////////////////////////////////// +// Send / Recv main dialog procedure +static INT_PTR CALLBACK gg_img_dlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch (msg) { + case WM_INITDIALOG: + { + RECT rect; + + TranslateDialogDefault(hwndDlg); + // This should be already initialized + // InitCommonControls(); + + // Get dialog data + dat = (GGIMAGEDLGDATA *)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); + + // Save dialog handle + dat->hWnd = hwndDlg; + + // Send event if someone's waiting + if (dat->hEvent) SetEvent(dat->hEvent); + else dat->gg->netlog("gg_img_dlgproc(): Creation event not found, but someone might be waiting."); + + // Making buttons flat + SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BUTTONSETASFLATBTN, TRUE, 0); + SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BUTTONSETASFLATBTN, TRUE, 0); + SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BUTTONSETASFLATBTN, TRUE, 0); + SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BUTTONSETASFLATBTN, TRUE, 0); + + // Setting images for buttons + SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("previous", FALSE)); + SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("next", FALSE)); + SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("delete", FALSE)); + SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("save", FALSE)); + + // Setting tooltips for buttons + SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Previous image"), BATF_TCHAR); + SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Next image"), BATF_TCHAR); + SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Delete image from the list"), BATF_TCHAR); + SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Save image to disk"), BATF_TCHAR); + + // Set main window image + WindowSetIcon(hwndDlg, "image"); + + TCHAR *szName = pcli->pfnGetContactDisplayName(dat->hContact, 0), szTitle[128]; + if (dat->bReceiving) + mir_sntprintf(szTitle, SIZEOF(szTitle), TranslateT("Image from %s"), szName); + else + mir_sntprintf(szTitle, SIZEOF(szTitle), TranslateT("Image for %s"), szName); + SetWindowText(hwndDlg, szTitle); + + // Store client extents + GetClientRect(hwndDlg, &rect); + dat->minSize.cx = rect.right - rect.left; + dat->minSize.cy = rect.bottom - rect.top; + } + return TRUE; + + case WM_SIZE: + { + UTILRESIZEDIALOG urd = {0}; + urd.cbSize = sizeof(urd); + urd.hInstance = hInstance; + urd.hwndDlg = hwndDlg; + urd.lpTemplate = dat->bReceiving ? MAKEINTRESOURCEA(IDD_IMAGE_RECV) : MAKEINTRESOURCEA(IDD_IMAGE_SEND); + urd.pfnResizer = sttImageDlgResizer; + CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd); + if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) + InvalidateRect(hwndDlg, NULL, FALSE); + return 0; + } + + case WM_SIZING: + { + RECT *pRect = (RECT *)lParam; + if (pRect->right - pRect->left < dat->minSize.cx) + { + if (wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT) + pRect->left = pRect->right - dat->minSize.cx; + else + pRect->right = pRect->left + dat->minSize.cx; + } + if (pRect->bottom - pRect->top < dat->minSize.cy) + { + if (wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOP || wParam == WMSZ_TOPRIGHT) + pRect->top = pRect->bottom - dat->minSize.cy; + else + pRect->bottom = pRect->top + dat->minSize.cy; + } + } + return TRUE; + + case WM_CLOSE: + EndDialog(hwndDlg, 0); + break; + + // Flash the window + case WM_TIMER: + if (wParam == TIMERID_FLASHWND) + FlashWindow(hwndDlg, TRUE); + break; + + // Kill the timer + case WM_ACTIVATE: + if (LOWORD(wParam) != WA_ACTIVE) + break; + case WM_MOUSEACTIVATE: + if (KillTimer(hwndDlg, TIMERID_FLASHWND)) + FlashWindow(hwndDlg, FALSE); + break; + + case WM_PAINT: + if (dat->lpImages) + { + GGIMAGEENTRY *img = dat->lpImages; + int i; + + for (i = 1; img && (i < dat->nImg); i++) + img = img->lpNext; + + if (!img) + { + dat->gg->netlog("gg_img_dlgproc(): Image was not found on the list. Cannot paint the window."); + return FALSE; + } + + if (dat->bReceiving) + { + TCHAR szTitle[128]; + mir_sntprintf(szTitle, SIZEOF(szTitle), _T("%s (%d / %d)"), img->lpszFileName, dat->nImg, dat->nImgTotal); + SetDlgItemText(hwndDlg, IDC_IMG_NAME, szTitle); + } + else + SetDlgItemText(hwndDlg, IDC_IMG_NAME, img->lpszFileName); + gg_img_paint(hwndDlg, img); + } + break; + + case WM_DESTROY: + if (dat) + { + // Deleting all image entries + GGIMAGEENTRY *temp, *img = dat->lpImages; + GGPROTO *gg = dat->gg; + while (temp = img) + { + img = img->lpNext; + gg_img_releasepicture(temp); + } + ReleaseIconEx("previous", FALSE); + ReleaseIconEx("next", FALSE); + ReleaseIconEx("delete", FALSE); + ReleaseIconEx("save", FALSE); + WindowFreeIcon(hwndDlg); + EnterCriticalSection(&gg->img_mutex); + list_remove(&gg->imagedlgs, dat, 1); + LeaveCriticalSection(&gg->img_mutex); + } + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_IMG_CANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + + case IDC_IMG_PREV: + if (dat->nImg > 1) + { + dat->nImg--; + InvalidateRect(hwndDlg, NULL, FALSE); + } + return TRUE; + + case IDC_IMG_NEXT: + if (dat->nImg < dat->nImgTotal) + { + dat->nImg++; + InvalidateRect(hwndDlg, NULL, FALSE); + } + return TRUE; + + case IDC_IMG_DELETE: + { + GGIMAGEENTRY *del, *img = dat->lpImages; + if (dat->nImg == 1) + { + del = dat->lpImages; + dat->lpImages = img->lpNext; + } + else + { + int i; + for (i = 1; img && (i < dat->nImg - 1); i++) + img = img->lpNext; + if (!img) + { + dat->gg->netlog("gg_img_dlgproc(): Image was not found on the list. Cannot delete it from the list."); + return FALSE; + } + del = img->lpNext; + img->lpNext = del->lpNext; + dat->nImg --; + } + + if ((-- dat->nImgTotal) == 0) + EndDialog(hwndDlg, 0); + else + InvalidateRect(hwndDlg, NULL, FALSE); + + gg_img_releasepicture(del); + } + return TRUE; + + case IDC_IMG_SAVE: + { + GGIMAGEENTRY *img = dat->lpImages; + int i; + + for (i = 1; img && (i < dat->nImg); i++) + img = img->lpNext; + if (!img) + { + dat->gg->netlog("gg_img_dlgproc(): Image was not found on the list. Cannot launch saving."); + return FALSE; + } + gg_img_saveimage(hwndDlg, img); + } + return TRUE; + + case IDC_IMG_SEND: + { + unsigned char format[20]; + char *msg = "\xA0\0"; + GGPROTO *gg = dat->gg; + + if (dat->lpImages && gg->isonline()) + { + uin_t uin = (uin_t)db_get_b(dat->hContact, gg->m_szModuleName, GG_KEY_UIN, 0); + struct gg_msg_richtext_format *r = NULL; + struct gg_msg_richtext_image *p = NULL; + LPVOID pvData = NULL; + int len; + + ((struct gg_msg_richtext*)format)->flag = 2; + + r = (struct gg_msg_richtext_format *)(format + sizeof(struct gg_msg_richtext)); + r->position = 0; + r->font = GG_FONT_IMAGE; + + p = (struct gg_msg_richtext_image *)(format + sizeof(struct gg_msg_richtext) + sizeof(struct gg_msg_richtext_format)); + p->unknown1 = 0x109; + p->size = dat->lpImages->nSize; + + dat->lpImages->crc32 = p->crc32 = gg_fix32(gg_crc32(0, (BYTE*)dat->lpImages->lpData, dat->lpImages->nSize)); + + len = sizeof(struct gg_msg_richtext_format) + sizeof(struct gg_msg_richtext_image); + ((struct gg_msg_richtext*)format)->length = len; + + EnterCriticalSection(&gg->sess_mutex); + gg_send_message_richtext(gg->sess, GG_CLASS_CHAT, (uin_t)uin, (unsigned char*)msg, format, len + sizeof(struct gg_msg_richtext)); + LeaveCriticalSection(&gg->sess_mutex); + + // Protect dat from releasing + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)0); + + EndDialog(hwndDlg, 0); + } + return TRUE; + } + break; + } + break; + + case WM_ADDIMAGE: // lParam == GGIMAGEENTRY *dat + { + GGIMAGEENTRY *lpImage = (GGIMAGEENTRY *)lParam; + GGIMAGEENTRY *lpImages = dat->lpImages; + + if (!dat->lpImages) // first image entry + dat->lpImages = lpImage; + else // adding at the end of the list + { + while (lpImages->lpNext) + lpImages = lpImages->lpNext; + lpImages->lpNext = lpImage; + } + dat->nImg = ++ dat->nImgTotal; + } + // Fit window to image + if (!gg_img_fit(hwndDlg)) + InvalidateRect(hwndDlg, NULL, FALSE); + return TRUE; + + case WM_CHOOSEIMG: + { + TCHAR szFilter[128]; + TCHAR szFileName[MAX_PATH]; + OPENFILENAME ofn = {0}; + + gg_img_getfilter(szFilter, sizeof(szFilter)); + *szFileName = 0; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = hwndDlg; + ofn.hInstance = hInstance; + ofn.lpstrFilter = szFilter; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = MAX_PATH; + ofn.lpstrTitle = TranslateT("Select picture to send"); + ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY; + if (GetOpenFileName(&ofn)) + { + if (dat->lpImages) + gg_img_releasepicture(dat->lpImages); + if (!(dat->lpImages = (GGIMAGEENTRY *)dat->gg->img_loadpicture(0, szFileName))) + { + EndDialog(hwndDlg, 0); + return FALSE; + } + if (!gg_img_fit(hwndDlg)) + InvalidateRect(hwndDlg, NULL, FALSE); + } + else + { + EndDialog(hwndDlg, 0); + return FALSE; + } + return TRUE; + } + } + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Image dialog call thread + +void __cdecl GGPROTO::img_dlgcallthread(void *param) +{ + HWND hMIWnd = 0; //(HWND) CallService(MS_CLUI_GETHWND, 0, 0); + + GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)param; + DialogBoxParam(hInstance, dat->bReceiving ? MAKEINTRESOURCE(IDD_IMAGE_RECV) : MAKEINTRESOURCE(IDD_IMAGE_SEND), + hMIWnd, gg_img_dlgproc, (LPARAM) dat); +} + +//////////////////////////////////////////////////////////////////////////// +// Open dialog receive for specified contact +GGIMAGEDLGDATA *gg_img_recvdlg(GGPROTO *gg, HANDLE hContact) +{ + // Create dialog data + GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)calloc(1, sizeof(GGIMAGEDLGDATA)); + dat->hContact = hContact; + dat->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + dat->bReceiving = TRUE; + dat->gg = gg; + ResetEvent(dat->hEvent); + gg->forkthread(&GGPROTO::img_dlgcallthread, dat); + return dat; +} + +//////////////////////////////////////////////////////////////////////////// +// Checks if an image is already saved to the specified path +// Returns 1 if yes, 0 if no or -1 if different image on this path is found +int gg_img_isexists(TCHAR *szPath, GGIMAGEENTRY *dat) +{ + struct _stat st; + + if (_tstat(szPath, &st) != 0) + return 0; + + if (st.st_size == dat->nSize) + { + FILE *fp = _tfopen(szPath, _T("rb")); + if (!fp) return 0; + char *lpData = (char*)mir_alloc(dat->nSize); + if (fread(lpData, 1, dat->nSize, fp) == dat->nSize) + { + if (dat->crc32 == gg_fix32(gg_crc32(0, (BYTE*)lpData, dat->nSize)) || + memcmp(lpData, dat->lpData, dat->nSize) == 0) + { + mir_free(lpData); + fclose(fp); + return 1; + } + } + mir_free(lpData); + fclose(fp); + } + + return -1; +} + +//////////////////////////////////////////////////////////////////////////// +// Determine if image's file name has the proper extension +TCHAR *gg_img_hasextension(TCHAR *filename) +{ + if (filename != NULL && *filename != '\0') + { + TCHAR *imgtype = _tcsrchr(filename, '.'); + if (imgtype != NULL) + { + size_t len = _tcslen(imgtype); + imgtype++; + if (len == 4 && (_tcsicmp(imgtype, _T("bmp")) == 0 || + _tcsicmp(imgtype, _T("gif")) == 0 || + _tcsicmp(imgtype, _T("jpg")) == 0 || + _tcsicmp(imgtype, _T("png")) == 0)) + return --imgtype; + if (len == 5 && _tcsicmp(imgtype, _T("jpeg")) == 0) + return --imgtype; + } + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// Display received image using message with [img] BBCode + +int GGPROTO::img_displayasmsg(HANDLE hContact, void *img) +{ + GGIMAGEENTRY *dat = (GGIMAGEENTRY *)img; + TCHAR szPath[MAX_PATH], path[MAX_PATH], *pImgext, imgext[6]; + size_t tPathLen; + int i, res; + + if (hImagesFolder == NULL || FoldersGetCustomPathT(hImagesFolder, path, MAX_PATH, _T(""))) { + TCHAR *tmpPath = Utils_ReplaceVarsT( _T("%miranda_userdata%")); + tPathLen = mir_sntprintf(szPath, MAX_PATH, _T("%s\\%s\\ImageCache"), tmpPath, m_tszUserName); + mir_free(tmpPath); + } + else { + _tcscpy(szPath, path); + tPathLen = _tcslen(szPath); + } + + if ( _taccess(szPath, 0)) + CallService(MS_UTILS_CREATEDIRTREET, 0, (LPARAM)szPath); + + mir_sntprintf(szPath + tPathLen, MAX_PATH - tPathLen, _T("\\%s"), dat->lpszFileName); + if ((pImgext = gg_img_hasextension(szPath)) == NULL) + pImgext = szPath + _tcslen(szPath); + mir_sntprintf(imgext, SIZEOF(imgext), _T("%s"), pImgext); + for (i = 1; ; ++i) + { + if ((res = gg_img_isexists(szPath, dat)) != -1) break; + mir_sntprintf(szPath, MAX_PATH, _T("%.*s (%u)%s"), pImgext - szPath, szPath, i, imgext); + } + + if (res == 0) { + // Image file not found, thus create it + FILE *fp = _tfopen(szPath, _T("w+b")); + if (fp) { + res = fwrite(dat->lpData, dat->nSize, 1, fp) > 0; + fclose(fp); + } + } + + if (res != 0) { + char image_msg[MAX_PATH + 11]; + CCSDATA ccs = {0}; + PROTORECVEVENT pre = {0}; + + ccs.szProtoService = PSR_MESSAGE; + ccs.hContact = hContact; + ccs.lParam = (LPARAM)⪯ + mir_snprintf(image_msg, SIZEOF(image_msg), "[img]%s[/img]", szPath); + pre.timestamp = time(NULL); + pre.szMessage = image_msg; + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM) &ccs); + netlog("gg_img_displayasmsg: Image saved to %s.", szPath); + } + else + { + netlog("gg_img_displayasmsg: Cannot save image to %s.", szPath); + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////// +// Return if uin has it's window already opened + +BOOL GGPROTO::img_opened(uin_t uin) +{ + list_t l = imagedlgs; + while (l) + { + GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)l->data; + if (dat->uin == uin) + return TRUE; + l = l->next; + } + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Image Module : Looking for window entry, create if not found + +int GGPROTO::img_display(HANDLE hContact, void *img) +{ + list_t l = imagedlgs; + GGIMAGEDLGDATA *dat; + + if (!img) return FALSE; + + // Look for already open dialog + EnterCriticalSection(&img_mutex); + while (l) + { + dat = (GGIMAGEDLGDATA *)l->data; + if (dat->bReceiving && dat->hContact == hContact) + break; + l = l->next; + } + + if (!l) dat = NULL; + + if (!dat) + { + dat = gg_img_recvdlg(this, hContact); + dat->uin = db_get_b(hContact, m_szModuleName, GG_KEY_UIN, 0); + + while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0); + CloseHandle(dat->hEvent); + dat->hEvent = NULL; + + list_add(&imagedlgs, dat, 0); + } + LeaveCriticalSection(&img_mutex); + + SendMessage(dat->hWnd, WM_ADDIMAGE, 0, (LPARAM)img); + if (/*db_get_b(NULL, "Chat", "FlashWindowHighlight", 0) != 0 && */ + GetActiveWindow() != dat->hWnd && GetForegroundWindow() != dat->hWnd) + SetTimer(dat->hWnd, TIMERID_FLASHWND, 900, NULL); + + /* DEPRECATED: No more grabbing the focus... just flashing + SetForegroundWindow(dat->hWnd); + SetFocus(dat->hWnd); + */ + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////// +// Helper function to determine image file format and the right extension + +const TCHAR *gg_img_guessfileextension(const char *lpData) +{ + if (lpData != NULL) + { + if (memcmp(lpData, "BM", 2) == 0) + return _T(".bmp"); + if (memcmp(lpData, "GIF8", 4) == 0) + return _T(".gif"); + if (memcmp(lpData, "\xFF\xD8", 2) == 0) + return _T(".jpg"); + if (memcmp(lpData, "\x89PNG", 4) == 0) + return _T(".png"); + } + return _T(""); +} + +//////////////////////////////////////////////////////////////////////////// +// Image Window : Loading picture and sending for display + +void* GGPROTO::img_loadpicture(gg_event* e, TCHAR *szFileName) +{ + GGIMAGEENTRY *dat; + + if (!szFileName && + (!e || !e->event.image_reply.size || !e->event.image_reply.image || !e->event.image_reply.filename)) + return NULL; + + dat = (GGIMAGEENTRY *)calloc(1, sizeof(GGIMAGEENTRY)); + if (dat == NULL) + return NULL; + + // Copy the file name + if (szFileName) + { + FILE *fp = _tfopen(szFileName, _T("rb")); + if (!fp) { + free(dat); + netlog("gg_img_loadpicture(): fopen(\"%s\", \"rb\") failed.", szFileName); + return NULL; + } + fseek(fp, 0, SEEK_END); + dat->nSize = ftell(fp); + if (dat->nSize <= 0) + { + fclose(fp); + free(dat); + netlog("gg_img_loadpicture(): Zero file size \"%s\" failed.", szFileName); + return NULL; + } + // Maximum acceptable image size + if (dat->nSize > 255 * 1024) + { + fclose(fp); + free(dat); + netlog("gg_img_loadpicture(): Image size of \"%s\" exceeds 255 KB.", szFileName); + MessageBox(NULL, TranslateT("Image exceeds maximum allowed size of 255 KB."), m_tszUserName, MB_OK | MB_ICONEXCLAMATION); + return NULL; + } + fseek(fp, 0, SEEK_SET); + dat->lpData = (char*)malloc(dat->nSize); + if (fread(dat->lpData, 1, dat->nSize, fp) < dat->nSize) + { + free(dat->lpData); + fclose(fp); + free(dat); + netlog("gg_img_loadpicture(): Reading file \"%s\" failed.", szFileName); + return NULL; + } + fclose(fp); + dat->lpszFileName = _tcsdup(szFileName); + } + // Copy picture from packet + else if (e && e->event.image_reply.filename) + { + dat->nSize = e->event.image_reply.size; + dat->lpData = (char*)malloc(dat->nSize); + memcpy(dat->lpData, e->event.image_reply.image, dat->nSize); + + mir_ptr tmpFileName( mir_a2t(e->event.image_reply.filename)); + if (!gg_img_hasextension(tmpFileName)) { + // Add missing file extension + const TCHAR *szImgType = gg_img_guessfileextension(dat->lpData); + if (*szImgType) { + dat->lpszFileName = (TCHAR*)calloc(sizeof(TCHAR), lstrlen(tmpFileName) + lstrlen(szImgType) + 1); + if (dat->lpszFileName != NULL) { + _tcscpy(dat->lpszFileName, tmpFileName); + _tcscat(dat->lpszFileName, szImgType); + } + } + } + + if (dat->lpszFileName == NULL) + dat->lpszFileName = _tcsdup( _A2T( e->event.image_reply.filename)); + } + + //////////////////////////////////////////////////////////////////// + // Loading picture using Miranda Image services + + // Load image from memory + if (!szFileName) + { + IMGSRVC_MEMIO memio; + memio.iLen = dat->nSize; + memio.pBuf = (void *)dat->lpData; + memio.fif = FIF_UNKNOWN; /* detect */ + memio.flags = 0; + dat->hBitmap = (HBITMAP) CallService(MS_IMG_LOADFROMMEM, (WPARAM) &memio, 0); + } + // Load image from file + else + dat->hBitmap = (HBITMAP) CallService(MS_IMG_LOAD, (WPARAM) szFileName, 0); + + // If everything is fine return the handle + if (dat->hBitmap) return dat; + + netlog("gg_img_loadpicture(): MS_IMG_LOAD(MEM) failed."); + if (dat) + { + if (dat->lpData) + free(dat->lpData); + if (dat->lpszFileName) + free(dat->lpszFileName); + free(dat); + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////// +// Image Recv : AddEvent proc +INT_PTR GGPROTO::img_recvimage(WPARAM wParam, LPARAM lParam) +{ + CLISTEVENT *cle = (CLISTEVENT *)lParam; + GGIMAGEENTRY *img = (GGIMAGEENTRY *)cle->lParam; + + netlog("gg_img_recvimage(%x, %x): Popup new image.", wParam, lParam); + if (!img) return FALSE; + + img_display(cle->hContact, img); + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Windows queue management +//////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////// +// Removes dat structure +int gg_img_remove(GGIMAGEDLGDATA *dat) +{ + GGIMAGEENTRY *temp = NULL, *img = NULL; + GGPROTO *gg; + + if (!dat) return FALSE; + gg = dat->gg; + + EnterCriticalSection(&gg->img_mutex); + + // Remove the structure + img = dat->lpImages; + + // Destroy picture handle + while (temp = img) + { + img = img->lpNext; + gg_img_releasepicture(temp); + } + + // Remove from list + list_remove(&gg->imagedlgs, dat, 1); + LeaveCriticalSection(&gg->img_mutex); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////// +// +GGIMAGEDLGDATA* gg_img_find(GGPROTO *gg, uin_t uin, uint32_t crc32) +{ + int res = 0; + list_t l = gg->imagedlgs; + GGIMAGEDLGDATA *dat; + + EnterCriticalSection(&gg->img_mutex); + while (l) + { + uin_t c_uin; + + dat = (GGIMAGEDLGDATA *)l->data; + if (!dat) break; + + c_uin = db_get_b(dat->hContact, dat->gg->m_szModuleName, GG_KEY_UIN, 0); + + if (!dat->bReceiving && dat->lpImages && dat->lpImages->crc32 == crc32 && c_uin == uin) + { + LeaveCriticalSection(&gg->img_mutex); + return dat; + } + + l = l->next; + } + LeaveCriticalSection(&gg->img_mutex); + + gg->netlog("gg_img_find(): Image not found on the list. It might be released before calling this function."); + return NULL; +} + + +//////////////////////////////////////////////////////////////////////////// +// Image Module : Send on Request + +BOOL GGPROTO::img_sendonrequest(gg_event* e) +{ + GGIMAGEDLGDATA *dat = gg_img_find(this, e->event.image_request.sender, e->event.image_request.crc32); + if (!this || !dat || !isonline()) + return FALSE; + + EnterCriticalSection(&sess_mutex); + gg_image_reply(sess, e->event.image_request.sender, dat->lpImages->lpszFileName, dat->lpImages->lpData, dat->lpImages->nSize); + LeaveCriticalSection(&sess_mutex); + + gg_img_remove(dat); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////// +// Send Image : Run (Thread and main) + +INT_PTR GGPROTO::img_sendimg(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE)wParam; + GGIMAGEDLGDATA *dat = NULL; + + EnterCriticalSection(&img_mutex); + if (!dat) + { + dat = (GGIMAGEDLGDATA *)calloc(1, sizeof(GGIMAGEDLGDATA)); + dat->hContact = hContact; + dat->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + dat->gg = this; + ResetEvent(dat->hEvent); + + // Create new dialog + forkthread(&GGPROTO::img_dlgcallthread, dat); + + while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0); + CloseHandle(dat->hEvent); + dat->hEvent = NULL; + + list_add(&imagedlgs, dat, 0); + } + + // Request choose dialog + SendMessage(dat->hWnd, WM_CHOOSEIMG, 0, 0); + LeaveCriticalSection(&img_mutex); + + return 0; +} diff --git a/protocols/Gadu-Gadu/import.c b/protocols/Gadu-Gadu/import.c deleted file mode 100644 index 460c417a0e..0000000000 --- a/protocols/Gadu-Gadu/import.c +++ /dev/null @@ -1,670 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 Adam Strzelecki -// -// 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 "gg.h" - -//////////////////////////////////////////////////////////////////////////////// -// Checks if a group already exists in Miranda with -// the specified name. -// Returns 1 if a group with the name exists, returns 0 otherwise. -int GroupNameExists(const char *name) -{ - char idstr[33]; - DBVARIANT dbv; - int i; - - for (i = 0; ; i++) { - _itoa(i, idstr, 10); - if (DBGetContactSettingString(NULL, "CListGroups", idstr, &dbv)) break; - if (!strcmp(dbv.pszVal + 1, name)) { - DBFreeVariant(&dbv); - return 1; - } - DBFreeVariant(&dbv); - } - return 0; -} - - -//////////////////////////////////////////////////////////////////////////////// -// Creates a group with a specified name in the -// Miranda contact list. -// Returns proper group name -char *CreateGroup(char *groupName) -{ - int groupId; - char groupIdStr[11]; - char groupName2[127]; - char *p; - DBVARIANT dbv; - - // Cleanup group name from weird characters - - // Skip first break - while(*groupName && *groupName == '\\') groupName++; - - p = strrchr(groupName, '\\'); - // Cleanup end - while(p && !(*(p + 1))) - { - *p = 0; - p = strrchr(groupName, '\\'); - } - // Create upper groups - if(p) - { - *p = 0; - CreateGroup(groupName); - *p = '\\'; - } - - // Is this a duplicate? - if (!GroupNameExists(groupName)) - { - lstrcpyn(groupName2 + 1, groupName, (int)strlen(groupName) + 1); - - // Find an unused id - for (groupId = 0; ; groupId++) { - _itoa(groupId, groupIdStr,10); - if (DBGetContactSettingString(NULL, "CListGroups", groupIdStr, &dbv)) - break; - DBFreeVariant(&dbv); - } - - groupName2[0] = 1|GROUPF_EXPANDED; // 1 is required so we never get '\0' - DBWriteContactSettingString(NULL, "CListGroups", groupIdStr, groupName2); - } - return groupName; -} - -char *gg_makecontacts(GGPROTO *gg, int cr) -{ - string_t s = string_init(NULL); - char *contacts; - - // Readup contacts - HANDLE hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); - while (hContact) - { - char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if (szProto != NULL && !strcmp(szProto, GG_PROTO) && !DBGetContactSettingByte(hContact, GG_PROTO, "ChatRoom", 0)) - { - DBVARIANT dbv; - - // Readup FirstName - if (!DBGetContactSettingString(hContact, GG_PROTO, "FirstName", &dbv)) - { - string_append(s, dbv.pszVal); - DBFreeVariant(&dbv); - } - string_append_c(s, ';'); - // Readup LastName - if (!DBGetContactSettingString(hContact, GG_PROTO, "LastName", &dbv)) - { - string_append(s, dbv.pszVal); - DBFreeVariant(&dbv); - } - string_append_c(s, ';'); - - // Readup Nick - if (!DBGetContactSettingString(hContact, "CList", "MyHandle", &dbv) || !DBGetContactSettingString(hContact, GG_PROTO, GG_KEY_NICK, &dbv)) - { - DBVARIANT dbv2; - if (!DBGetContactSettingString(hContact, GG_PROTO, "NickName", &dbv2)) - { - string_append(s, dbv2.pszVal); - DBFreeVariant(&dbv2); - } - else - string_append(s, dbv.pszVal); - string_append_c(s, ';'); - string_append(s, dbv.pszVal); - DBFreeVariant(&dbv); - } - else - string_append_c(s, ';'); - string_append_c(s, ';'); - - // Readup Phone (fixed: uses stored editable phones) - if (!DBGetContactSettingString(hContact, "UserInfo", "MyPhone0", &dbv)) - { - // Remove SMS postfix - char *sms = strstr(dbv.pszVal, " SMS"); - if(sms) *sms = 0; - - string_append(s, dbv.pszVal); - DBFreeVariant(&dbv); - } - string_append_c(s, ';'); - // Readup Group - if (!DBGetContactSettingString(hContact, "CList", "Group", &dbv)) - { - string_append(s, dbv.pszVal); - DBFreeVariant(&dbv); - } - string_append_c(s, ';'); - // Readup Uin - string_append(s, ditoa(DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0))); - string_append_c(s, ';'); - // Readup Mail (fixed: uses stored editable mails) - if (!DBGetContactSettingString(hContact, "UserInfo", "Mye-mail0", &dbv)) - { - string_append(s, dbv.pszVal); - DBFreeVariant(&dbv); - } - if(cr) - string_append(s, ";0;;0;\r\n"); - else - string_append(s, ";0;;0;\n"); - } - hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); - } - - contacts = string_free(s, 0); - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_makecontacts(): \n%s", contacts); -#endif - - return contacts; -} - -char *strndup(char *str, int c) -{ - char *ret = (char*)malloc(c + 1); - ret[c] = 0; - strncpy(ret, str, c); - return ret; -} - -void gg_parsecontacts(GGPROTO *gg, char *contacts) -{ - char *p = strchr(contacts, ':'), *n; - char *strFirstName, *strLastName, *strNickname, *strNick, *strPhone, *strGroup, *strUin, *strMail; - uin_t uin; - - // Skip to proper data - if(p && p < strchr(contacts, ';')) p++; - else p = contacts; - - while(p) - { - // Processing line - strFirstName = strLastName = strNickname = strNick = strPhone = strGroup = strUin = strMail = NULL; - uin = 0; - - // FirstName - if(p) - { - n = strchr(p, ';'); - if(n && n != p) strFirstName = strndup(p, (n - p)); - p = (n + 1); - } - // LastName - if(n && p) - { - n = strchr(p, ';'); - if(n && n != p) strLastName = strndup(p, (n - p)); - p = (n + 1); - } - // Nickname - if(n && p) - { - n = strchr(p, ';'); - if(n && n != p) strNickname = strndup(p, (n - p)); - p = (n + 1); - } - // Nick - if(n && p) - { - n = strchr(p, ';'); - if(n && n != p) strNick = strndup(p, (n - p)); - p = (n + 1); - } - // Phone - if(n && p) - { - n = strchr(p, ';'); - if(n && n != p) - { - strPhone = malloc((n - p) + 5); - strncpy(strPhone, p, (n - p)); - strcpy((strPhone + (n - p)), " SMS"); // Add SMS postfix - } - p = (n + 1); - } - // Group - if(n && p) - { - n = strchr(p, ';'); - if(n && n != p) strGroup = strndup(p, (n - p)); - p = (n + 1); - } - // Uin - if(n && p) - { - n = strchr(p, ';'); - if(n && n != p) - { - strUin = strndup(p, (n - p)); - uin = atoi(strUin); - } - p = (n + 1); - } - // Mail - if(n && p) - { - n = strchr(p, ';'); - if(n && n != p) strMail = strndup(p, (n - p)); - n = strchr(p, '\n'); - p = (n + 1); - } - if (!n) p = NULL; - - // Loadup contact - if(uin && strNick) - { - HANDLE hContact = gg_getcontact(gg, uin, 1, 1, strNick); -#ifdef DEBUGMODE - gg_netlog(gg, "gg_parsecontacts(): Found contact %d with nickname \"%s\".", uin, strNick); -#endif - // Write group - if(hContact && strGroup) - DBWriteContactSettingString(hContact, "CList", "Group", CreateGroup(strGroup)); - - // Write misc data - if(hContact && strFirstName) DBWriteContactSettingString(hContact, GG_PROTO, "FirstName", strFirstName); - if(hContact && strLastName) DBWriteContactSettingString(hContact, GG_PROTO, "LastName", strLastName); - if(hContact && strPhone) DBWriteContactSettingString(hContact, "UserInfo", "MyPhone0", strPhone); // Store now in User Info - if(hContact && strMail) DBWriteContactSettingString(hContact, "UserInfo", "Mye-mail0", strMail); // Store now in User Info - } - - // Release stuff - if(strFirstName) free(strFirstName); - if(strLastName) free(strLastName); - if(strNickname) free(strNickname); - if(strNick) free(strNick); - if(strPhone) free(strPhone); - if(strGroup) free(strGroup); - if(strUin) free(strUin); - if(strMail) free(strMail); - } -} - -////////////////////////////////////////////////////////// -// import from server -static INT_PTR gg_import_server(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - char *password; - uin_t uin; - DBVARIANT dbv; - - // Check if connected - if (!gg_isonline(gg)) - { - MessageBox(NULL, - Translate("You have to be connected before you can import/export contacts from/to server."), - GG_PROTONAME, MB_OK | MB_ICONSTOP - ); - return 0; - } - - // Readup password - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) - { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - password = _strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - else return 0; - - if (!(uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0))) - return 0; - - // Making contacts list - EnterCriticalSection(&gg->sess_mutex); - if (gg_userlist_request(gg->sess, GG_USERLIST_GET, NULL) == -1) - { - char error[128]; - LeaveCriticalSection(&gg->sess_mutex); - mir_snprintf(error, sizeof(error), Translate("List cannot be imported because of error:\n\t%s"), strerror(errno)); - MessageBox( - NULL, - error, - GG_PROTONAME, - MB_OK | MB_ICONSTOP - ); - gg_netlog(gg, "gg_import_server(): Cannot import list because of \"%s\".", strerror(errno)); - } - LeaveCriticalSection(&gg->sess_mutex); - free(password); - - return 0; -} - -////////////////////////////////////////////////////////// -// remove from server -static INT_PTR gg_remove_server(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - char *password; - uin_t uin; - DBVARIANT dbv; - - // Check if connected - if (!gg_isonline(gg)) - { - MessageBox(NULL, - Translate("You have to be connected before you can import/export contacts from/to server."), - GG_PROTONAME, MB_OK | MB_ICONSTOP - ); - return 0; - } - - // Readup password - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) - { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - password = _strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - else return 0; - - if (!(uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0))) - return 0; - - // Making contacts list - EnterCriticalSection(&gg->sess_mutex); - if (gg_userlist_request(gg->sess, GG_USERLIST_PUT, NULL) == -1) - { - char error[128]; - LeaveCriticalSection(&gg->sess_mutex); - mir_snprintf(error, sizeof(error), Translate("List cannot be removeed because of error:\n\t%s"), strerror(errno)); - MessageBox( - NULL, - error, - GG_PROTONAME, - MB_OK | MB_ICONSTOP - ); - gg_netlog(gg, "gg_remove_server(): Cannot remove list because of \"%s\".", strerror(errno)); - } - LeaveCriticalSection(&gg->sess_mutex); - - // Set list removal - gg->list_remove = TRUE; - free(password); - - return 0; -} - -static INT_PTR gg_import_text(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - char str[MAX_PATH] = "\0"; - OPENFILENAME ofn = {0}; - char filter[512], *pfilter; - struct _stat st; - FILE *f; - - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - strncpy(filter, Translate("Text files"), sizeof(filter)); - strncat(filter, " (*.txt)", sizeof(filter) - strlen(filter)); - pfilter = filter + strlen(filter) + 1; - if(pfilter >= filter + sizeof(filter)) return 0; - strncpy(pfilter, "*.TXT", sizeof(filter) - (pfilter - filter)); - pfilter = pfilter + strlen(pfilter) + 1; - if(pfilter >= filter + sizeof(filter)) return 0; - strncpy(pfilter, Translate("All Files"), sizeof(filter) - (pfilter - filter)); - strncat(pfilter, " (*)", sizeof(filter) - (pfilter - filter) - strlen(pfilter)); - pfilter = pfilter + strlen(pfilter) + 1; - if(pfilter >= filter + sizeof(filter)) return 0; - strncpy(pfilter, "*", sizeof(filter) - (pfilter - filter)); - pfilter = pfilter + strlen(pfilter) + 1; - if(pfilter >= filter + sizeof(filter)) return 0; - *pfilter = '\0'; - ofn.lpstrFilter = filter; - ofn.lpstrFile = str; - ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; - ofn.nMaxFile = sizeof(str); - ofn.nMaxFileTitle = MAX_PATH; - ofn.lpstrDefExt = "txt"; - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_import_text()"); -#endif - if (!GetOpenFileName(&ofn)) return 0; - - f = fopen(str, "r"); - _stat(str, &st); - - if(f && st.st_size) - { - char *contacts = malloc(st.st_size * sizeof(char)); - fread(contacts, sizeof(char), st.st_size, f); - fclose(f); - gg_parsecontacts(gg, contacts); - free(contacts); - - MessageBox( - NULL, - Translate("List import successful."), - GG_PROTONAME, - MB_OK | MB_ICONINFORMATION - ); - } - else - { - char error[128]; - mir_snprintf(error, sizeof(error), Translate("List cannot be imported from file \"%s\" because of error:\n\t%s"), str, strerror(errno)); - MessageBox( - NULL, - error, - GG_PROTONAME, - MB_OK | MB_ICONSTOP - ); - gg_netlog(gg, "gg_import_text(): Cannot import list from file \"%s\" because of \"%s\".", str, strerror(errno)); - } - - return 0; -} - -static INT_PTR gg_export_text(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - char str[MAX_PATH]; - OPENFILENAME ofn = {0}; - char filter[512], *pfilter; - FILE *f; - - strncpy(str, Translate("contacts"), sizeof(str)); - strncat(str, ".txt", sizeof(str) - strlen(str)); - - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - strncpy(filter, Translate("Text files"), sizeof(filter)); - strncat(filter, " (*.txt)", sizeof(filter) - strlen(filter)); - pfilter = filter + strlen(filter) + 1; - if(pfilter >= filter + sizeof(filter)) return 0; - strncpy(pfilter, "*.TXT", sizeof(filter) - (pfilter - filter)); - pfilter = pfilter + strlen(pfilter) + 1; - if(pfilter >= filter + sizeof(filter)) return 0; - strncpy(pfilter, Translate("All Files"), sizeof(filter) - (pfilter - filter)); - strncat(pfilter, " (*)", sizeof(filter) - (pfilter - filter) - strlen(pfilter)); - pfilter = pfilter + strlen(pfilter) + 1; - if(pfilter >= filter + sizeof(filter)) return 0; - strncpy(pfilter, "*", sizeof(filter) - (pfilter - filter)); - pfilter = pfilter + strlen(pfilter) + 1; - if(pfilter >= filter + sizeof(filter)) return 0; - *pfilter = '\0'; - ofn.lpstrFilter = filter; - ofn.lpstrFile = str; - ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; - ofn.nMaxFile = sizeof(str); - ofn.nMaxFileTitle = MAX_PATH; - ofn.lpstrDefExt = "txt"; - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_export_text(%s).", str); -#endif - if (!GetSaveFileName(&ofn)) return 0; - - if(f = fopen(str, "w")) - { - char *contacts = gg_makecontacts(gg, 0); - fwrite(contacts, sizeof(char), strlen(contacts), f); - fclose(f); - free(contacts); - - MessageBox( - NULL, - Translate("List export successful."), - GG_PROTONAME, - MB_OK | MB_ICONINFORMATION - ); - } - else - { - char error[128]; - mir_snprintf(error, sizeof(error), Translate("List cannot be exported to file \"%s\" because of error:\n\t%s"), str, strerror(errno)); - MessageBox( - NULL, - error, - GG_PROTONAME, - MB_OK | MB_ICONSTOP - ); - gg_netlog(gg, "gg_import_text(): Cannot export list to file \"%s\" because of \"%s\".", str, strerror(errno)); - } - - return 0; -} - -////////////////////////////////////////////////////////// -// export to server -static INT_PTR gg_export_server(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - char *password, *contacts; - uin_t uin; - DBVARIANT dbv; - - // Check if connected - if (!gg_isonline(gg)) - { - MessageBox(NULL, - Translate("You have to be connected before you can import/export contacts from/to server."), - GG_PROTONAME, MB_OK | MB_ICONSTOP - ); - return 0; - } - - // Readup password - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) - { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - password = _strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - else return 0; - - if (!(uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0))) - return 0; - - // Making contacts list - contacts = gg_makecontacts(gg, 1); - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_userlist_request(%s).", contacts); -#endif - - EnterCriticalSection(&gg->sess_mutex); - if (gg_userlist_request(gg->sess, GG_USERLIST_PUT, contacts) == -1) - { - char error[128]; - LeaveCriticalSection(&gg->sess_mutex); - mir_snprintf(error, sizeof(error), Translate("List cannot be exported because of error:\n\t%s"), strerror(errno)); - MessageBox( - NULL, - error, - GG_PROTONAME, - MB_OK | MB_ICONSTOP - ); - gg_netlog(gg, "gg_export_server(): Cannot export list because of \"%s\".", strerror(errno)); - } - LeaveCriticalSection(&gg->sess_mutex); - - // Set list removal - gg->list_remove = FALSE; - free(contacts); - free(password); - - return 0; -} - -////////////////////////////////////////////////////////// -// Import menus and stuff -void gg_import_init(GGPROTO *gg, HGENMENU hRoot) -{ - CLISTMENUITEM mi = {0}; - char service[64]; - - mi.cbSize = sizeof(mi); - mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE; - mi.hParentMenu = hRoot; - - // Import from server item - mir_snprintf(service, sizeof(service), GGS_IMPORT_SERVER, GG_PROTO); - CreateProtoServiceFunction(service, gg_import_server, gg); - mi.position = 2000500001; - mi.icolibItem = GetIconHandle(IDI_IMPORT_SERVER); - mi.pszName = LPGEN("Import List From &Server"); - mi.pszService = service; - gg->hMainMenu[2] = Menu_AddProtoMenuItem(&mi); - - // Import from textfile - mir_snprintf(service, sizeof(service), GGS_IMPORT_TEXT, GG_PROTO); - CreateProtoServiceFunction(service, gg_import_text, gg); - mi.position = 2000500002; - mi.icolibItem = GetIconHandle(IDI_IMPORT_TEXT); - mi.pszName = LPGEN("Import List From &Text File..."); - mi.pszService = service; - gg->hMainMenu[3] = Menu_AddProtoMenuItem(&mi); - - // Remove from server - mir_snprintf(service, sizeof(service), GGS_REMOVE_SERVER, GG_PROTO); - CreateProtoServiceFunction(service, gg_remove_server, gg); - mi.position = 2000500003; - mi.icolibItem = GetIconHandle(IDI_REMOVE_SERVER); - mi.pszName = LPGEN("&Remove List From Server"); - mi.pszService = service; - gg->hMainMenu[4] = Menu_AddProtoMenuItem(&mi); - - // Export to server - mir_snprintf(service, sizeof(service), GGS_EXPORT_SERVER, GG_PROTO); - CreateProtoServiceFunction(service, gg_export_server, gg); - mi.position = 2005000001; - mi.icolibItem = GetIconHandle(IDI_EXPORT_SERVER); - mi.pszName = LPGEN("Export List To &Server"); - mi.pszService = service; - gg->hMainMenu[5] = Menu_AddProtoMenuItem(&mi); - - // Export to textfile - mir_snprintf(service, sizeof(service), GGS_EXPORT_TEXT, GG_PROTO); - CreateProtoServiceFunction(service, gg_export_text, gg); - mi.position = 2005000002; - mi.icolibItem = GetIconHandle(IDI_EXPORT_TEXT); - mi.pszName = LPGEN("Export List To &Text File..."); - mi.pszService = service; - gg->hMainMenu[6] = Menu_AddProtoMenuItem(&mi); -} diff --git a/protocols/Gadu-Gadu/import.cpp b/protocols/Gadu-Gadu/import.cpp new file mode 100644 index 0000000000..c07e2c2006 --- /dev/null +++ b/protocols/Gadu-Gadu/import.cpp @@ -0,0 +1,651 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 Adam Strzelecki +// +// 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 "gg.h" + +//////////////////////////////////////////////////////////////////////////////// +// Checks if a group already exists in Miranda with +// the specified name. +// Returns 1 if a group with the name exists, returns 0 otherwise. +int GroupNameExists(const char *name) +{ + char idstr[33]; + DBVARIANT dbv; + int i; + + for (i = 0; ; i++) { + _itoa(i, idstr, 10); + if (db_get_s(NULL, "CListGroups", idstr, &dbv, DBVT_ASCIIZ)) break; + if (!strcmp(dbv.pszVal + 1, name)) { + DBFreeVariant(&dbv); + return 1; + } + DBFreeVariant(&dbv); + } + return 0; +} + + +//////////////////////////////////////////////////////////////////////////////// +// Creates a group with a specified name in the +// Miranda contact list. +// Returns proper group name + +char *CreateGroup(char *groupName) +{ + int groupId; + char groupIdStr[11]; + char groupName2[127]; + char *p; + DBVARIANT dbv; + + // Cleanup group name from weird characters + + // Skip first break + while(*groupName && *groupName == '\\') groupName++; + + p = strrchr(groupName, '\\'); + // Cleanup end + while(p && !(*(p + 1))) + { + *p = 0; + p = strrchr(groupName, '\\'); + } + // Create upper groups + if (p) + { + *p = 0; + CreateGroup(groupName); + *p = '\\'; + } + + // Is this a duplicate? + if (!GroupNameExists(groupName)) + { + lstrcpynA(groupName2 + 1, groupName, (int)strlen(groupName) + 1); + + // Find an unused id + for (groupId = 0; ; groupId++) { + _itoa(groupId, groupIdStr,10); + if (db_get_s(NULL, "CListGroups", groupIdStr, &dbv, DBVT_ASCIIZ)) + break; + DBFreeVariant(&dbv); + } + + groupName2[0] = 1|GROUPF_EXPANDED; // 1 is required so we never get '\0' + db_set_s(NULL, "CListGroups", groupIdStr, groupName2); + } + return groupName; +} + +char *gg_makecontacts(GGPROTO *gg, int cr) +{ + string_t s = string_init(NULL); + char *contacts; + + // Readup contacts + HANDLE hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while (hContact) + { + char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && !strcmp(szProto, gg->m_szModuleName) && !db_get_b(hContact, gg->m_szModuleName, "ChatRoom", 0)) + { + DBVARIANT dbv; + + // Readup FirstName + if (!db_get_s(hContact, gg->m_szModuleName, "FirstName", &dbv, DBVT_ASCIIZ)) + { + string_append(s, dbv.pszVal); + DBFreeVariant(&dbv); + } + string_append_c(s, ';'); + // Readup LastName + if (!db_get_s(hContact, gg->m_szModuleName, "LastName", &dbv, DBVT_ASCIIZ)) + { + string_append(s, dbv.pszVal); + DBFreeVariant(&dbv); + } + string_append_c(s, ';'); + + // Readup Nick + if (!db_get_s(hContact, "CList", "MyHandle", &dbv, DBVT_ASCIIZ) || !db_get_s(hContact, gg->m_szModuleName, GG_KEY_NICK, &dbv, DBVT_ASCIIZ)) + { + DBVARIANT dbv2; + if (!db_get_s(hContact, gg->m_szModuleName, "NickName", &dbv2, DBVT_ASCIIZ)) + { + string_append(s, dbv2.pszVal); + DBFreeVariant(&dbv2); + } + else + string_append(s, dbv.pszVal); + string_append_c(s, ';'); + string_append(s, dbv.pszVal); + DBFreeVariant(&dbv); + } + else + string_append_c(s, ';'); + string_append_c(s, ';'); + + // Readup Phone (fixed: uses stored editable phones) + if (!db_get_s(hContact, "UserInfo", "MyPhone0", &dbv, DBVT_ASCIIZ)) + { + // Remove SMS postfix + char *sms = strstr(dbv.pszVal, " SMS"); + if (sms) *sms = 0; + + string_append(s, dbv.pszVal); + DBFreeVariant(&dbv); + } + string_append_c(s, ';'); + // Readup Group + if (!db_get_s(hContact, "CList", "Group", &dbv, DBVT_ASCIIZ)) + { + string_append(s, dbv.pszVal); + DBFreeVariant(&dbv); + } + string_append_c(s, ';'); + // Readup Uin + string_append(s, ditoa(db_get_b(hContact, gg->m_szModuleName, GG_KEY_UIN, 0))); + string_append_c(s, ';'); + // Readup Mail (fixed: uses stored editable mails) + if (!db_get_s(hContact, "UserInfo", "Mye-mail0", &dbv, DBVT_ASCIIZ)) + { + string_append(s, dbv.pszVal); + DBFreeVariant(&dbv); + } + if (cr) + string_append(s, ";0;;0;\r\n"); + else + string_append(s, ";0;;0;\n"); + } + hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); + } + + contacts = string_free(s, 0); + +#ifdef DEBUGMODE + gg->netlog("gg_makecontacts(): \n%s", contacts); +#endif + + return contacts; +} + +char *strndup(char *str, int c) +{ + char *ret = (char*)malloc(c + 1); + ret[c] = 0; + strncpy(ret, str, c); + return ret; +} + +void GGPROTO::parsecontacts(char *contacts) +{ + char *p = strchr(contacts, ':'), *n; + char *strFirstName, *strLastName, *strNickname, *strNick, *strPhone, *strGroup, *strUin, *strMail; + uin_t uin; + + // Skip to proper data + if (p && p < strchr(contacts, ';')) p++; + else p = contacts; + + while(p) + { + // Processing line + strFirstName = strLastName = strNickname = strNick = strPhone = strGroup = strUin = strMail = NULL; + uin = 0; + + // FirstName + if (p) + { + n = strchr(p, ';'); + if (n && n != p) strFirstName = strndup(p, (n - p)); + p = (n + 1); + } + // LastName + if (n && p) + { + n = strchr(p, ';'); + if (n && n != p) strLastName = strndup(p, (n - p)); + p = (n + 1); + } + // Nickname + if (n && p) + { + n = strchr(p, ';'); + if (n && n != p) strNickname = strndup(p, (n - p)); + p = (n + 1); + } + // Nick + if (n && p) + { + n = strchr(p, ';'); + if (n && n != p) strNick = strndup(p, (n - p)); + p = (n + 1); + } + // Phone + if (n && p) + { + n = strchr(p, ';'); + if (n && n != p) + { + strPhone = (char*)malloc((n - p) + 5); + strncpy(strPhone, p, (n - p)); + strcpy((strPhone + (n - p)), " SMS"); // Add SMS postfix + } + p = (n + 1); + } + // Group + if (n && p) + { + n = strchr(p, ';'); + if (n && n != p) strGroup = strndup(p, (n - p)); + p = (n + 1); + } + // Uin + if (n && p) + { + n = strchr(p, ';'); + if (n && n != p) + { + strUin = strndup(p, (n - p)); + uin = atoi(strUin); + } + p = (n + 1); + } + // Mail + if (n && p) + { + n = strchr(p, ';'); + if (n && n != p) strMail = strndup(p, (n - p)); + n = strchr(p, '\n'); + p = (n + 1); + } + if (!n) p = NULL; + + // Loadup contact + if (uin && strNick) + { + HANDLE hContact = getcontact(uin, 1, 1, _A2T(strNick)); +#ifdef DEBUGMODE + netlog("gg_parsecontacts(): Found contact %d with nickname \"%s\".", uin, strNick); +#endif + // Write group + if (hContact && strGroup) + db_set_s(hContact, "CList", "Group", CreateGroup(strGroup)); + + // Write misc data + if (hContact && strFirstName) db_set_s(hContact, m_szModuleName, "FirstName", strFirstName); + if (hContact && strLastName) db_set_s(hContact, m_szModuleName, "LastName", strLastName); + if (hContact && strPhone) db_set_s(hContact, "UserInfo", "MyPhone0", strPhone); // Store now in User Info + if (hContact && strMail) db_set_s(hContact, "UserInfo", "Mye-mail0", strMail); // Store now in User Info + } + + // Release stuff + if (strFirstName) free(strFirstName); + if (strLastName) free(strLastName); + if (strNickname) free(strNickname); + if (strNick) free(strNick); + if (strPhone) free(strPhone); + if (strGroup) free(strGroup); + if (strUin) free(strUin); + if (strMail) free(strMail); + } +} + +////////////////////////////////////////////////////////// +// import from server + +INT_PTR GGPROTO::import_server(WPARAM wParam, LPARAM lParam) +{ + char *password; + uin_t uin; + DBVARIANT dbv; + + // Check if connected + if (!isonline()) + { + MessageBox(NULL, + TranslateT("You have to be connected before you can import/export contacts from/to server."), + m_tszUserName, MB_OK | MB_ICONSTOP + ); + return 0; + } + + // Readup password + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) + { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + password = _strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + else return 0; + + if (!(uin = db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0))) + return 0; + + // Making contacts list + EnterCriticalSection(&sess_mutex); + if (gg_userlist_request(sess, GG_USERLIST_GET, NULL) == -1) + { + TCHAR error[128]; + LeaveCriticalSection(&sess_mutex); + mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be imported because of error:\n\t%s"), _tcserror(errno)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + netlog("gg_import_server(): Cannot import list because of \"%s\".", strerror(errno)); + } + LeaveCriticalSection(&sess_mutex); + free(password); + + return 0; +} + +////////////////////////////////////////////////////////// +// remove from server + +INT_PTR GGPROTO::remove_server(WPARAM wParam, LPARAM lParam) +{ + char *password; + uin_t uin; + DBVARIANT dbv; + + // Check if connected + if (!isonline()) + { + MessageBox(NULL, + TranslateT("You have to be connected before you can import/export contacts from/to server."), + m_tszUserName, MB_OK | MB_ICONSTOP + ); + return 0; + } + + // Readup password + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) + { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + password = _strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + else return 0; + + if (!(uin = db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0))) + return 0; + + // Making contacts list + EnterCriticalSection(&sess_mutex); + if (gg_userlist_request(sess, GG_USERLIST_PUT, NULL) == -1) + { + TCHAR error[128]; + LeaveCriticalSection(&sess_mutex); + mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be removeed because of error:\n\t%s"), strerror(errno)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + netlog("gg_remove_server(): Cannot remove list because of \"%s\".", strerror(errno)); + } + LeaveCriticalSection(&sess_mutex); + + // Set list removal + is_list_remove = TRUE; + free(password); + + return 0; +} + +INT_PTR GGPROTO::import_text(WPARAM wParam, LPARAM lParam) +{ + TCHAR str[MAX_PATH]; + TCHAR filter[512], *pfilter; + struct _stat st; + FILE *f; + + OPENFILENAME ofn = {0}; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + _tcsncpy(filter, TranslateT("Text files"), SIZEOF(filter)); + _tcsncat(filter, _T(" (*.txt)"), SIZEOF(filter) - _tcslen(filter)); + pfilter = filter + _tcslen(filter) + 1; + if (pfilter >= filter + SIZEOF(filter)) + return 0; + + _tcsncpy(pfilter, _T("*.TXT"), SIZEOF(filter) - (pfilter - filter)); + pfilter = pfilter + _tcslen(pfilter) + 1; + if (pfilter >= filter + SIZEOF(filter)) + return 0; + _tcsncpy(pfilter, TranslateT("All Files"), SIZEOF(filter) - (pfilter - filter)); + _tcsncat(pfilter, _T(" (*)"), SIZEOF(filter) - (pfilter - filter) - _tcslen(pfilter)); + pfilter = pfilter + _tcslen(pfilter) + 1; + + if (pfilter >= filter + SIZEOF(filter)) + return 0; + + _tcsncpy(pfilter, _T("*"), SIZEOF(filter) - (pfilter - filter)); + pfilter = pfilter + _tcslen(pfilter) + 1; + if (pfilter >= filter + SIZEOF(filter)) + return 0; + + *pfilter = '\0'; + ofn.lpstrFilter = filter; + ofn.lpstrFile = str; + ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.nMaxFile = sizeof(str); + ofn.nMaxFileTitle = MAX_PATH; + ofn.lpstrDefExt = _T("txt"); + +#ifdef DEBUGMODE + netlog("gg_import_text()"); +#endif + if (!GetOpenFileName(&ofn)) return 0; + + f = _tfopen(str, _T("r")); + _tstat(str, &st); + + if (f && st.st_size) + { + char *contacts = (char*)malloc(st.st_size * sizeof(char)); + fread(contacts, sizeof(char), st.st_size, f); + fclose(f); + parsecontacts(contacts); + free(contacts); + + MessageBox(NULL, TranslateT("List import successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + else + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be imported from file \"%s\" because of error:\n\t%s"), str, _tcserror(errno)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + netlog("gg_import_text(): Cannot import list from file \"%S\" because of \"%s\".", str, strerror(errno)); + } + + return 0; +} + +INT_PTR GGPROTO::export_text(WPARAM wParam, LPARAM lParam) +{ + TCHAR str[MAX_PATH]; + OPENFILENAME ofn = {0}; + TCHAR filter[512], *pfilter; + FILE *f; + + _tcsncpy(str, TranslateT("contacts"), sizeof(str)); + _tcsncat(str, _T(".txt"), sizeof(str) - _tcslen(str)); + + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + _tcsncpy(filter, TranslateT("Text files"), SIZEOF(filter)); + _tcsncat(filter, _T(" (*.txt)"), SIZEOF(filter) - _tcslen(filter)); + pfilter = filter + _tcslen(filter) + 1; + if (pfilter >= filter + SIZEOF(filter)) + return 0; + _tcsncpy(pfilter, _T("*.TXT"), SIZEOF(filter) - (pfilter - filter)); + pfilter = pfilter + _tcslen(pfilter) + 1; + if (pfilter >= filter + SIZEOF(filter)) + return 0; + _tcsncpy(pfilter, TranslateT("All Files"), SIZEOF(filter) - (pfilter - filter)); + _tcsncat(pfilter, _T(" (*)"), SIZEOF(filter) - (pfilter - filter) - _tcslen(pfilter)); + pfilter = pfilter + _tcslen(pfilter) + 1; + if (pfilter >= filter + SIZEOF(filter)) + return 0; + _tcsncpy(pfilter, _T("*"), SIZEOF(filter) - (pfilter - filter)); + pfilter = pfilter + _tcslen(pfilter) + 1; + if (pfilter >= filter + SIZEOF(filter)) + return 0; + *pfilter = '\0'; + ofn.lpstrFilter = filter; + ofn.lpstrFile = str; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; + ofn.nMaxFile = sizeof(str); + ofn.nMaxFileTitle = MAX_PATH; + ofn.lpstrDefExt = _T("txt"); + +#ifdef DEBUGMODE + netlog("gg_export_text(%s).", str); +#endif + if (!GetSaveFileName(&ofn)) return 0; + + if (f = _tfopen(str, _T("w"))) { + char *contacts = gg_makecontacts(this, 0); + fwrite(contacts, sizeof(char), strlen(contacts), f); + fclose(f); + free(contacts); + + MessageBox(NULL, TranslateT("List export successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + else + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be exported to file \"%s\" because of error:\n\t%s"), str, _tcserror(errno)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + netlog("gg_import_text(): Cannot export list to file \"%s\" because of \"%s\".", str, strerror(errno)); + } + + return 0; +} + +////////////////////////////////////////////////////////// +// export to server + +INT_PTR GGPROTO::export_server(WPARAM wParam, LPARAM lParam) +{ + char *password, *contacts; + uin_t uin; + DBVARIANT dbv; + + // Check if connected + if (!isonline()) + { + MessageBox(NULL, + TranslateT("You have to be connected before you can import/export contacts from/to server."), + m_tszUserName, MB_OK | MB_ICONSTOP + ); + return 0; + } + + // Readup password + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) + { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + password = _strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + else return 0; + + if (!(uin = db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0))) + return 0; + + // Making contacts list + contacts = gg_makecontacts(this, 1); + +#ifdef DEBUGMODE + netlog("gg_userlist_request(%s).", contacts); +#endif + + EnterCriticalSection(&sess_mutex); + if (gg_userlist_request(sess, GG_USERLIST_PUT, contacts) == -1) + { + TCHAR error[128]; + LeaveCriticalSection(&sess_mutex); + mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be exported because of error:\n\t%s"), _tcserror(errno)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + netlog("gg_export_server(): Cannot export list because of \"%s\".", strerror(errno)); + } + LeaveCriticalSection(&sess_mutex); + + // Set list removal + is_list_remove = FALSE; + free(contacts); + free(password); + + return 0; +} + +////////////////////////////////////////////////////////// +// Import menus and stuff + +void GGPROTO::import_init(HGENMENU hRoot) +{ + CLISTMENUITEM mi = {0}; + char service[64]; + + mi.cbSize = sizeof(mi); + mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE; + mi.hParentMenu = hRoot; + + // Import from server item + mir_snprintf(service, sizeof(service), GGS_IMPORT_SERVER, m_szModuleName); + createProtoService(service, &GGPROTO::import_server); + mi.position = 2000500001; + mi.icolibItem = GetIconHandle(IDI_IMPORT_SERVER); + mi.pszName = LPGEN("Import List From &Server"); + mi.pszService = service; + hMainMenu[2] = Menu_AddProtoMenuItem(&mi); + + // Import from textfile + mir_snprintf(service, sizeof(service), GGS_IMPORT_TEXT, m_szModuleName); + createProtoService(service, &GGPROTO::import_text); + mi.position = 2000500002; + mi.icolibItem = GetIconHandle(IDI_IMPORT_TEXT); + mi.pszName = LPGEN("Import List From &Text File..."); + mi.pszService = service; + hMainMenu[3] = Menu_AddProtoMenuItem(&mi); + + // Remove from server + mir_snprintf(service, sizeof(service), GGS_REMOVE_SERVER, m_szModuleName); + createProtoService(service, &GGPROTO::remove_server); + mi.position = 2000500003; + mi.icolibItem = GetIconHandle(IDI_REMOVE_SERVER); + mi.pszName = LPGEN("&Remove List From Server"); + mi.pszService = service; + hMainMenu[4] = Menu_AddProtoMenuItem(&mi); + + // Export to server + mir_snprintf(service, sizeof(service), GGS_EXPORT_SERVER, m_szModuleName); + createProtoService(service, &GGPROTO::export_server); + mi.position = 2005000001; + mi.icolibItem = GetIconHandle(IDI_EXPORT_SERVER); + mi.pszName = LPGEN("Export List To &Server"); + mi.pszService = service; + hMainMenu[5] = Menu_AddProtoMenuItem(&mi); + + // Export to textfile + mir_snprintf(service, sizeof(service), GGS_EXPORT_TEXT, m_szModuleName); + createProtoService(service, &GGPROTO::export_text); + mi.position = 2005000002; + mi.icolibItem = GetIconHandle(IDI_EXPORT_TEXT); + mi.pszName = LPGEN("Export List To &Text File..."); + mi.pszService = service; + hMainMenu[6] = Menu_AddProtoMenuItem(&mi); +} diff --git a/protocols/Gadu-Gadu/keepalive.c b/protocols/Gadu-Gadu/keepalive.c deleted file mode 100644 index a0c7ddb032..0000000000 --- a/protocols/Gadu-Gadu/keepalive.c +++ /dev/null @@ -1,92 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 Adam Strzelecki -// -// 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 "gg.h" - -/* NOTE: Eventhough SetTimer seems to support UINT_PTR for idEvent, it seems that TimerProc - * does not get full pointer but just 2 byte lower bytes. - */ -#define MAX_TIMERS 8 -GGPROTO *g_timers[MAX_TIMERS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; - -static VOID CALLBACK gg_keepalive(HWND hwnd, UINT message, UINT_PTR idEvent, DWORD dwTime) -{ - int i; - - //Search for GGPROTO* context - for(i = 0; i < MAX_TIMERS; i++) - if(g_timers[i]->timer == idEvent) - break; - - if(i < MAX_TIMERS) - { - GGPROTO *gg = g_timers[i]; - if (gg_isonline(gg)) - { - #ifdef DEBUGMODE - gg_netlog(gg, "Sending keep-alive"); - #endif - EnterCriticalSection(&gg->sess_mutex); - gg_ping(gg->sess); - LeaveCriticalSection(&gg->sess_mutex); - } - } -} - -void gg_keepalive_init(GGPROTO *gg) -{ - if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_KEEPALIVE, GG_KEYDEF_KEEPALIVE)) - { - int i; - for(i = 0; i < MAX_TIMERS && g_timers[i] != NULL; i++); - if(i < MAX_TIMERS) - { - #ifdef DEBUGMODE - gg_netlog(gg, "gg_keepalive_init(): Initializing Timer %d", i); - #endif - gg->timer = SetTimer(NULL, 0, 1000 * 30, gg_keepalive); - g_timers[i] = gg; - } - } -} - -void gg_keepalive_destroy(GGPROTO *gg) -{ -#ifdef DEBUGMODE - gg_netlog(gg, "gg_destroykeepalive(): Killing Timer"); -#endif - if (gg->timer) - { - int i; - KillTimer(NULL, gg->timer); - for(i = 0; i < MAX_TIMERS; i++) - if(g_timers[i] == gg) { - g_timers[i] = NULL; - break; - } - gg->timer = 0; -#ifdef DEBUGMODE - gg_netlog(gg, "gg_destroykeepalive(): Killed Timer %d", i); -#endif - } -#ifdef DEBUGMODE - gg_netlog(gg, "gg_destroykeepalive(): End"); -#endif -} diff --git a/protocols/Gadu-Gadu/keepalive.cpp b/protocols/Gadu-Gadu/keepalive.cpp new file mode 100644 index 0000000000..02b661c222 --- /dev/null +++ b/protocols/Gadu-Gadu/keepalive.cpp @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 Adam Strzelecki +// +// 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 "gg.h" + +/* NOTE: Eventhough SetTimer seems to support UINT_PTR for idEvent, it seems that TimerProc + * does not get full pointer but just 2 byte lower bytes. + */ +#define MAX_TIMERS 8 +GGPROTO *g_timers[MAX_TIMERS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + +static VOID CALLBACK gg_keepalive(HWND hwnd, UINT message, UINT_PTR idEvent, DWORD dwTime) +{ + int i; + + //Search for GGPROTO* context + for(i = 0; i < MAX_TIMERS; i++) + if (g_timers[i]->timer == idEvent) + break; + + if (i < MAX_TIMERS) + { + GGPROTO *gg = g_timers[i]; + if (gg->isonline()) + { + #ifdef DEBUGMODE + gg->netlog("Sending keep-alive"); + #endif + EnterCriticalSection(&gg->sess_mutex); + gg_ping(gg->sess); + LeaveCriticalSection(&gg->sess_mutex); + } + } +} + +void GGPROTO::keepalive_init() +{ + if (db_get_b(NULL, m_szModuleName, GG_KEY_KEEPALIVE, GG_KEYDEF_KEEPALIVE)) + { + int i; + for(i = 0; i < MAX_TIMERS && g_timers[i] != NULL; i++); + if (i < MAX_TIMERS) + { + #ifdef DEBUGMODE + netlog("gg_keepalive_init(): Initializing Timer %d", i); + #endif + timer = SetTimer(NULL, 0, 1000 * 30, gg_keepalive); + g_timers[i] = this; + } + } +} + +void GGPROTO::keepalive_destroy() +{ +#ifdef DEBUGMODE + netlog("gg_destroykeepalive(): Killing Timer"); +#endif + if (timer) + { + int i; + KillTimer(NULL, timer); + for(i = 0; i < MAX_TIMERS; i++) + if (g_timers[i] == this) { + g_timers[i] = NULL; + break; + } + timer = 0; +#ifdef DEBUGMODE + netlog("gg_destroykeepalive(): Killed Timer %d", i); +#endif + } +#ifdef DEBUGMODE + netlog("gg_destroykeepalive(): End"); +#endif +} diff --git a/protocols/Gadu-Gadu/libgadu/libgadu.h b/protocols/Gadu-Gadu/libgadu/libgadu.h index 08d8bb6912..d273d998e1 100644 --- a/protocols/Gadu-Gadu/libgadu/libgadu.h +++ b/protocols/Gadu-Gadu/libgadu/libgadu.h @@ -690,7 +690,7 @@ int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, int gg_ping(struct gg_session *sess); int gg_userlist_request(struct gg_session *sess, char type, const char *request); int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32); -int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size); +int gg_image_reply(struct gg_session *sess, uin_t recipient, const TCHAR *filename, const char *image, int size); int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length); uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len); @@ -848,7 +848,7 @@ struct gg_event_msg { uin_t sender; /**< Numer nadawcy/odbiorcy */ int msgclass; /**< Klasa wiadomoĹ›ci */ time_t time; /**< Czas nadania */ - unsigned char *message; /**< Treść wiadomoĹ›ci */ + char *message; /**< Treść wiadomoĹ›ci */ int recipients_count; /**< Liczba odbiorcĂłw konferencji */ uin_t *recipients; /**< Odbiorcy konferencji */ diff --git a/protocols/Gadu-Gadu/libgadu/pthread.c b/protocols/Gadu-Gadu/libgadu/pthread.c index 2496bb4964..9a8988a358 100644 --- a/protocols/Gadu-Gadu/libgadu/pthread.c +++ b/protocols/Gadu-Gadu/libgadu/pthread.c @@ -44,7 +44,7 @@ int pthread_detach(pthread_t *tid) /* wait for thread termination */ int pthread_join(pthread_t *tid, void **value_ptr) { - if(tid->dwThreadId == GetCurrentThreadId()) + if (tid->dwThreadId == GetCurrentThreadId()) return 35 /*EDEADLK*/; WaitForSingleObject(tid->hThread, INFINITE); diff --git a/protocols/Gadu-Gadu/links.c b/protocols/Gadu-Gadu/links.c deleted file mode 100644 index 6141358475..0000000000 --- a/protocols/Gadu-Gadu/links.c +++ /dev/null @@ -1,173 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2009-2012 Bartosz Białek -// -// 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 "gg.h" -#include "m_assocmgr.h" - -////////////////////////////////////////////////////////// -// File Association Manager support - -#define GGS_PARSELINK "%s/ParseLink" -#define GGS_MENUCHOOSE "%s/MenuChoose" - -static HANDLE hInstanceMenu; -static HANDLE hServiceMenuChoose; -static HANDLE hServiceParseLink; - -static INT_PTR gg_menuchoose(WPARAM wParam, LPARAM lParam) -{ - if (lParam) - *(void**)lParam = (void*)wParam; - return 0; -} - -static INT_PTR gg_parselink(WPARAM wParam, LPARAM lParam) -{ - char *arg = (char*)lParam; - list_t l = g_Instances; - GGPROTO *gg = NULL; - uin_t uin; - CLISTMENUITEM mi = {0}; - int items = 0; - - if (list_count(l) == 0) - return 0; - - if (arg == NULL) - return 1; - - arg = strchr(arg, ':'); - - if (arg == NULL) - return 1; - - for (++arg; *arg == '/'; ++arg); - uin = atoi(arg); - - if (!uin) - return 1; - - for (mi.cbSize = sizeof(mi); l; l = l->next) - { - GGPROTO *gginst = l->data; - - mi.flags = CMIM_FLAGS; - if (gginst->proto.m_iStatus > ID_STATUS_OFFLINE) - { - ++items; - gg = l->data; - mi.flags |= CMIM_ICON; - mi.hIcon = LoadSkinnedProtoIcon(GG_PROTO, gg->proto.m_iStatus); - } - else - { - mi.flags |= CMIF_HIDDEN; - mi.hIcon = NULL; - } - - CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)gginst->hInstanceMenuItem, (LPARAM)&mi); - if (mi.hIcon) - CallService(MS_SKIN2_RELEASEICON, (WPARAM)mi.hIcon, 0); - } - - if (items > 1) - { - ListParam param = {0}; - HMENU hMenu = CreatePopupMenu(); - POINT pt; - int cmd = 0; - - param.MenuObjectHandle = hInstanceMenu; - CallService(MO_BUILDMENU, (WPARAM)hMenu, (LPARAM)¶m); - - GetCursorPos(&pt); - cmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, pcli->hwndContactList, NULL); - DestroyMenu(hMenu); - - if (cmd) - CallService(MO_PROCESSCOMMANDBYMENUIDENT, cmd, (LPARAM)&gg); - } - - if (gg == NULL) - return 0; - - if (ServiceExists(MS_MSG_SENDMESSAGE)) - { - HANDLE hContact = gg_getcontact(gg, uin, 1, 0, NULL); - if (hContact != NULL) - CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, 0); - } - - return 0; -} - -void gg_links_instancemenu_init() -{ - char service[MAXMODULELABELLENGTH]; - TMenuParam mnu = {0}; - TMO_MenuItem tmi = {0}; - - mir_snprintf(service, sizeof(service), GGS_MENUCHOOSE, GGDEF_PROTO); - hServiceMenuChoose = CreateServiceFunction(service, gg_menuchoose); - mnu.cbSize = sizeof(mnu); - mnu.name = "GGAccountChooser"; - mnu.ExecService = service; - hInstanceMenu = (HANDLE)CallService(MO_CREATENEWMENUOBJECT, 0, (LPARAM)&mnu); - - tmi.cbSize = sizeof(tmi); - tmi.flags = CMIF_ICONFROMICOLIB; - tmi.pszName = "Cancel"; - tmi.position = 9999999; - tmi.hIcolibItem = LoadSkinnedIconHandle(SKINICON_OTHER_DELETE); - CallService(MO_ADDNEWMENUITEM, (WPARAM)hInstanceMenu, (LPARAM)&tmi); -} - -void gg_links_init() -{ - if (ServiceExists(MS_ASSOCMGR_ADDNEWURLTYPE)) - { - char service[MAXMODULELABELLENGTH]; - - mir_snprintf(service, sizeof(service), GGS_PARSELINK, GGDEF_PROTO); - hServiceParseLink = CreateServiceFunction(service, gg_parselink); - AssocMgr_AddNewUrlType("gg:", Translate("Gadu-Gadu Link Protocol"), hInstance, IDI_GG, service, 0); - } -} - -void gg_links_destroy() -{ - DestroyServiceFunction(hServiceMenuChoose); - if (ServiceExists(MS_ASSOCMGR_ADDNEWURLTYPE)) - DestroyServiceFunction(hServiceParseLink); -} - -void gg_links_instance_init(GGPROTO *gg) -{ - if (ServiceExists(MS_ASSOCMGR_ADDNEWURLTYPE)) - { - TMO_MenuItem tmi = {0}; - tmi.cbSize = sizeof(tmi); - tmi.flags = CMIF_TCHAR; - tmi.ownerdata = gg; - tmi.position = list_count(g_Instances); - tmi.ptszName = GG_PROTONAME; - gg->hInstanceMenuItem = (HANDLE)CallService(MO_ADDNEWMENUITEM, (WPARAM)hInstanceMenu, (LPARAM)&tmi); - } -} diff --git a/protocols/Gadu-Gadu/links.cpp b/protocols/Gadu-Gadu/links.cpp new file mode 100644 index 0000000000..53770f0bcd --- /dev/null +++ b/protocols/Gadu-Gadu/links.cpp @@ -0,0 +1,173 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2009-2012 Bartosz Białek +// +// 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 "gg.h" +#include "m_assocmgr.h" + +////////////////////////////////////////////////////////// +// File Association Manager support + +#define GGS_PARSELINK "%s/ParseLink" +#define GGS_MENUCHOOSE "%s/MenuChoose" + +static HANDLE hInstanceMenu; +static HANDLE hServiceMenuChoose; +static HANDLE hServiceParseLink; + +static INT_PTR gg_menuchoose(WPARAM wParam, LPARAM lParam) +{ + if (lParam) + *(void**)lParam = (void*)wParam; + return 0; +} + +static INT_PTR gg_parselink(WPARAM wParam, LPARAM lParam) +{ + char *arg = (char*)lParam; + list_t l = g_Instances; + GGPROTO *gg = NULL; + uin_t uin; + CLISTMENUITEM mi = {0}; + int items = 0; + + if (list_count(l) == 0) + return 0; + + if (arg == NULL) + return 1; + + arg = strchr(arg, ':'); + + if (arg == NULL) + return 1; + + for (++arg; *arg == '/'; ++arg); + uin = atoi(arg); + + if (!uin) + return 1; + + for (mi.cbSize = sizeof(mi); l; l = l->next) + { + GGPROTO *gginst = (GGPROTO*)l->data; + + mi.flags = CMIM_FLAGS; + if (gginst->m_iStatus > ID_STATUS_OFFLINE) + { + ++items; + gg = (GGPROTO*)l->data; + mi.flags |= CMIM_ICON; + mi.hIcon = LoadSkinnedProtoIcon(gg->m_szModuleName, gg->m_iStatus); + } + else + { + mi.flags |= CMIF_HIDDEN; + mi.hIcon = NULL; + } + + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)gginst->hInstanceMenuItem, (LPARAM)&mi); + if (mi.hIcon) + CallService(MS_SKIN2_RELEASEICON, (WPARAM)mi.hIcon, 0); + } + + if (items > 1) + { + ListParam param = {0}; + HMENU hMenu = CreatePopupMenu(); + POINT pt; + int cmd = 0; + + param.MenuObjectHandle = hInstanceMenu; + CallService(MO_BUILDMENU, (WPARAM)hMenu, (LPARAM)¶m); + + GetCursorPos(&pt); + cmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, pcli->hwndContactList, NULL); + DestroyMenu(hMenu); + + if (cmd) + CallService(MO_PROCESSCOMMANDBYMENUIDENT, cmd, (LPARAM)&gg); + } + + if (gg == NULL) + return 0; + + if (ServiceExists(MS_MSG_SENDMESSAGE)) + { + HANDLE hContact = gg->getcontact(uin, 1, 0, NULL); + if (hContact != NULL) + CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, 0); + } + + return 0; +} + +void gg_links_instancemenu_init() +{ + char service[MAXMODULELABELLENGTH]; + TMenuParam mnu = {0}; + TMO_MenuItem tmi = {0}; + + mir_snprintf(service, sizeof(service), GGS_MENUCHOOSE, GGDEF_PROTO); + hServiceMenuChoose = CreateServiceFunction(service, gg_menuchoose); + mnu.cbSize = sizeof(mnu); + mnu.name = "GGAccountChooser"; + mnu.ExecService = service; + hInstanceMenu = (HANDLE)CallService(MO_CREATENEWMENUOBJECT, 0, (LPARAM)&mnu); + + tmi.cbSize = sizeof(tmi); + tmi.flags = CMIF_ICONFROMICOLIB; + tmi.pszName = "Cancel"; + tmi.position = 9999999; + tmi.hIcolibItem = LoadSkinnedIconHandle(SKINICON_OTHER_DELETE); + CallService(MO_ADDNEWMENUITEM, (WPARAM)hInstanceMenu, (LPARAM)&tmi); +} + +void gg_links_init() +{ + if (ServiceExists(MS_ASSOCMGR_ADDNEWURLTYPE)) + { + char service[MAXMODULELABELLENGTH]; + + mir_snprintf(service, sizeof(service), GGS_PARSELINK, GGDEF_PROTO); + hServiceParseLink = CreateServiceFunction(service, gg_parselink); + AssocMgr_AddNewUrlType("gg:", Translate("Gadu-Gadu Link Protocol"), hInstance, IDI_GG, service, 0); + } +} + +void gg_links_destroy() +{ + DestroyServiceFunction(hServiceMenuChoose); + if (ServiceExists(MS_ASSOCMGR_ADDNEWURLTYPE)) + DestroyServiceFunction(hServiceParseLink); +} + +void GGPROTO::links_instance_init() +{ + if (ServiceExists(MS_ASSOCMGR_ADDNEWURLTYPE)) + { + TMO_MenuItem tmi = {0}; + tmi.cbSize = sizeof(tmi); + tmi.flags = CMIF_TCHAR; + tmi.ownerdata = this; + tmi.position = list_count(g_Instances); + tmi.ptszName = m_tszUserName; + hInstanceMenuItem = (HANDLE)CallService(MO_ADDNEWMENUITEM, (WPARAM)hInstanceMenu, (LPARAM)&tmi); + } +} diff --git a/protocols/Gadu-Gadu/oauth.c b/protocols/Gadu-Gadu/oauth.c deleted file mode 100644 index 15f9e592fe..0000000000 --- a/protocols/Gadu-Gadu/oauth.c +++ /dev/null @@ -1,588 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2010 Bartosz Białek -// -// 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 "gg.h" -#include -#include -#include "protocol.h" - -////////////////////////////////////////////////////////// -// OAuth 1.0 implementation - -// Service Provider must accept the HTTP Authorization header - -// RSA-SHA1 signature method (see RFC 3447 section 8.2 -// and RSASSA-PKCS1-v1_5 algorithm) is unimplemented - -typedef struct -{ - char *name; - char *value; -} OAUTHPARAMETER; - -typedef enum -{ - HMACSHA1, - RSASHA1, - PLAINTEXT -} OAUTHSIGNMETHOD; - -static int paramsortFunc(OAUTHPARAMETER *p1, OAUTHPARAMETER *p2) -{ - int res = strcmp(p1->name, p2->name); - return res != 0 ? res : strcmp(p1->value, p2->value); -} - -// HMAC-SHA1 (see RFC 2104 for details) -void hmacsha1_hash(mir_sha1_byte_t *text, int text_len, mir_sha1_byte_t *key, int key_len, - mir_sha1_byte_t digest[MIR_SHA1_HASH_SIZE]) -{ - mir_sha1_ctx context; - mir_sha1_byte_t k_ipad[64]; - mir_sha1_byte_t k_opad[64]; - int i; - - if (key_len > 64) { - mir_sha1_ctx tctx; - mir_sha1_byte_t tk[MIR_SHA1_HASH_SIZE]; - - mir_sha1_init(&tctx); - mir_sha1_append(&tctx, key, key_len); - mir_sha1_finish(&tctx, tk); - - key = tk; - key_len = MIR_SHA1_HASH_SIZE; - } - - memset(k_ipad, 0x36, 64); - memset(k_opad, 0x5c, 64); - - for (i = 0; i < key_len; i++) { - k_ipad[i] ^= key[i]; - k_opad[i] ^= key[i]; - } - - mir_sha1_init(&context); - mir_sha1_append(&context, k_ipad, 64); - mir_sha1_append(&context, text, text_len); - mir_sha1_finish(&context, digest); - - mir_sha1_init(&context); - mir_sha1_append(&context, k_opad, 64); - mir_sha1_append(&context, digest, MIR_SHA1_HASH_SIZE); - mir_sha1_finish(&context, digest); -} - -// see RFC 3986 for details -#define isunreserved(c) ( isalnum((unsigned char)c) || c == '-' || c == '.' || c == '_' || c == '~') -char *oauth_uri_escape(const char *str) -{ - char *res; - int size, ix = 0; - - if (str == NULL) return mir_strdup(""); - - size = (int)strlen(str) + 1; - res = (char *)mir_alloc(size); - - while (*str) { - if (!isunreserved(*str)) { - size += 2; - res = (char *)mir_realloc(res, size); - mir_snprintf(&res[ix], 4, "%%%X%X", (*str >> 4) & 15, *str & 15); - ix += 3; - } - else - res[ix++] = *str; - str++; - } - res[ix] = 0; - - return res; -} - -// generates Signature Base String -char *oauth_generate_signature(SortedList *params, const char *httpmethod, const char *url) -{ - char *res, *urlenc, *urlnorm; - OAUTHPARAMETER *p; - int i, ix = 0, size; - - if (httpmethod == NULL || url == NULL || !params->realCount) return mir_strdup(""); - - urlnorm = (char *)mir_alloc(strlen(url) + 1); - while (*url) { - if (*url == '?' || *url == '#') break; // see RFC 3986 section 3 - urlnorm[ix++] = tolower(*url); - url++; - } - urlnorm[ix] = 0; - if ((res = strstr(urlnorm, ":80")) != NULL) - memmove(res, res + 3, strlen(res) - 2); - else if ((res = strstr(urlnorm, ":443")) != NULL) - memmove(res, res + 4, strlen(res) - 3); - - urlenc = oauth_uri_escape(urlnorm); - mir_free(urlnorm); - size = (int)strlen(httpmethod) + (int)strlen(urlenc) + 1 + 2; - - for (i = 0; i < params->realCount; i++) { - p = params->items[i]; - if (!strcmp(p->name, "oauth_signature")) continue; - if (i > 0) size += 3; - size += (int)strlen(p->name) + (int)strlen(p->value) + 3; - } - - res = (char *)mir_alloc(size); - strcpy(res, httpmethod); - strcat(res, "&"); - strcat(res, urlenc); - mir_free(urlenc); - strcat(res, "&"); - - for (i = 0; i < params->realCount; i++) { - p = params->items[i]; - if (!strcmp(p->name, "oauth_signature")) continue; - if (i > 0) strcat(res, "%26"); - strcat(res, p->name); - strcat(res, "%3D"); - strcat(res, p->value); - } - - return res; -} - -char *oauth_getparam(SortedList *params, const char *name) -{ - OAUTHPARAMETER *p; - int i; - - if (name == NULL) return NULL; - - for (i = 0; i < params->realCount; i++) { - p = params->items[i]; - if (!strcmp(p->name, name)) - return p->value; - } - - return NULL; -} - -void oauth_setparam(SortedList *params, const char *name, const char *value) -{ - OAUTHPARAMETER *p; - int i; - - if (name == NULL) return; - - for (i = 0; i < params->realCount; i++) { - p = params->items[i]; - if (!strcmp(p->name, name)) { - mir_free(p->value); - p->value = oauth_uri_escape(value); - return; - } - } - - p = mir_alloc(sizeof(OAUTHPARAMETER)); - p->name = oauth_uri_escape(name); - p->value = oauth_uri_escape(value); - List_InsertPtr(params, p); -} - -void oauth_freeparams(SortedList *params) -{ - OAUTHPARAMETER *p; - int i; - - for (i = 0; i < params->realCount; i++) { - p = params->items[i]; - mir_free(p->name); - mir_free(p->value); - } -} - -int oauth_sign_request(SortedList *params, const char *httpmethod, const char *url, - const char *consumer_secret, const char *token_secret) -{ - char *sign = NULL, *signmethod; - - if (!params->realCount) return -1; - - signmethod = oauth_getparam(params, "oauth_signature_method"); - if (signmethod == NULL) return -1; - - if (!strcmp(signmethod, "HMAC-SHA1")) { - char *text = oauth_generate_signature(params, httpmethod, url); - char *key; - char *csenc = oauth_uri_escape(consumer_secret); - char *tsenc = oauth_uri_escape(token_secret); - mir_sha1_byte_t digest[MIR_SHA1_HASH_SIZE]; - NETLIBBASE64 nlb64 = {0}; - int signlen; - - key = (char *)mir_alloc(strlen(csenc) + strlen(tsenc) + 2); - strcpy(key, csenc); - strcat(key, "&"); - strcat(key, tsenc); - mir_free(csenc); - mir_free(tsenc); - - hmacsha1_hash(text, (int)strlen(text), key, (int)strlen(key), digest); - - signlen = Netlib_GetBase64EncodedBufferSize(MIR_SHA1_HASH_SIZE); - sign = (char *)mir_alloc(signlen); - nlb64.pszEncoded = sign; - nlb64.cchEncoded = signlen; - nlb64.pbDecoded = digest; - nlb64.cbDecoded = MIR_SHA1_HASH_SIZE; - CallService(MS_NETLIB_BASE64ENCODE, 0, (LPARAM)&nlb64); - - mir_free(text); - mir_free(key); - } -// else if (!strcmp(signmethod, "RSA-SHA1")) { // unimplemented -// } - else { // PLAINTEXT - char *csenc = oauth_uri_escape(consumer_secret); - char *tsenc = oauth_uri_escape(token_secret); - - sign = (char *)mir_alloc(strlen(csenc) + strlen(tsenc) + 2); - strcpy(sign, csenc); - strcat(sign, "&"); - strcat(sign, tsenc); - mir_free(csenc); - mir_free(tsenc); - } - - oauth_setparam(params, "oauth_signature", sign); - mir_free(sign); - - return 0; -} - -char *oauth_generate_nonce() -{ - mir_md5_byte_t digest[16]; - char *result, *str, timestamp[22], randnum[16]; - int i; - - mir_snprintf(timestamp, sizeof(timestamp), "%ld", time(NULL)); - CallService(MS_UTILS_GETRANDOM, (WPARAM)sizeof(randnum), (LPARAM)randnum); - - str = (char *)mir_alloc(strlen(timestamp) + strlen(randnum) + 1); - strcpy(str, timestamp); - strcat(str, randnum); - mir_md5_hash(str, (int)strlen(str), digest); - mir_free(str); - - result = (char *)mir_alloc(32 + 1); - for (i = 0; i < 16; i++) - sprintf(result + (i<<1), "%02x", digest[i]); - - return result; -} - -char *oauth_auth_header(const char *httpmethod, const char *url, OAUTHSIGNMETHOD signmethod, - const char *consumer_key, const char *consumer_secret, - const char *token, const char *token_secret) -{ - OAUTHPARAMETER *p; - int i, size; - char *res, timestamp[22], *nonce; - SortedList oauth_parameters = {0}; - - if (httpmethod == NULL || url == NULL) return NULL; - - oauth_parameters.sortFunc = paramsortFunc; - oauth_parameters.increment = 1; - - oauth_setparam(&oauth_parameters, "oauth_consumer_key", consumer_key); - oauth_setparam(&oauth_parameters, "oauth_version", "1.0"); - switch (signmethod) { - case HMACSHA1: oauth_setparam(&oauth_parameters, "oauth_signature_method", "HMAC-SHA1"); break; - case RSASHA1: oauth_setparam(&oauth_parameters, "oauth_signature_method", "RSA-SHA1"); break; - default: oauth_setparam(&oauth_parameters, "oauth_signature_method", "PLAINTEXT"); break; - }; - mir_snprintf(timestamp, sizeof(timestamp), "%ld", time(NULL)); - oauth_setparam(&oauth_parameters, "oauth_timestamp", timestamp); - nonce = oauth_generate_nonce(); - oauth_setparam(&oauth_parameters, "oauth_nonce", nonce); - mir_free(nonce); - if (token != NULL && *token) - oauth_setparam(&oauth_parameters, "oauth_token", token); - - if (oauth_sign_request(&oauth_parameters, httpmethod, url, consumer_secret, token_secret)) { - oauth_freeparams(&oauth_parameters); - List_Destroy(&oauth_parameters); - return NULL; - } - - size = 7; - for (i = 0; i < oauth_parameters.realCount; i++) { - p = oauth_parameters.items[i]; - if (i > 0) size++; - size += (int)strlen(p->name) + (int)strlen(p->value) + 3; - } - - res = (char *)mir_alloc(size); - strcpy(res, "OAuth "); - - for (i = 0; i < oauth_parameters.realCount; i++) { - p = oauth_parameters.items[i]; - if (i > 0) strcat(res, ","); - strcat(res, p->name); - strcat(res, "=\""); - strcat(res, p->value); - strcat(res, "\""); - } - - oauth_freeparams(&oauth_parameters); - List_Destroy(&oauth_parameters); - - return res; -} - -char *gg_oauth_header(GGPROTO *gg, const char *httpmethod, const char *url) -{ - char *res, uin[32], *password = NULL, *token = NULL, *token_secret = NULL; - DBVARIANT dbv; - - UIN2ID(DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0), uin); - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) { - CallService(MS_DB_CRYPT_DECODESTRING, (WPARAM)(int)strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal); - password = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_TOKEN, &dbv)) { - token = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_TOKENSECRET, &dbv)) { - CallService(MS_DB_CRYPT_DECODESTRING, (WPARAM)(int)strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal); - token_secret = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - - res = oauth_auth_header(httpmethod, url, HMACSHA1, uin, password, token, token_secret); - mir_free(password); - mir_free(token); - mir_free(token_secret); - - return res; -} - -int gg_oauth_receivetoken(GGPROTO *gg) -{ - NETLIBHTTPHEADER httpHeaders[3]; - NETLIBHTTPREQUEST req = {0}; - NETLIBHTTPREQUEST *resp; - char szUrl[256], uin[32], *password = NULL, *str, *token = NULL, *token_secret = NULL; - DBVARIANT dbv; - int res = 0; - HANDLE nlc = NULL; - - UIN2ID(DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0), uin); - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal); - password = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - - // 1. Obtaining an Unauthorized Request Token - gg_netlog(gg, "gg_oauth_receivetoken(): Obtaining an Unauthorized Request Token..."); - strcpy(szUrl, "http://api.gadu-gadu.pl/request_token"); - str = oauth_auth_header("POST", szUrl, HMACSHA1, uin, password, NULL, NULL); - - req.cbSize = sizeof(req); - req.requestType = REQUEST_POST; - req.szUrl = szUrl; - req.flags = NLHRF_NODUMP | NLHRF_HTTP11 | NLHRF_PERSISTENT; - req.headersCount = 3; - req.headers = httpHeaders; - httpHeaders[0].szName = "User-Agent"; - httpHeaders[0].szValue = GG8_VERSION; - httpHeaders[1].szName = "Authorization"; - httpHeaders[1].szValue = str; - httpHeaders[2].szName = "Accept"; - httpHeaders[2].szValue = "*/*"; - - resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req); - if (resp) { - nlc = resp->nlc; - if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { - HXML hXml; - TCHAR *xmlAction; - TCHAR *tag; - - xmlAction = gg_a2t(resp->pData); - tag = gg_a2t("result"); - hXml = xi.parseString(xmlAction, 0, tag); - if (hXml != NULL) { - HXML node; - - mir_free(tag); tag = gg_a2t("oauth_token"); - node = xi.getChildByPath(hXml, tag, 0); - token = node != NULL ? gg_t2a(xi.getText(node)) : NULL; - - mir_free(tag); tag = gg_a2t("oauth_token_secret"); - node = xi.getChildByPath(hXml, tag, 0); - token_secret = node != NULL ? gg_t2a(xi.getText(node)) : NULL; - - xi.destroyNode(hXml); - } - mir_free(tag); - mir_free(xmlAction); - } - else gg_netlog(gg, "gg_oauth_receivetoken(): Invalid response code from HTTP request"); - CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); - } - else gg_netlog(gg, "gg_oauth_receivetoken(): No response from HTTP request"); - - // 2. Obtaining User Authorization - gg_netlog(gg, "gg_oauth_receivetoken(): Obtaining User Authorization..."); - mir_free(str); - str = oauth_uri_escape("http://www.mojageneracja.pl"); - - mir_snprintf(szUrl, 256, "callback_url=%s&request_token=%s&uin=%s&password=%s", - str, token, uin, password); - mir_free(str); - str = mir_strdup(szUrl); - - ZeroMemory(&req, sizeof(req)); - req.cbSize = sizeof(req); - req.requestType = REQUEST_POST; - req.szUrl = szUrl; - req.flags = NLHRF_NODUMP | NLHRF_HTTP11; - req.headersCount = 3; - req.headers = httpHeaders; - strcpy(szUrl, "https://login.gadu-gadu.pl/authorize"); - httpHeaders[1].szName = "Content-Type"; - httpHeaders[1].szValue = "application/x-www-form-urlencoded"; - req.pData = str; - req.dataLength = (int)strlen(str); - - resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req); - if (resp) CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); - else gg_netlog(gg, "gg_oauth_receivetoken(): No response from HTTP request"); - - // 3. Obtaining an Access Token - gg_netlog(gg, "gg_oauth_receivetoken(): Obtaining an Access Token..."); - strcpy(szUrl, "http://api.gadu-gadu.pl/access_token"); - mir_free(str); - str = oauth_auth_header("POST", szUrl, HMACSHA1, uin, password, token, token_secret); - mir_free(token); - mir_free(token_secret); - token = NULL; - token_secret = NULL; - - ZeroMemory(&req, sizeof(req)); - req.cbSize = sizeof(req); - req.requestType = REQUEST_POST; - req.szUrl = szUrl; - req.flags = NLHRF_NODUMP | NLHRF_HTTP11 | NLHRF_PERSISTENT; - req.nlc = nlc; - req.headersCount = 3; - req.headers = httpHeaders; - httpHeaders[1].szName = "Authorization"; - httpHeaders[1].szValue = str; - - resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req); - if (resp) { - if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { - HXML hXml; - TCHAR *xmlAction; - TCHAR *tag; - - xmlAction = gg_a2t(resp->pData); - tag = gg_a2t("result"); - hXml = xi.parseString(xmlAction, 0, tag); - if (hXml != NULL) { - HXML node; - - mir_free(tag); tag = gg_a2t("oauth_token"); - node = xi.getChildByPath(hXml, tag, 0); - token = node != NULL ? gg_t2a(xi.getText(node)) : NULL; - - mir_free(tag); tag = gg_a2t("oauth_token_secret"); - node = xi.getChildByPath(hXml, tag, 0); - token_secret = node != NULL ? gg_t2a(xi.getText(node)) : NULL; - - xi.destroyNode(hXml); - } - mir_free(tag); - mir_free(xmlAction); - } - else gg_netlog(gg, "gg_oauth_receivetoken(): Invalid response code from HTTP request"); - Netlib_CloseHandle(resp->nlc); - CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); - } - else gg_netlog(gg, "gg_oauth_receivetoken(): No response from HTTP request"); - - mir_free(password); - mir_free(str); - - if (token != NULL && token_secret != NULL) { - DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_TOKEN, token); - CallService(MS_DB_CRYPT_ENCODESTRING, (WPARAM)(int)strlen(token_secret) + 1, (LPARAM) token_secret); - DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_TOKENSECRET, token_secret); - gg_netlog(gg, "gg_oauth_receivetoken(): Access Token obtained successfully."); - res = 1; - } - else { - DBDeleteContactSetting(NULL, GG_PROTO, GG_KEY_TOKEN); - DBDeleteContactSetting(NULL, GG_PROTO, GG_KEY_TOKENSECRET); - gg_netlog(gg, "gg_oauth_receivetoken(): Failed to obtain Access Token."); - } - mir_free(token); - mir_free(token_secret); - - return res; -} - -int gg_oauth_checktoken(GGPROTO *gg, int force) -{ - if (!force) { - char *token = NULL, *token_secret = NULL; - DBVARIANT dbv; - int res = 1; - - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_TOKEN, &dbv)) { - token = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_TOKENSECRET, &dbv)) { - CallService(MS_DB_CRYPT_DECODESTRING, (WPARAM)(int)strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal); - token_secret = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - - if (token == NULL || token_secret == NULL) { - res = gg_oauth_receivetoken(gg); - } - - mir_free(token); - mir_free(token_secret); - - return res; - } - - return gg_oauth_receivetoken(gg); -} diff --git a/protocols/Gadu-Gadu/oauth.cpp b/protocols/Gadu-Gadu/oauth.cpp new file mode 100644 index 0000000000..d652a79020 --- /dev/null +++ b/protocols/Gadu-Gadu/oauth.cpp @@ -0,0 +1,584 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2010 Bartosz Białek +// +// 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 "gg.h" +#include +#include +#include "protocol.h" + +////////////////////////////////////////////////////////// +// OAuth 1.0 implementation + +// Service Provider must accept the HTTP Authorization header + +// RSA-SHA1 signature method (see RFC 3447 section 8.2 +// and RSASSA-PKCS1-v1_5 algorithm) is unimplemented + +typedef struct +{ + char *name; + char *value; +} OAUTHPARAMETER; + +typedef enum +{ + HMACSHA1, + RSASHA1, + PLAINTEXT +} OAUTHSIGNMETHOD; + +static int paramsortFunc(const OAUTHPARAMETER *p1, const OAUTHPARAMETER *p2) +{ + int res = strcmp(p1->name, p2->name); + return res != 0 ? res : strcmp(p1->value, p2->value); +} + +// HMAC-SHA1 (see RFC 2104 for details) +void hmacsha1_hash(mir_sha1_byte_t *text, int text_len, mir_sha1_byte_t *key, int key_len, + mir_sha1_byte_t digest[MIR_SHA1_HASH_SIZE]) +{ + mir_sha1_ctx context; + mir_sha1_byte_t k_ipad[64]; + mir_sha1_byte_t k_opad[64]; + int i; + + if (key_len > 64) { + mir_sha1_ctx tctx; + mir_sha1_byte_t tk[MIR_SHA1_HASH_SIZE]; + + mir_sha1_init(&tctx); + mir_sha1_append(&tctx, key, key_len); + mir_sha1_finish(&tctx, tk); + + key = tk; + key_len = MIR_SHA1_HASH_SIZE; + } + + memset(k_ipad, 0x36, 64); + memset(k_opad, 0x5c, 64); + + for (i = 0; i < key_len; i++) { + k_ipad[i] ^= key[i]; + k_opad[i] ^= key[i]; + } + + mir_sha1_init(&context); + mir_sha1_append(&context, k_ipad, 64); + mir_sha1_append(&context, text, text_len); + mir_sha1_finish(&context, digest); + + mir_sha1_init(&context); + mir_sha1_append(&context, k_opad, 64); + mir_sha1_append(&context, digest, MIR_SHA1_HASH_SIZE); + mir_sha1_finish(&context, digest); +} + +// see RFC 3986 for details +#define isunreserved(c) ( isalnum((unsigned char)c) || c == '-' || c == '.' || c == '_' || c == '~') +char *oauth_uri_escape(const char *str) +{ + char *res; + int size, ix = 0; + + if (str == NULL) return mir_strdup(""); + + size = (int)strlen(str) + 1; + res = (char *)mir_alloc(size); + + while (*str) { + if (!isunreserved(*str)) { + size += 2; + res = (char *)mir_realloc(res, size); + mir_snprintf(&res[ix], 4, "%%%X%X", (*str >> 4) & 15, *str & 15); + ix += 3; + } + else + res[ix++] = *str; + str++; + } + res[ix] = 0; + + return res; +} + +// generates Signature Base String + +char *oauth_generate_signature(LIST ¶ms, const char *httpmethod, const char *url) +{ + char *res, *urlenc, *urlnorm; + OAUTHPARAMETER *p; + int i, ix = 0, size; + + if (httpmethod == NULL || url == NULL || !params.getCount()) return mir_strdup(""); + + urlnorm = (char *)mir_alloc(strlen(url) + 1); + while (*url) { + if (*url == '?' || *url == '#') break; // see RFC 3986 section 3 + urlnorm[ix++] = tolower(*url); + url++; + } + urlnorm[ix] = 0; + if ((res = strstr(urlnorm, ":80")) != NULL) + memmove(res, res + 3, strlen(res) - 2); + else if ((res = strstr(urlnorm, ":443")) != NULL) + memmove(res, res + 4, strlen(res) - 3); + + urlenc = oauth_uri_escape(urlnorm); + mir_free(urlnorm); + size = (int)strlen(httpmethod) + (int)strlen(urlenc) + 1 + 2; + + for (i = 0; i < params.getCount(); i++) { + p = params[i]; + if (!strcmp(p->name, "oauth_signature")) continue; + if (i > 0) size += 3; + size += (int)strlen(p->name) + (int)strlen(p->value) + 3; + } + + res = (char *)mir_alloc(size); + strcpy(res, httpmethod); + strcat(res, "&"); + strcat(res, urlenc); + mir_free(urlenc); + strcat(res, "&"); + + for (i = 0; i < params.getCount(); i++) { + p = params[i]; + if (!strcmp(p->name, "oauth_signature")) continue; + if (i > 0) strcat(res, "%26"); + strcat(res, p->name); + strcat(res, "%3D"); + strcat(res, p->value); + } + + return res; +} + +char *oauth_getparam(LIST ¶ms, const char *name) +{ + OAUTHPARAMETER *p; + int i; + + if (name == NULL) return NULL; + + for (i = 0; i < params.getCount(); i++) { + p = params[i]; + if (!strcmp(p->name, name)) + return p->value; + } + + return NULL; +} + +void oauth_setparam(LIST ¶ms, const char *name, const char *value) +{ + OAUTHPARAMETER *p; + int i; + + if (name == NULL) return; + + for (i = 0; i < params.getCount(); i++) { + p = params[i]; + if (!strcmp(p->name, name)) { + mir_free(p->value); + p->value = oauth_uri_escape(value); + return; + } + } + + p = (OAUTHPARAMETER*)mir_alloc(sizeof(OAUTHPARAMETER)); + p->name = oauth_uri_escape(name); + p->value = oauth_uri_escape(value); + params.insert(p); +} + +void oauth_freeparams(LIST ¶ms) +{ + OAUTHPARAMETER *p; + int i; + + for (i = 0; i < params.getCount(); i++) { + p = params[i]; + mir_free(p->name); + mir_free(p->value); + } +} + +int oauth_sign_request(LIST ¶ms, const char *httpmethod, const char *url, + const char *consumer_secret, const char *token_secret) +{ + char *sign = NULL, *signmethod; + + if (!params.getCount()) return -1; + + signmethod = oauth_getparam(params, "oauth_signature_method"); + if (signmethod == NULL) return -1; + + if (!strcmp(signmethod, "HMAC-SHA1")) { + char *text = oauth_generate_signature(params, httpmethod, url); + char *key; + char *csenc = oauth_uri_escape(consumer_secret); + char *tsenc = oauth_uri_escape(token_secret); + mir_sha1_byte_t digest[MIR_SHA1_HASH_SIZE]; + NETLIBBASE64 nlb64 = {0}; + int signlen; + + key = (char *)mir_alloc(strlen(csenc) + strlen(tsenc) + 2); + strcpy(key, csenc); + strcat(key, "&"); + strcat(key, tsenc); + mir_free(csenc); + mir_free(tsenc); + + hmacsha1_hash((BYTE*)text, (int)strlen(text), (BYTE*)key, (int)strlen(key), digest); + + signlen = Netlib_GetBase64EncodedBufferSize(MIR_SHA1_HASH_SIZE); + sign = (char *)mir_alloc(signlen); + nlb64.pszEncoded = sign; + nlb64.cchEncoded = signlen; + nlb64.pbDecoded = digest; + nlb64.cbDecoded = MIR_SHA1_HASH_SIZE; + CallService(MS_NETLIB_BASE64ENCODE, 0, (LPARAM)&nlb64); + + mir_free(text); + mir_free(key); + } +// else if (!strcmp(signmethod, "RSA-SHA1")) { // unimplemented +// } + else { // PLAINTEXT + char *csenc = oauth_uri_escape(consumer_secret); + char *tsenc = oauth_uri_escape(token_secret); + + sign = (char *)mir_alloc(strlen(csenc) + strlen(tsenc) + 2); + strcpy(sign, csenc); + strcat(sign, "&"); + strcat(sign, tsenc); + mir_free(csenc); + mir_free(tsenc); + } + + oauth_setparam(params, "oauth_signature", sign); + mir_free(sign); + + return 0; +} + +char *oauth_generate_nonce() +{ + mir_md5_byte_t digest[16]; + char *result, *str, timestamp[22], randnum[16]; + int i; + + mir_snprintf(timestamp, sizeof(timestamp), "%ld", time(NULL)); + CallService(MS_UTILS_GETRANDOM, (WPARAM)sizeof(randnum), (LPARAM)randnum); + + str = (char *)mir_alloc(strlen(timestamp) + strlen(randnum) + 1); + strcpy(str, timestamp); + strcat(str, randnum); + mir_md5_hash((BYTE*)str, (int)strlen(str), digest); + mir_free(str); + + result = (char *)mir_alloc(32 + 1); + for (i = 0; i < 16; i++) + sprintf(result + (i<<1), "%02x", digest[i]); + + return result; +} + +char *oauth_auth_header(const char *httpmethod, const char *url, OAUTHSIGNMETHOD signmethod, + const char *consumer_key, const char *consumer_secret, + const char *token, const char *token_secret) +{ + int i, size; + char *res, timestamp[22], *nonce; + + if (httpmethod == NULL || url == NULL) return NULL; + + LIST oauth_parameters(1, paramsortFunc); + oauth_setparam(oauth_parameters, "oauth_consumer_key", consumer_key); + oauth_setparam(oauth_parameters, "oauth_version", "1.0"); + switch (signmethod) { + case HMACSHA1: oauth_setparam(oauth_parameters, "oauth_signature_method", "HMAC-SHA1"); break; + case RSASHA1: oauth_setparam(oauth_parameters, "oauth_signature_method", "RSA-SHA1"); break; + default: oauth_setparam(oauth_parameters, "oauth_signature_method", "PLAINTEXT"); break; + }; + mir_snprintf(timestamp, sizeof(timestamp), "%ld", time(NULL)); + oauth_setparam(oauth_parameters, "oauth_timestamp", timestamp); + nonce = oauth_generate_nonce(); + oauth_setparam(oauth_parameters, "oauth_nonce", nonce); + mir_free(nonce); + if (token != NULL && *token) + oauth_setparam(oauth_parameters, "oauth_token", token); + + if (oauth_sign_request(oauth_parameters, httpmethod, url, consumer_secret, token_secret)) { + oauth_freeparams(oauth_parameters); + oauth_parameters.destroy(); + return NULL; + } + + size = 7; + for (i = 0; i < oauth_parameters.getCount(); i++) { + OAUTHPARAMETER *p = oauth_parameters[i]; + if (i > 0) size++; + size += (int)strlen(p->name) + (int)strlen(p->value) + 3; + } + + res = (char *)mir_alloc(size); + strcpy(res, "OAuth "); + + for (i = 0; i < oauth_parameters.getCount(); i++) { + OAUTHPARAMETER *p = oauth_parameters[i]; + if (i > 0) strcat(res, ","); + strcat(res, p->name); + strcat(res, "=\""); + strcat(res, p->value); + strcat(res, "\""); + } + + oauth_freeparams(oauth_parameters); + oauth_parameters.destroy(); + return res; +} + +char* GGPROTO::oauth_header(const char *httpmethod, const char *url) +{ + char *res, uin[32], *password = NULL, *token = NULL, *token_secret = NULL; + DBVARIANT dbv; + + UIN2ID(db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0), uin); + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { + CallService(MS_DB_CRYPT_DECODESTRING, (WPARAM)(int)strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal); + password = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + if (!db_get_s(NULL, m_szModuleName, GG_KEY_TOKEN, &dbv, DBVT_ASCIIZ)) { + token = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + if (!db_get_s(NULL, m_szModuleName, GG_KEY_TOKENSECRET, &dbv, DBVT_ASCIIZ)) { + CallService(MS_DB_CRYPT_DECODESTRING, (WPARAM)(int)strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal); + token_secret = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + + res = oauth_auth_header(httpmethod, url, HMACSHA1, uin, password, token, token_secret); + mir_free(password); + mir_free(token); + mir_free(token_secret); + + return res; +} + +int GGPROTO::oauth_receivetoken() +{ + NETLIBHTTPHEADER httpHeaders[3]; + NETLIBHTTPREQUEST req = {0}; + NETLIBHTTPREQUEST *resp; + char szUrl[256], uin[32], *password = NULL, *str, *token = NULL, *token_secret = NULL; + DBVARIANT dbv; + int res = 0; + HANDLE nlc = NULL; + + UIN2ID(db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0), uin); + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal); + password = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + + // 1. Obtaining an Unauthorized Request Token + netlog("gg_oauth_receivetoken(): Obtaining an Unauthorized Request Token..."); + strcpy(szUrl, "http://api.gadu-gadu.pl/request_token"); + str = oauth_auth_header("POST", szUrl, HMACSHA1, uin, password, NULL, NULL); + + req.cbSize = sizeof(req); + req.requestType = REQUEST_POST; + req.szUrl = szUrl; + req.flags = NLHRF_NODUMP | NLHRF_HTTP11 | NLHRF_PERSISTENT; + req.headersCount = 3; + req.headers = httpHeaders; + httpHeaders[0].szName = "User-Agent"; + httpHeaders[0].szValue = GG8_VERSION; + httpHeaders[1].szName = "Authorization"; + httpHeaders[1].szValue = str; + httpHeaders[2].szName = "Accept"; + httpHeaders[2].szValue = "*/*"; + + resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)netlib, (LPARAM)&req); + if (resp) { + nlc = resp->nlc; + if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { + HXML hXml; + TCHAR *xmlAction; + TCHAR *tag; + + xmlAction = mir_a2t(resp->pData); + tag = mir_a2t("result"); + hXml = xi.parseString(xmlAction, 0, tag); + if (hXml != NULL) { + HXML node; + + mir_free(tag); tag = mir_a2t("oauth_token"); + node = xi.getChildByPath(hXml, tag, 0); + token = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + + mir_free(tag); tag = mir_a2t("oauth_token_secret"); + node = xi.getChildByPath(hXml, tag, 0); + token_secret = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + + xi.destroyNode(hXml); + } + mir_free(tag); + mir_free(xmlAction); + } + else netlog("gg_oauth_receivetoken(): Invalid response code from HTTP request"); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); + } + else netlog("gg_oauth_receivetoken(): No response from HTTP request"); + + // 2. Obtaining User Authorization + netlog("gg_oauth_receivetoken(): Obtaining User Authorization..."); + mir_free(str); + str = oauth_uri_escape("http://www.mojageneracja.pl"); + + mir_snprintf(szUrl, 256, "callback_url=%s&request_token=%s&uin=%s&password=%s", + str, token, uin, password); + mir_free(str); + str = mir_strdup(szUrl); + + ZeroMemory(&req, sizeof(req)); + req.cbSize = sizeof(req); + req.requestType = REQUEST_POST; + req.szUrl = szUrl; + req.flags = NLHRF_NODUMP | NLHRF_HTTP11; + req.headersCount = 3; + req.headers = httpHeaders; + strcpy(szUrl, "https://login.gadu-gadu.pl/authorize"); + httpHeaders[1].szName = "Content-Type"; + httpHeaders[1].szValue = "application/x-www-form-urlencoded"; + req.pData = str; + req.dataLength = (int)strlen(str); + + resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)netlib, (LPARAM)&req); + if (resp) CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); + else netlog("gg_oauth_receivetoken(): No response from HTTP request"); + + // 3. Obtaining an Access Token + netlog("gg_oauth_receivetoken(): Obtaining an Access Token..."); + strcpy(szUrl, "http://api.gadu-gadu.pl/access_token"); + mir_free(str); + str = oauth_auth_header("POST", szUrl, HMACSHA1, uin, password, token, token_secret); + mir_free(token); + mir_free(token_secret); + token = NULL; + token_secret = NULL; + + ZeroMemory(&req, sizeof(req)); + req.cbSize = sizeof(req); + req.requestType = REQUEST_POST; + req.szUrl = szUrl; + req.flags = NLHRF_NODUMP | NLHRF_HTTP11 | NLHRF_PERSISTENT; + req.nlc = nlc; + req.headersCount = 3; + req.headers = httpHeaders; + httpHeaders[1].szName = "Authorization"; + httpHeaders[1].szValue = str; + + resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)netlib, (LPARAM)&req); + if (resp) { + if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { + HXML hXml; + TCHAR *xmlAction; + TCHAR *tag; + + xmlAction = mir_a2t(resp->pData); + tag = mir_a2t("result"); + hXml = xi.parseString(xmlAction, 0, tag); + if (hXml != NULL) { + HXML node; + + mir_free(tag); tag = mir_a2t("oauth_token"); + node = xi.getChildByPath(hXml, tag, 0); + token = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + + mir_free(tag); tag = mir_a2t("oauth_token_secret"); + node = xi.getChildByPath(hXml, tag, 0); + token_secret = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + + xi.destroyNode(hXml); + } + mir_free(tag); + mir_free(xmlAction); + } + else netlog("gg_oauth_receivetoken(): Invalid response code from HTTP request"); + Netlib_CloseHandle(resp->nlc); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); + } + else netlog("gg_oauth_receivetoken(): No response from HTTP request"); + + mir_free(password); + mir_free(str); + + if (token != NULL && token_secret != NULL) { + db_set_s(NULL, m_szModuleName, GG_KEY_TOKEN, token); + CallService(MS_DB_CRYPT_ENCODESTRING, (WPARAM)(int)strlen(token_secret) + 1, (LPARAM) token_secret); + db_set_s(NULL, m_szModuleName, GG_KEY_TOKENSECRET, token_secret); + netlog("gg_oauth_receivetoken(): Access Token obtained successfully."); + res = 1; + } + else { + db_unset(NULL, m_szModuleName, GG_KEY_TOKEN); + db_unset(NULL, m_szModuleName, GG_KEY_TOKENSECRET); + netlog("gg_oauth_receivetoken(): Failed to obtain Access Token."); + } + mir_free(token); + mir_free(token_secret); + + return res; +} + +int GGPROTO::oauth_checktoken(int force) +{ + if (!force) { + char *token = NULL, *token_secret = NULL; + DBVARIANT dbv; + int res = 1; + + if (!db_get_s(NULL, m_szModuleName, GG_KEY_TOKEN, &dbv, DBVT_ASCIIZ)) { + token = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + if (!db_get_s(NULL, m_szModuleName, GG_KEY_TOKENSECRET, &dbv, DBVT_ASCIIZ)) { + CallService(MS_DB_CRYPT_DECODESTRING, (WPARAM)(int)strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal); + token_secret = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + + if (token == NULL || token_secret == NULL) { + res = oauth_receivetoken(); + } + + mir_free(token); + mir_free(token_secret); + + return res; + } + + return oauth_receivetoken(); +} diff --git a/protocols/Gadu-Gadu/ownerinfo.c b/protocols/Gadu-Gadu/ownerinfo.c deleted file mode 100644 index 28cfb4df75..0000000000 --- a/protocols/Gadu-Gadu/ownerinfo.c +++ /dev/null @@ -1,88 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 Adam Strzelecki -// -// 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 "gg.h" - -////////////////////////////////////////////////////////// -// remind password - -typedef struct -{ - uin_t uin; - const char *email; -} GG_REMIND_PASS; - -void __cdecl gg_remindpasswordthread(GGPROTO *gg, void *param) -{ - // Connection handle - struct gg_http *h; - GG_REMIND_PASS *rp = (GG_REMIND_PASS *)param; - GGTOKEN token; - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_remindpasswordthread(): Starting."); -#endif - if (!rp || !rp->email || !rp->uin || !strlen(rp->email)) - { - if(rp) free(rp); - return; - } - - // Get token - if (!gg_gettoken(gg, &token)) return; - - if (!(h = gg_remind_passwd3(rp->uin, rp->email, token.id, token.val, 0))) - { - char error[128]; - mir_snprintf(error, sizeof(error), Translate("Password could not be reminded because of error:\n\t%s"), strerror(errno)); - MessageBox( - NULL, - error, - GG_PROTONAME, - MB_OK | MB_ICONSTOP - ); - gg_netlog(gg, "gg_remindpasswordthread(): Password could not be reminded because of \"%s\".", strerror(errno)); - } - else - { - gg_pubdir_free(h); - gg_netlog(gg, "gg_remindpasswordthread(): Password remind successful."); - MessageBox( - NULL, - Translate("Password was sent to your e-mail."), - GG_PROTONAME, - MB_OK | MB_ICONINFORMATION - ); - } - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_remindpasswordthread(): End."); -#endif - if(rp) free(rp); -} - -void gg_remindpassword(GGPROTO *gg, uin_t uin, const char *email) -{ - GG_REMIND_PASS *rp = malloc(sizeof(GG_REMIND_PASS)); - - rp->uin = uin; - rp->email = email; - gg_forkthread(gg, gg_remindpasswordthread, rp); -} diff --git a/protocols/Gadu-Gadu/ownerinfo.cpp b/protocols/Gadu-Gadu/ownerinfo.cpp new file mode 100644 index 0000000000..6c37cdb180 --- /dev/null +++ b/protocols/Gadu-Gadu/ownerinfo.cpp @@ -0,0 +1,78 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 Adam Strzelecki +// +// 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 "gg.h" + +////////////////////////////////////////////////////////// +// remind password + +typedef struct +{ + uin_t uin; + const char *email; +} GG_REMIND_PASS; + +void __cdecl GGPROTO::remindpasswordthread(void *param) +{ + // Connection handle + struct gg_http *h; + GG_REMIND_PASS *rp = (GG_REMIND_PASS *)param; + GGTOKEN token; + +#ifdef DEBUGMODE + netlog("gg_remindpasswordthread(): Starting."); +#endif + if (!rp || !rp->email || !rp->uin || !strlen(rp->email)) + { + if (rp) free(rp); + return; + } + + // Get token + if (!gettoken(&token)) return; + + if (!(h = gg_remind_passwd3(rp->uin, rp->email, token.id, token.val, 0))) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("Password could not be reminded because of error:\n\t%s"), _tcserror(errno)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + netlog("gg_remindpasswordthread(): Password could not be reminded because of \"%s\".", strerror(errno)); + } + else + { + gg_pubdir_free(h); + netlog("gg_remindpasswordthread(): Password remind successful."); + MessageBox(NULL, TranslateT("Password was sent to your e-mail."), m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + +#ifdef DEBUGMODE + netlog("gg_remindpasswordthread(): End."); +#endif + if (rp) free(rp); +} + +void GGPROTO::remindpassword(uin_t uin, const char *email) +{ + GG_REMIND_PASS *rp = (GG_REMIND_PASS*)malloc(sizeof(GG_REMIND_PASS)); + + rp->uin = uin; + rp->email = email; + forkthread(&GGPROTO::remindpasswordthread, rp); +} diff --git a/protocols/Gadu-Gadu/popups.c b/protocols/Gadu-Gadu/popups.c deleted file mode 100644 index 323bf44590..0000000000 --- a/protocols/Gadu-Gadu/popups.c +++ /dev/null @@ -1,172 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2011-2012 Bartosz Białek -// -// 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 "gg.h" - -typedef struct _tag_PopupData -{ - unsigned flags; - char* title; - char* text; - GGPROTO* gg; -} PopupData; - -///////////////////////////////////////////////////////////////////////////////////////// -// Popup plugin window proc - -LRESULT CALLBACK PopupWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) - { - case WM_COMMAND: - { - PopupData* puData = (PopupData*)PUGetPluginData(hWnd); - if (puData != NULL) - { - if (puData->flags & GG_POPUP_MULTILOGON) - gg_sessions_view(puData->gg, 0, 0); - } - PUDeletePopUp(hWnd); - break; - } - - case WM_CONTEXTMENU: - PUDeletePopUp(hWnd); - break; - - case UM_FREEPLUGINDATA: - { - PopupData* puData = (PopupData*)PUGetPluginData(hWnd); - if (puData != NULL && puData != (PopupData*)CALLSERVICE_NOTFOUND) - { - mir_free(puData->title); - mir_free(puData->text); - mir_free(puData); - } - break; - } - } - - return DefWindowProc(hWnd, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Popup plugin class registration - -void gg_initpopups(GGPROTO* gg) -{ - char szDescr[256], szName[256]; - POPUPCLASS puc = {0}; - - puc.cbSize = sizeof(puc); - puc.PluginWindowProc = PopupWindowProc; - - puc.ptszDescription = szDescr; - puc.pszName = szName; - puc.colorBack = RGB(173, 206, 247); - puc.colorText = GetSysColor(COLOR_WINDOWTEXT); - puc.hIcon = CopyIcon(LoadIconEx("main", FALSE)); - ReleaseIconEx("main", FALSE); - puc.iSeconds = 4; - mir_snprintf(szDescr, SIZEOF(szDescr), "%s/%s", GG_PROTONAME, Translate("Notify")); - mir_snprintf(szName, SIZEOF(szName), "%s_%s", GG_PROTO, "Notify"); - CallService(MS_POPUP_REGISTERCLASS, 0, (WPARAM)&puc); - - puc.ptszDescription = szDescr; - puc.pszName = szName; - puc.colorBack = RGB(191, 0, 0); // Red - puc.colorText = RGB(255, 245, 225); // Yellow - puc.iSeconds = 60; - puc.hIcon = (HICON)LoadImage(NULL, IDI_WARNING, IMAGE_ICON, 0, 0, LR_SHARED); - mir_snprintf(szDescr, SIZEOF(szDescr), "%s/%s", GG_PROTONAME, Translate("Error")); - mir_snprintf(szName, SIZEOF(szName), "%s_%s", GG_PROTO, "Error"); - CallService(MS_POPUP_REGISTERCLASS, 0, (WPARAM)&puc); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Show popup - popup plugin support - -void CALLBACK sttMainThreadCallback(PVOID dwParam) -{ - PopupData* puData = (PopupData*)dwParam; - GGPROTO* gg = puData->gg; - - if (ServiceExists(MS_POPUP_ADDPOPUPCLASS)) - { - char szName[256]; - POPUPDATACLASS ppd = {sizeof(ppd)}; - ppd.ptszTitle = puData->title; - ppd.ptszText = puData->text; - ppd.PluginData = puData; - ppd.pszClassName = szName; - - if (puData->flags & GG_POPUP_ERROR || puData->flags & GG_POPUP_WARNING) - mir_snprintf(szName, SIZEOF(szName), "%s_%s", GG_PROTO, "Error"); - else - mir_snprintf(szName, SIZEOF(szName), "%s_%s", GG_PROTO, "Notify"); - - CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&ppd); - } - else - { - if (puData->flags & GG_POPUP_ALLOW_MSGBOX) - { - BOOL bShow = TRUE; - - if (puData->flags & GG_POPUP_ONCE) - { - HWND hWnd = FindWindow(NULL, GG_PROTONAME); - while (hWnd != NULL) - { - if (FindWindowEx(hWnd, NULL, NULL, puData->text) != NULL) - { - bShow = FALSE; - break; - } - hWnd = FindWindowEx(NULL, hWnd, NULL, GG_PROTONAME); - } - } - - if (bShow) - { - UINT uIcon = puData->flags & GG_POPUP_ERROR ? MB_ICONERROR : puData->flags & GG_POPUP_WARNING ? MB_ICONEXCLAMATION : MB_ICONINFORMATION; - MessageBox(NULL, puData->text, GG_PROTONAME, MB_OK | uIcon); - } - } - mir_free(puData->title); - mir_free(puData->text); - mir_free(puData); - } -} - -void gg_showpopup(GGPROTO* gg, const char* nickname, const char* msg, int flags) -{ - PopupData* puData; - - if (Miranda_Terminated()) return; - - puData = (PopupData*)mir_alloc(sizeof(PopupData)); - puData->flags = flags; - puData->title = mir_strdup(nickname); - puData->text = mir_strdup(msg); - puData->gg = gg; - - CallFunctionAsync(sttMainThreadCallback, puData); -} diff --git a/protocols/Gadu-Gadu/popups.cpp b/protocols/Gadu-Gadu/popups.cpp new file mode 100644 index 0000000000..69ac1c350d --- /dev/null +++ b/protocols/Gadu-Gadu/popups.cpp @@ -0,0 +1,174 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2011-2012 Bartosz Białek +// +// 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 "gg.h" + +struct PopupData +{ + unsigned flags; + TCHAR* title; + TCHAR* text; + GGPROTO* gg; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// Popup plugin window proc + +LRESULT CALLBACK PopupWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_COMMAND: + { + PopupData* puData = (PopupData*)PUGetPluginData(hWnd); + if (puData != NULL) + { + if (puData->flags & GG_POPUP_MULTILOGON) + puData->gg->sessions_view(0, 0); + } + PUDeletePopUp(hWnd); + break; + } + + case WM_CONTEXTMENU: + PUDeletePopUp(hWnd); + break; + + case UM_FREEPLUGINDATA: + { + PopupData* puData = (PopupData*)PUGetPluginData(hWnd); + if (puData != NULL && puData != (PopupData*)CALLSERVICE_NOTFOUND) + { + mir_free(puData->title); + mir_free(puData->text); + mir_free(puData); + } + break; + } + } + + return DefWindowProc(hWnd, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Popup plugin class registration + +void GGPROTO::initpopups() +{ + TCHAR szDescr[256]; + char szName[256]; + + POPUPCLASS puc = {0}; + puc.cbSize = sizeof(puc); + puc.PluginWindowProc = PopupWindowProc; + puc.flags = PCF_TCHAR; + + puc.ptszDescription = szDescr; + puc.pszName = szName; + puc.colorBack = RGB(173, 206, 247); + puc.colorText = GetSysColor(COLOR_WINDOWTEXT); + puc.hIcon = CopyIcon(LoadIconEx("main", FALSE)); + ReleaseIconEx("main", FALSE); + puc.iSeconds = 4; + mir_sntprintf(szDescr, SIZEOF(szDescr), _T("%s/%s"), m_tszUserName, TranslateT("Notify")); + mir_snprintf(szName, SIZEOF(szName), "%s_%s", m_szModuleName, "Notify"); + CallService(MS_POPUP_REGISTERCLASS, 0, (WPARAM)&puc); + + puc.ptszDescription = szDescr; + puc.pszName = szName; + puc.colorBack = RGB(191, 0, 0); // Red + puc.colorText = RGB(255, 245, 225); // Yellow + puc.iSeconds = 60; + puc.hIcon = (HICON)LoadImage(NULL, IDI_WARNING, IMAGE_ICON, 0, 0, LR_SHARED); + mir_sntprintf(szDescr, SIZEOF(szDescr), _T("%s/%s"), m_tszUserName, TranslateT("Error")); + mir_snprintf(szName, SIZEOF(szName), "%s_%s", m_szModuleName, "Error"); + CallService(MS_POPUP_REGISTERCLASS, 0, (WPARAM)&puc); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Show popup - popup plugin support + +void CALLBACK sttMainThreadCallback(PVOID dwParam) +{ + PopupData* puData = (PopupData*)dwParam; + GGPROTO* gg = puData->gg; + + if (ServiceExists(MS_POPUP_ADDPOPUPCLASS)) + { + char szName[256]; + POPUPDATACLASS ppd = {sizeof(ppd)}; + ppd.ptszTitle = puData->title; + ppd.ptszText = puData->text; + ppd.PluginData = puData; + ppd.pszClassName = szName; + + if (puData->flags & GG_POPUP_ERROR || puData->flags & GG_POPUP_WARNING) + mir_snprintf(szName, SIZEOF(szName), "%s_%s", gg->m_szModuleName, "Error"); + else + mir_snprintf(szName, SIZEOF(szName), "%s_%s", gg->m_szModuleName, "Notify"); + + CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&ppd); + } + else + { + if (puData->flags & GG_POPUP_ALLOW_MSGBOX) + { + BOOL bShow = TRUE; + + if (puData->flags & GG_POPUP_ONCE) + { + HWND hWnd = FindWindow(NULL, gg->m_tszUserName); + while (hWnd != NULL) + { + if (FindWindowEx(hWnd, NULL, NULL, puData->text) != NULL) + { + bShow = FALSE; + break; + } + hWnd = FindWindowEx(NULL, hWnd, NULL, gg->m_tszUserName); + } + } + + if (bShow) + { + UINT uIcon = puData->flags & GG_POPUP_ERROR ? MB_ICONERROR : puData->flags & GG_POPUP_WARNING ? MB_ICONEXCLAMATION : MB_ICONINFORMATION; + MessageBox(NULL, puData->text, gg->m_tszUserName, MB_OK | uIcon); + } + } + mir_free(puData->title); + mir_free(puData->text); + mir_free(puData); + } +} + +void GGPROTO::showpopup(const TCHAR* nickname, const TCHAR* msg, int flags) +{ + PopupData* puData; + + if (Miranda_Terminated()) return; + + puData = (PopupData*)mir_alloc(sizeof(PopupData)); + puData->flags = flags; + puData->title = mir_tstrdup(nickname); + puData->text = mir_tstrdup(msg); + puData->gg = this; + + CallFunctionAsync(sttMainThreadCallback, puData); +} diff --git a/protocols/Gadu-Gadu/services.c b/protocols/Gadu-Gadu/services.c deleted file mode 100644 index 420d0666ff..0000000000 --- a/protocols/Gadu-Gadu/services.c +++ /dev/null @@ -1,1021 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2009 Adam Strzelecki -// Copyright (c) 2009-2012 Bartosz Białek -// -// 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 "gg.h" -#include - -////////////////////////////////////////////////////////// -// Status mode -> DB -char *gg_status2db(int status, const char *suffix) -{ - char *prefix; - static char str[64]; - - switch(status) { - case ID_STATUS_AWAY: prefix = "Away"; break; - case ID_STATUS_NA: prefix = "Na"; break; - case ID_STATUS_DND: prefix = "Dnd"; break; - case ID_STATUS_OCCUPIED: prefix = "Occupied"; break; - case ID_STATUS_FREECHAT: prefix = "FreeChat"; break; - case ID_STATUS_ONLINE: prefix = "On"; break; - case ID_STATUS_OFFLINE: prefix = "Off"; break; - case ID_STATUS_INVISIBLE: prefix = "Inv"; break; - case ID_STATUS_ONTHEPHONE: prefix = "Otp"; break; - case ID_STATUS_OUTTOLUNCH: prefix = "Otl"; break; - default: return NULL; - } - strncpy(str, prefix, sizeof(str)); - strncat(str, suffix, sizeof(str) - strlen(str)); - return str; -} - -////////////////////////////////////////////////////////// -// checks proto capabilities -DWORD_PTR gg_getcaps(PROTO_INTERFACE *proto, int type, HANDLE hContact) -{ - switch (type) { - case PFLAGNUM_1: - return PF1_IM | PF1_BASICSEARCH | PF1_EXTSEARCH | PF1_EXTSEARCHUI | PF1_SEARCHBYNAME | - PF1_MODEMSG | PF1_NUMERICUSERID | PF1_VISLIST | PF1_FILE; - case PFLAGNUM_2: - return PF2_ONLINE | PF2_SHORTAWAY | PF2_HEAVYDND | PF2_FREECHAT | PF2_INVISIBLE | - PF2_LONGAWAY; - case PFLAGNUM_3: - return PF2_ONLINE | PF2_SHORTAWAY | PF2_HEAVYDND | PF2_FREECHAT | PF2_INVISIBLE; - case PFLAGNUM_4: - return PF4_NOCUSTOMAUTH | PF4_SUPPORTTYPING | PF4_AVATARS | PF4_IMSENDOFFLINE; - case PFLAGNUM_5: - return PF2_LONGAWAY; - case PFLAG_UNIQUEIDTEXT: - return (DWORD_PTR) Translate("Gadu-Gadu Number"); - case PFLAG_UNIQUEIDSETTING: - return (DWORD_PTR) GG_KEY_UIN; - } - return 0; -} - -////////////////////////////////////////////////////////// -// loads protocol icon -HICON gg_geticon(PROTO_INTERFACE *proto, int iconIndex) -{ - if (LOWORD(iconIndex) == PLI_PROTOCOL) - { - HICON hIcon; - BOOL big; - - if (iconIndex & PLIF_ICOLIBHANDLE) - return (HICON)GetIconHandle(IDI_GG); - - big = (iconIndex & PLIF_SMALL) == 0; - hIcon = LoadIconEx("main", big); - - if (iconIndex & PLIF_ICOLIB) - return hIcon; - - hIcon = CopyIcon(hIcon); - ReleaseIconEx("main", big); - return hIcon; - } - - return (HICON)NULL; -} - -////////////////////////////////////////////////////////// -// gets protocol status -GGINLINE char *gg_getstatusmsg(GGPROTO *gg, int status) -{ - switch(status) - { - case ID_STATUS_ONLINE: - return gg->modemsg.online; - break; - case ID_STATUS_DND: - return gg->modemsg.dnd; - break; - case ID_STATUS_FREECHAT: - return gg->modemsg.freechat; - break; - case ID_STATUS_INVISIBLE: - return gg->modemsg.invisible; - break; - case ID_STATUS_AWAY: - default: - return gg->modemsg.away; - } -} - -////////////////////////////////////////////////////////// -// sets specified protocol status -int gg_refreshstatus(GGPROTO *gg, int status) -{ - if(status == ID_STATUS_OFFLINE) - { - gg_disconnect(gg); - return TRUE; - } - - if (!gg_isonline(gg)) - { - DWORD exitCode = 0; - GetExitCodeThread(gg->pth_sess.hThread, &exitCode); - if (exitCode == STILL_ACTIVE) - return TRUE; -#ifdef DEBUGMODE - gg_netlog(gg, "gg_refreshstatus(): Going to connect..."); -#endif - gg_threadwait(gg, &gg->pth_sess); - gg->pth_sess.hThread = gg_forkthreadex(gg, gg_mainthread, NULL, &gg->pth_sess.dwThreadId); - } - else - { - char *szMsg = NULL; - // Select proper msg - EnterCriticalSection(&gg->modemsg_mutex); - szMsg = mir_strdup(gg_getstatusmsg(gg, status)); - LeaveCriticalSection(&gg->modemsg_mutex); - if(szMsg) - { - gg_netlog(gg, "gg_refreshstatus(): Setting status and away message."); - EnterCriticalSection(&gg->sess_mutex); - gg_change_status_descr(gg->sess, status_m2gg(gg, status, szMsg != NULL), szMsg); - LeaveCriticalSection(&gg->sess_mutex); - } - else - { - gg_netlog(gg, "gg_refreshstatus(): Setting just status."); - EnterCriticalSection(&gg->sess_mutex); - gg_change_status(gg->sess, status_m2gg(gg, status, 0)); - LeaveCriticalSection(&gg->sess_mutex); - } - // Change status of the contact with our own UIN (if got yourself added to the contact list) - gg_changecontactstatus(gg, DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0), status_m2gg(gg, status, szMsg != NULL), szMsg, 0, 0, 0, 0); - gg_broadcastnewstatus(gg, status); - mir_free(szMsg); - } - - return TRUE; -} - -////////////////////////////////////////////////////////// -// normalize gg status -int gg_normalizestatus(int status) -{ - switch(status) - { - case ID_STATUS_ONLINE: - return ID_STATUS_ONLINE; - case ID_STATUS_DND: - return ID_STATUS_DND; - case ID_STATUS_FREECHAT: - return ID_STATUS_FREECHAT; - case ID_STATUS_OFFLINE: - return ID_STATUS_OFFLINE; - case ID_STATUS_INVISIBLE: - return ID_STATUS_INVISIBLE; - default: - return ID_STATUS_AWAY; - } -} - -////////////////////////////////////////////////////////// -// sets protocol status -int gg_setstatus(PROTO_INTERFACE *proto, int iNewStatus) -{ - GGPROTO *gg = (GGPROTO *)proto; - int nNewStatus = gg_normalizestatus(iNewStatus); - - EnterCriticalSection(&gg->modemsg_mutex); - gg->proto.m_iDesiredStatus = nNewStatus; - LeaveCriticalSection(&gg->modemsg_mutex); - - // If waiting for connection retry attempt then signal to stop that - if (gg->hConnStopEvent) SetEvent(gg->hConnStopEvent); - - if (gg->proto.m_iStatus == nNewStatus) return 0; - gg_netlog(gg, "gg_setstatus(): PS_SETSTATUS(%d) normalized to %d.", iNewStatus, nNewStatus); - gg_refreshstatus(gg, nNewStatus); - - return 0; -} - -////////////////////////////////////////////////////////// -// when messsage received -int gg_recvmessage(PROTO_INTERFACE *proto, HANDLE hContact, PROTORECVEVENT *pre) -{ - CCSDATA ccs = { hContact, PSR_MESSAGE, 0, ( LPARAM )pre }; - return CallService(MS_PROTO_RECVMSG, 0, ( LPARAM )&ccs); -} - -////////////////////////////////////////////////////////// -// when messsage sent -typedef struct -{ - HANDLE hContact; - int seq; -} GG_SEQ_ACK; -void __cdecl gg_sendackthread(GGPROTO *gg, void *ack) -{ - SleepEx(100, FALSE); - ProtoBroadcastAck(GG_PROTO, ((GG_SEQ_ACK *)ack)->hContact, - ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE) ((GG_SEQ_ACK *)ack)->seq, 0); - mir_free(ack); -} -int gg_sendmessage(PROTO_INTERFACE *proto, HANDLE hContact, int flags, const char *msg) -{ - GGPROTO *gg = (GGPROTO *)proto; - uin_t uin; - - if (msg && gg_isonline(gg) && (uin = (uin_t)DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0))) - { - int seq; - EnterCriticalSection(&gg->sess_mutex); - seq = gg_send_message(gg->sess, GG_CLASS_CHAT, uin, msg); - LeaveCriticalSection(&gg->sess_mutex); - if (!DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_MSGACK, GG_KEYDEF_MSGACK)) - { - // Auto-ack message without waiting for server ack - GG_SEQ_ACK *ack = mir_alloc(sizeof(GG_SEQ_ACK)); - if (ack) - { - ack->seq = seq; - ack->hContact = hContact; - gg_forkthread(gg, gg_sendackthread, ack); - } - } - return seq; - } - return 0; -} - -////////////////////////////////////////////////////////// -// when basic search -void __cdecl gg_searchthread(GGPROTO *gg, void *empty) -{ - SleepEx(100, FALSE); - gg_netlog(gg, "gg_searchthread(): Failed search."); - ProtoBroadcastAck(GG_PROTO, NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)1, 0); -} -HANDLE gg_basicsearch(PROTO_INTERFACE *proto, const PROTOCHAR *id) -{ - GGPROTO *gg = (GGPROTO *)proto; - gg_pubdir50_t req; - char *ida; - - if (!gg_isonline(gg)) - return (HANDLE)0; - - if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) - { - gg_forkthread(gg, gg_searchthread, NULL); - return (HANDLE)1; - } - - ida = gg_t2a(id); - - // Add uin and search it - gg_pubdir50_add(req, GG_PUBDIR50_UIN, ida); - gg_pubdir50_seq_set(req, GG_SEQ_SEARCH); - - mir_free(ida); - - EnterCriticalSection(&gg->sess_mutex); - if (!gg_pubdir50(gg->sess, req)) - { - LeaveCriticalSection(&gg->sess_mutex); - gg_forkthread(gg, gg_searchthread, NULL); - return (HANDLE)1; - } - LeaveCriticalSection(&gg->sess_mutex); - gg_netlog(gg, "gg_basicsearch(): Seq %d.", req->seq); - gg_pubdir50_free(req); - - return (HANDLE)1; -} -static HANDLE gg_searchbydetails(PROTO_INTERFACE *proto, const PROTOCHAR *nick, const PROTOCHAR *firstName, const PROTOCHAR *lastName) -{ - GGPROTO *gg = (GGPROTO *)proto; - gg_pubdir50_t req; - unsigned long crc; - char data[512] = "\0"; - - // Check if connected and if there's a search data - if (!gg_isonline(gg)) - return 0; - - if (!nick && !firstName && !lastName) - return 0; - - if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) - { - gg_forkthread(gg, gg_searchthread, NULL); - return (HANDLE)1; - } - - // Add uin and search it - if(nick) - { - char *nickA = gg_t2a(nick); - gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, nickA); - strncat(data, nickA, sizeof(data) - strlen(data)); - mir_free(nickA); - } - strncat(data, ".", sizeof(data) - strlen(data)); - - if(firstName) - { - char *firstNameA = gg_t2a(firstName); - gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, firstNameA); - strncat(data, firstNameA, sizeof(data) - strlen(data)); - mir_free(firstNameA); - } - strncat(data, ".", sizeof(data) - strlen(data)); - - if(lastName) - { - char *lastNameA = gg_t2a(lastName); - gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, lastNameA); - strncat(data, lastNameA, sizeof(data) - strlen(data)); - mir_free(lastNameA); - } - strncat(data, ".", sizeof(data) - strlen(data)); - - // Count crc & check if the data was equal if yes do same search with shift - crc = crc_get(data); - - if(crc == gg->last_crc && gg->next_uin) - gg_pubdir50_add(req, GG_PUBDIR50_START, ditoa(gg->next_uin)); - else - gg->last_crc = crc; - - gg_pubdir50_seq_set(req, GG_SEQ_SEARCH); - - EnterCriticalSection(&gg->sess_mutex); - if (!gg_pubdir50(gg->sess, req)) - { - LeaveCriticalSection(&gg->sess_mutex); - gg_forkthread(gg, gg_searchthread, NULL); - return (HANDLE)1; - } - LeaveCriticalSection(&gg->sess_mutex); - gg_netlog(gg, "gg_searchbyname(): Seq %d.", req->seq); - gg_pubdir50_free(req); - - return (HANDLE)1; -} - -////////////////////////////////////////////////////////// -// when contact is added to list -HANDLE gg_addtolist(PROTO_INTERFACE *proto, int flags, PROTOSEARCHRESULT *psr) -{ - GGPROTO *gg = (GGPROTO *)proto; - GGSEARCHRESULT *sr = (GGSEARCHRESULT *)psr; - char *szNick = psr->flags & PSR_UNICODE ? mir_u2a((wchar_t *)sr->hdr.nick) : mir_strdup(sr->hdr.nick); - uin_t uin; - HANDLE hContact; - - if (psr->cbSize == sizeof(GGSEARCHRESULT)) - uin = sr->uin; - else - uin = psr->flags & PSR_UNICODE ? _wtoi((wchar_t*)psr->id) : atoi(psr->id); - - hContact = gg_getcontact(gg, uin, 1, flags & PALF_TEMPORARY ? 0 : 1, szNick); - mir_free(szNick); - - return hContact; -} - -////////////////////////////////////////////////////////// -// user info request -void __cdecl gg_cmdgetinfothread(GGPROTO *gg, void *hContact) -{ - SleepEx(100, FALSE); - gg_netlog(gg, "gg_cmdgetinfothread(): Failed info retreival."); - ProtoBroadcastAck(GG_PROTO, hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE) 1, 0); -} -int gg_getinfo(PROTO_INTERFACE *proto, HANDLE hContact, int infoType) -{ - GGPROTO *gg = (GGPROTO *)proto; - gg_pubdir50_t req; - - // Custom contact info - if(hContact) - { - if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) - { - gg_forkthread(gg, gg_cmdgetinfothread, hContact); - return 1; - } - - // Add uin and search it - gg_pubdir50_add(req, GG_PUBDIR50_UIN, ditoa((uin_t)DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0))); - gg_pubdir50_seq_set(req, GG_SEQ_INFO); - - gg_netlog(gg, "gg_getinfo(): Requesting user info.", req->seq); - if(gg_isonline(gg)) - { - EnterCriticalSection(&gg->sess_mutex); - if (!gg_pubdir50(gg->sess, req)) - { - LeaveCriticalSection(&gg->sess_mutex); - gg_forkthread(gg, gg_cmdgetinfothread, hContact); - return 1; - } - LeaveCriticalSection(&gg->sess_mutex); - } - } - // Own contact info - else - { - if (!(req = gg_pubdir50_new(GG_PUBDIR50_READ))) - { - gg_forkthread(gg, gg_cmdgetinfothread, hContact); - return 1; - } - - // Add seq - gg_pubdir50_seq_set(req, GG_SEQ_CHINFO); - - gg_netlog(gg, "gg_getinfo(): Requesting owner info.", req->seq); - if(gg_isonline(gg)) - { - EnterCriticalSection(&gg->sess_mutex); - if (!gg_pubdir50(gg->sess, req)) - { - LeaveCriticalSection(&gg->sess_mutex); - gg_forkthread(gg, gg_cmdgetinfothread, hContact); - return 1; - } - LeaveCriticalSection(&gg->sess_mutex); - } - } - gg_netlog(gg, "gg_getinfo(): Seq %d.", req->seq); - gg_pubdir50_free(req); - - return 1; -} - -////////////////////////////////////////////////////////// -// when away message is requested -void __cdecl gg_getawaymsgthread(GGPROTO *gg, void *hContact) -{ - DBVARIANT dbv; - - SleepEx(100, FALSE); - if (!DBGetContactSettingTString(hContact, "CList", GG_KEY_STATUSDESCR, &dbv)) - { - ProtoBroadcastAck(GG_PROTO, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE) 1, (LPARAM) dbv.ptszVal); - gg_netlog(gg, "gg_getawaymsg(): Reading away msg <" TCHAR_STR_PARAM ">.", dbv.ptszVal); - DBFreeVariant(&dbv); - } - else - ProtoBroadcastAck(GG_PROTO, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE) 1, (LPARAM) NULL); -} -HANDLE gg_getawaymsg(PROTO_INTERFACE *proto, HANDLE hContact) -{ - gg_forkthread((GGPROTO *)proto, gg_getawaymsgthread, hContact); - - return (HANDLE)1; -} - -////////////////////////////////////////////////////////// -// when away message is being set -int gg_setawaymsg(PROTO_INTERFACE *proto, int iStatus, const PROTOCHAR *msgt) -{ - GGPROTO *gg = (GGPROTO *)proto; - int status = gg_normalizestatus(iStatus); - char **szMsg; - char *msg = gg_t2a(msgt); - - gg_netlog(gg, "gg_setawaymsg(): PS_SETAWAYMSG(%d, \"%s\").", iStatus, msg); - - EnterCriticalSection(&gg->modemsg_mutex); - // Select proper msg - switch(status) - { - case ID_STATUS_ONLINE: - szMsg = &gg->modemsg.online; - break; - case ID_STATUS_AWAY: - szMsg = &gg->modemsg.away; - break; - case ID_STATUS_DND: - szMsg = &gg->modemsg.dnd; - break; - case ID_STATUS_FREECHAT: - szMsg = &gg->modemsg.freechat; - break; - case ID_STATUS_INVISIBLE: - szMsg = &gg->modemsg.invisible; - break; - default: - LeaveCriticalSection(&gg->modemsg_mutex); - mir_free(msg); - return 1; - } - - // Check if we change status here somehow - if (*szMsg && msg && !strcmp(*szMsg, msg) - || !*szMsg && (!msg || !*msg)) - { - if (status == gg->proto.m_iDesiredStatus && gg->proto.m_iDesiredStatus == gg->proto.m_iStatus) - { - gg_netlog(gg, "gg_setawaymsg(): Message hasn't been changed, return."); - LeaveCriticalSection(&gg->modemsg_mutex); - mir_free(msg); - return 0; - } - } - else - { - if (*szMsg) - mir_free(*szMsg); - *szMsg = msg && *msg ? mir_strdup(msg) : NULL; -#ifdef DEBUGMODE - gg_netlog(gg, "gg_setawaymsg(): Message changed."); -#endif - } - LeaveCriticalSection(&gg->modemsg_mutex); - - // Change the status if it was desired by PS_SETSTATUS - if (status == gg->proto.m_iDesiredStatus) - gg_refreshstatus(gg, status); - - mir_free(msg); - return 0; -} - -////////////////////////////////////////////////////////// -// visible lists -int gg_setapparentmode(PROTO_INTERFACE *proto, HANDLE hContact, int mode) -{ - GGPROTO *gg = (GGPROTO *)proto; - DBWriteContactSettingWord(hContact, GG_PROTO, GG_KEY_APPARENT, (WORD)mode); - gg_notifyuser(gg, hContact, 1); - return 0; -} - -////////////////////////////////////////////////////////// -// create adv search dialog proc -INT_PTR CALLBACK gg_advancedsearchdlgproc(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam) -{ - switch(message) - { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)_T("")); // 0 - SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)Translate("Female")); // 1 - SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)Translate("Male")); // 2 - return TRUE; - case WM_COMMAND: - switch(LOWORD(wParam)) { - case IDOK: - SendMessage(GetParent(hwndDlg), WM_COMMAND,MAKEWPARAM(IDOK,BN_CLICKED), (LPARAM)GetDlgItem(GetParent(hwndDlg),IDOK)); - break; - case IDCANCEL: -// CheckDlgButton(GetParent(hwndDlg),IDC_ADVANCED,BST_UNCHECKED); -// SendMessage(GetParent(hwndDlg),WM_COMMAND,MAKEWPARAM(IDC_ADVANCED,BN_CLICKED),(LPARAM)GetDlgItem(GetParent(hwndDlg),IDC_ADVANCED)); - break; - } - break; - } - return FALSE; -} - -////////////////////////////////////////////////////////// -// create adv search dialog -HWND gg_createadvsearchui(PROTO_INTERFACE *proto, HWND owner) -{ - return CreateDialogParam(hInstance, - MAKEINTRESOURCE(IDD_GGADVANCEDSEARCH), owner, gg_advancedsearchdlgproc, (LPARAM)(GGPROTO *)proto); -} - -////////////////////////////////////////////////////////// -// search by advanced -HWND gg_searchbyadvanced(PROTO_INTERFACE *proto, HWND hwndDlg) -{ - GGPROTO *gg = (GGPROTO *)proto; - gg_pubdir50_t req; - char text[64], data[512] = "\0"; - unsigned long crc; - - // Check if connected - if (!gg_isonline(gg)) return (HWND)0; - - if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) - { - gg_forkthread(gg, gg_searchthread, NULL); - return (HWND)1; - } - - // Fetch search data - GetDlgItemText(hwndDlg, IDC_FIRSTNAME, text, sizeof(text)); - if(strlen(text)) - { - gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, text); - strncat(data, text, sizeof(data) - strlen(data)); - } - /* 1 */ strncat(data, ".", sizeof(data) - strlen(data)); - - GetDlgItemText(hwndDlg, IDC_LASTNAME, text, sizeof(text)); - if(strlen(text)) - { - gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, text); - strncat(data, text, sizeof(data) - strlen(data)); - } - /* 2 */ strncat(data, ".", sizeof(data) - strlen(data)); - - GetDlgItemText(hwndDlg, IDC_NICKNAME, text, sizeof(text)); - if(strlen(text)) - { - gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, text); - strncat(data, text, sizeof(data) - strlen(data)); - } - /* 3 */ strncat(data, ".", sizeof(data) - strlen(data)); - - GetDlgItemText(hwndDlg, IDC_CITY, text, sizeof(text)); - if(strlen(text)) - { - gg_pubdir50_add(req, GG_PUBDIR50_CITY, text); - strncat(data, text, sizeof(data) - strlen(data)); - } - /* 4 */ strncat(data, ".", sizeof(data) - strlen(data)); - - GetDlgItemText(hwndDlg, IDC_AGEFROM, text, sizeof(text)); - if(strlen(text)) - { - int yearTo = atoi(text); - int yearFrom; - time_t t = time(NULL); - struct tm *lt = localtime(&t); - int ay = lt->tm_year + 1900; - char age[16]; - - GetDlgItemText(hwndDlg, IDC_AGETO, age, sizeof(age)); - yearFrom = atoi(age); - - // Count & fix ranges - if (!yearTo) - yearTo = ay; - else - yearTo = ay - yearTo; - if (!yearFrom) - yearFrom = 0; - else - yearFrom = ay - yearFrom; - mir_snprintf(text, sizeof(text), "%d %d", yearFrom, yearTo); - - gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, text); - strncat(data, text, sizeof(data) - strlen(data)); - } - /* 5 */ strncat(data, ".", sizeof(data) - strlen(data)); - - switch(SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_GETCURSEL, 0, 0)) - { - case 1: - gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_FEMALE); - strncat(data, GG_PUBDIR50_GENDER_MALE, sizeof(data) - strlen(data)); - break; - case 2: - gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_MALE); - strncat(data, GG_PUBDIR50_GENDER_FEMALE, sizeof(data) - strlen(data)); - break; - } - /* 6 */ strncat(data, ".", sizeof(data) - strlen(data)); - - if(IsDlgButtonChecked(hwndDlg, IDC_ONLYCONNECTED)) - { - gg_pubdir50_add(req, GG_PUBDIR50_ACTIVE, GG_PUBDIR50_ACTIVE_TRUE); - strncat(data, GG_PUBDIR50_ACTIVE_TRUE, sizeof(data) - strlen(data)); - } - /* 7 */ strncat(data, ".", sizeof(data) - strlen(data)); - - // No data entered - if(strlen(data) <= 7 || (strlen(data) == 8 && IsDlgButtonChecked(hwndDlg, IDC_ONLYCONNECTED))) return (HWND)0; - - // Count crc & check if the data was equal if yes do same search with shift - crc = crc_get(data); - - if(crc == gg->last_crc && gg->next_uin) - gg_pubdir50_add(req, GG_PUBDIR50_START, ditoa(gg->next_uin)); - else - gg->last_crc = crc; - - gg_pubdir50_seq_set(req, GG_SEQ_SEARCH); - - if(gg_isonline(gg)) - { - EnterCriticalSection(&gg->sess_mutex); - if (!gg_pubdir50(gg->sess, req)) - { - LeaveCriticalSection(&gg->sess_mutex); - gg_forkthread(gg, gg_searchthread, NULL); - return (HWND)1; - } - LeaveCriticalSection(&gg->sess_mutex); - } - gg_netlog(gg, "gg_searchbyadvanced(): Seq %d.", req->seq); - gg_pubdir50_free(req); - - return (HWND)1; -} - -////////////////////////////////////////////////////////// -// gets avatar capabilities -INT_PTR gg_getavatarcaps(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - switch (wParam) { - case AF_MAXSIZE: - ((POINT *)lParam)->x = ((POINT *)lParam)->y = 200; - return 0; - case AF_FORMATSUPPORTED: - return (lParam == PA_FORMAT_JPEG || lParam == PA_FORMAT_GIF || lParam == PA_FORMAT_PNG); - case AF_ENABLED: - return DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS); - case AF_DONTNEEDDELAYS: - return 1; - case AF_MAXFILESIZE: - return 307200; - case AF_FETCHALWAYS: - return 1; - } - return 0; -} - -////////////////////////////////////////////////////////// -// gets avatar information -INT_PTR gg_getavatarinfo(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - PROTO_AVATAR_INFORMATIONT *pai = (PROTO_AVATAR_INFORMATIONT *)lParam; - char *AvatarHash = NULL, *AvatarSavedHash = NULL; - TCHAR *AvatarURL = NULL; - INT_PTR result = GAIR_NOAVATAR; - DBVARIANT dbv; - uin_t uin = (uin_t)DBGetContactSettingDword(pai->hContact, GG_PROTO, GG_KEY_UIN, 0); - - gg_netlog(gg, "gg_getavatarinfo(): Requesting avatar information for %d.", uin); - - pai->filename[0] = 0; - pai->format = PA_FORMAT_UNKNOWN; - - if (!uin || !DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) - return GAIR_NOAVATAR; - - if (!DBGetContactSettingByte(pai->hContact, GG_PROTO, GG_KEY_AVATARREQUESTED, GG_KEYDEF_AVATARREQUESTED)) { - gg_requestavatar(gg, pai->hContact, 1); - return (wParam & GAIF_FORCE) != 0 ? GAIR_WAITFOR : GAIR_NOAVATAR; - } - DBDeleteContactSetting(pai->hContact, GG_PROTO, GG_KEY_AVATARREQUESTED); - - pai->format = DBGetContactSettingByte(pai->hContact, GG_PROTO, GG_KEY_AVATARTYPE, GG_KEYDEF_AVATARTYPE); - - if (!DBGetContactSettingTString(pai->hContact, GG_PROTO, GG_KEY_AVATARURL, &dbv)) { - AvatarURL = mir_tstrdup(dbv.ptszVal); - DBFreeVariant(&dbv); - } - - if (AvatarURL != NULL && _tcslen(AvatarURL) > 0) { - TCHAR *AvatarName = _tcsrchr(AvatarURL, '/'); - AvatarName++; - AvatarHash = gg_avatarhash(AvatarName); - } - - if (!DBGetContactSettingString(pai->hContact, GG_PROTO, GG_KEY_AVATARHASH, &dbv)) { - AvatarSavedHash = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - - if (AvatarHash != NULL && AvatarSavedHash != NULL) { - gg_getavatarfilename(gg, pai->hContact, pai->filename, sizeof(pai->filename)); - if (!strcmp(AvatarHash, AvatarSavedHash) && !_access(pai->filename, 0)) { - result = GAIR_SUCCESS; - } - else if ((wParam & GAIF_FORCE) != 0) { - gg_netlog(gg, "gg_getavatarinfo(): Contact %d changed avatar.", uin); - remove(pai->filename); - DBWriteContactSettingString(pai->hContact, GG_PROTO, GG_KEY_AVATARHASH, AvatarHash); - gg_getavatar(gg, pai->hContact, AvatarURL); - result = GAIR_WAITFOR; - } - } - else if ((wParam & GAIF_FORCE) != 0) { - if (AvatarHash == NULL && AvatarSavedHash != NULL) { - gg_netlog(gg, "gg_getavatarinfo(): Contact %d deleted avatar.", uin); - gg_getavatarfilename(gg, pai->hContact, pai->filename, sizeof(pai->filename)); - remove(pai->filename); - DBDeleteContactSetting(pai->hContact, GG_PROTO, GG_KEY_AVATARHASH); - DBDeleteContactSetting(pai->hContact, GG_PROTO, GG_KEY_AVATARURL); - DBDeleteContactSetting(pai->hContact, GG_PROTO, GG_KEY_AVATARTYPE); - } - else if (AvatarHash != NULL && AvatarSavedHash == NULL) { - gg_netlog(gg, "gg_getavatarinfo(): Contact %d set avatar.", uin); - DBWriteContactSettingString(pai->hContact, GG_PROTO, GG_KEY_AVATARHASH, AvatarHash); - gg_getavatar(gg, pai->hContact, AvatarURL); - result = GAIR_WAITFOR; - } - } - - mir_free(AvatarHash); - mir_free(AvatarSavedHash); - mir_free(AvatarURL); - - return result; -} - -////////////////////////////////////////////////////////// -// gets avatar -INT_PTR gg_getmyavatar(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - TCHAR *szFilename = (TCHAR*)wParam; - int len = (int)lParam; - - gg_netlog(gg, "gg_getmyavatar(): Requesting user avatar."); - - if (szFilename == NULL || len <= 0) - return -1; - - if (!DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) - return -2; - - gg_getavatarfilename(gg, NULL, szFilename, len); - return _access(szFilename, 0); -} - -////////////////////////////////////////////////////////// -// sets avatar -INT_PTR gg_setmyavatar(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - char *szFilename = (char *)lParam; - - if (!DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) - return -2; - - if (szFilename == NULL) { - MessageBox( - NULL, - Translate("To remove your Gadu-Gadu avatar, you must use the MojaGeneracja.pl website."), - GG_PROTONAME, MB_OK | MB_ICONINFORMATION); - return -1; - } - else { - char szMyFilename[MAX_PATH]; - gg_getavatarfilename(gg, NULL, szMyFilename, sizeof(szMyFilename)); - if (strcmp(szFilename, szMyFilename) && !CopyFileA(szFilename, szMyFilename, FALSE)) { - gg_netlog(gg, "gg_setmyavatar(): Failed to set user avatar. File %s could not be created/overwritten.", szMyFilename); - return -1; - } - gg_setavatar(gg, szMyFilename); - } - - return 0; -} - -////////////////////////////////////////////////////////// -// gets protocol status message -INT_PTR gg_getmyawaymsg(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - INT_PTR res = 0; - char *szMsg; - - EnterCriticalSection(&gg->modemsg_mutex); - szMsg = gg_getstatusmsg(gg, wParam ? gg_normalizestatus(wParam) : gg->proto.m_iStatus); - if(gg_isonline(gg) && szMsg) - res = (lParam & SGMA_UNICODE) ? (INT_PTR)mir_a2u(szMsg) : (INT_PTR)mir_strdup(szMsg); - LeaveCriticalSection(&gg->modemsg_mutex); - return res; -} - -////////////////////////////////////////////////////////// -// gets account manager GUI -extern INT_PTR CALLBACK gg_acc_mgr_guidlgproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); - -static INT_PTR gg_get_acc_mgr_gui(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - return (INT_PTR) CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_ACCMGRUI), (HWND)lParam, gg_acc_mgr_guidlgproc, (LPARAM) gg); -} - -////////////////////////////////////////////////////////// -// leaves (terminates) conference -INT_PTR gg_leavechat(GGPROTO *gg, WPARAM wParam, LPARAM lParam) -{ - HANDLE hContact = (HANDLE)wParam; - if(hContact) - CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0); - - return 0; -} - -////////////////////////////////////////////////////////// -// sends a notification that the user is typing a message -int gg_useristyping(PROTO_INTERFACE *proto, HANDLE hContact, int type) -{ - GGPROTO *gg = (GGPROTO *)proto; - uin_t uin = DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0); - - if (!uin || !gg_isonline(gg)) return 0; - - if (type == PROTOTYPE_SELFTYPING_ON || type == PROTOTYPE_SELFTYPING_OFF) { - EnterCriticalSection(&gg->sess_mutex); - gg_typing_notification(gg->sess, uin, (type == PROTOTYPE_SELFTYPING_ON)); - LeaveCriticalSection(&gg->sess_mutex); - } - - return 0; -} - -////////////////////////////////////////////////////////// -// Dummies for function that have to be implemented - -HANDLE gg_dummy_addtolistbyevent(PROTO_INTERFACE *proto, int flags, int iContact, HANDLE hDbEvent) { return NULL; } -int gg_dummy_authorize(PROTO_INTERFACE *proto, HANDLE hContact) { return 0; } -int gg_dummy_authdeny(PROTO_INTERFACE *proto, HANDLE hContact, const TCHAR *szReason) { return 0; } -int gg_dummy_authrecv(PROTO_INTERFACE *proto, HANDLE hContact, PROTORECVEVENT *pre) { return 0; } -int gg_dummy_authrequest(PROTO_INTERFACE *proto, HANDLE hContact, const TCHAR *szMessage) { return 0; } -HANDLE gg_dummy_changeinfo(PROTO_INTERFACE *proto, int iInfoType, void *pInfoData) { return NULL; } -int gg_dummy_fileresume(PROTO_INTERFACE *proto, HANDLE hTransfer, int *action, const PROTOCHAR** szFilename) { return 0; } -HANDLE gg_dummy_searchbyemail(PROTO_INTERFACE *proto, const PROTOCHAR *email) { return NULL; } -int gg_dummy_recvcontacts(PROTO_INTERFACE *proto, HANDLE hContact, PROTORECVEVENT *pre) { return 0; } -int gg_dummy_recvurl(PROTO_INTERFACE *proto, HANDLE hContact, PROTORECVEVENT *pre) { return 0; } -int gg_dummy_sendcontacts(PROTO_INTERFACE *proto, HANDLE hContact, int flags, int nContacts, HANDLE *hContactsList) { return 0; } -int gg_dummy_sendurl(PROTO_INTERFACE *proto, HANDLE hContact, int flags, const char *url) { return 0; } -int gg_dummy_recvawaymsg(PROTO_INTERFACE *proto, HANDLE hContact, int mode, PROTORECVEVENT *evt) { return 0; } -int gg_dummy_sendawaymsg(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hProcess, const char *msg) { return 0; } - -////////////////////////////////////////////////////////// -// Register services -void gg_registerservices(GGPROTO *gg) -{ - gg->proto.vtbl->AddToList = gg_addtolist; - gg->proto.vtbl->AddToListByEvent = gg_dummy_addtolistbyevent; - - gg->proto.vtbl->Authorize = gg_dummy_authorize; - gg->proto.vtbl->AuthDeny = gg_dummy_authdeny; - gg->proto.vtbl->AuthRecv = gg_dummy_authrecv; - gg->proto.vtbl->AuthRequest = gg_dummy_authrequest; - - gg->proto.vtbl->ChangeInfo = gg_dummy_changeinfo; - - gg->proto.vtbl->FileAllow = gg_fileallow; - gg->proto.vtbl->FileCancel = gg_filecancel; - gg->proto.vtbl->FileDeny = gg_filedeny; - gg->proto.vtbl->FileResume = gg_dummy_fileresume; - - gg->proto.vtbl->GetCaps = gg_getcaps; - gg->proto.vtbl->GetIcon = gg_geticon; - gg->proto.vtbl->GetInfo = gg_getinfo; - - gg->proto.vtbl->SearchBasic = gg_basicsearch; - gg->proto.vtbl->SearchByEmail = gg_dummy_searchbyemail; - gg->proto.vtbl->SearchByName = gg_searchbydetails; - gg->proto.vtbl->SearchAdvanced = gg_searchbyadvanced; - gg->proto.vtbl->CreateExtendedSearchUI = gg_createadvsearchui; - - gg->proto.vtbl->RecvContacts = gg_dummy_recvcontacts; - gg->proto.vtbl->RecvFile = gg_recvfile; - gg->proto.vtbl->RecvMsg = gg_recvmessage; - gg->proto.vtbl->RecvUrl = gg_dummy_recvurl; - - gg->proto.vtbl->SendContacts = gg_dummy_sendcontacts; - gg->proto.vtbl->SendFile = gg_sendfile; - gg->proto.vtbl->SendMsg = gg_sendmessage; - gg->proto.vtbl->SendUrl = gg_dummy_sendurl; - - gg->proto.vtbl->SetApparentMode = gg_setapparentmode; - gg->proto.vtbl->SetStatus = gg_setstatus; - - gg->proto.vtbl->GetAwayMsg = gg_getawaymsg; - gg->proto.vtbl->RecvAwayMsg = gg_dummy_recvawaymsg; - gg->proto.vtbl->SendAwayMsg = gg_dummy_sendawaymsg; - gg->proto.vtbl->SetAwayMsg = gg_setawaymsg; - - gg->proto.vtbl->UserIsTyping = gg_useristyping; - - gg->proto.vtbl->OnEvent = gg_event; - - CreateProtoService(PS_GETAVATARCAPS, gg_getavatarcaps, gg); - CreateProtoService(PS_GETAVATARINFOT, gg_getavatarinfo, gg); - CreateProtoService(PS_GETMYAVATAR, gg_getmyavatar, gg); - CreateProtoService(PS_SETMYAVATAR, gg_setmyavatar, gg); - - CreateProtoService(PS_GETMYAWAYMSG, gg_getmyawaymsg, gg); - CreateProtoService(PS_CREATEACCMGRUI, gg_get_acc_mgr_gui, gg); - - CreateProtoService(PS_LEAVECHAT, gg_leavechat, gg); -} diff --git a/protocols/Gadu-Gadu/services.cpp b/protocols/Gadu-Gadu/services.cpp new file mode 100644 index 0000000000..921553fadb --- /dev/null +++ b/protocols/Gadu-Gadu/services.cpp @@ -0,0 +1,330 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// Copyright (c) 2009-2012 Bartosz Białek +// +// 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 "gg.h" +#include + +////////////////////////////////////////////////////////// +// Status mode -> DB +char *gg_status2db(int status, const char *suffix) +{ + char *prefix; + static char str[64]; + + switch(status) { + case ID_STATUS_AWAY: prefix = "Away"; break; + case ID_STATUS_NA: prefix = "Na"; break; + case ID_STATUS_DND: prefix = "Dnd"; break; + case ID_STATUS_OCCUPIED: prefix = "Occupied"; break; + case ID_STATUS_FREECHAT: prefix = "FreeChat"; break; + case ID_STATUS_ONLINE: prefix = "On"; break; + case ID_STATUS_OFFLINE: prefix = "Off"; break; + case ID_STATUS_INVISIBLE: prefix = "Inv"; break; + case ID_STATUS_ONTHEPHONE: prefix = "Otp"; break; + case ID_STATUS_OUTTOLUNCH: prefix = "Otl"; break; + default: return NULL; + } + strncpy(str, prefix, sizeof(str)); + strncat(str, suffix, sizeof(str) - strlen(str)); + return str; +} + +////////////////////////////////////////////////////////// +// gets protocol status + +char* GGPROTO::getstatusmsg(int status) +{ + switch(status) { + case ID_STATUS_ONLINE: + return modemsg.online; + break; + case ID_STATUS_DND: + return modemsg.dnd; + break; + case ID_STATUS_FREECHAT: + return modemsg.freechat; + break; + case ID_STATUS_INVISIBLE: + return modemsg.invisible; + break; + case ID_STATUS_AWAY: + default: + return modemsg.away; + } +} + +////////////////////////////////////////////////////////// +// sets specified protocol status + +int GGPROTO::refreshstatus(int status) +{ + if (status == ID_STATUS_OFFLINE) + { + disconnect(); + return TRUE; + } + + if (!isonline()) + { + DWORD exitCode = 0; + GetExitCodeThread(pth_sess.hThread, &exitCode); + if (exitCode == STILL_ACTIVE) + return TRUE; +#ifdef DEBUGMODE + netlog("gg_refreshstatus(): Going to connect..."); +#endif + threadwait(&pth_sess); + pth_sess.hThread = forkthreadex(&GGPROTO::mainthread, NULL, &pth_sess.dwThreadId); + } + else + { + char *szMsg = NULL; + // Select proper msg + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(getstatusmsg(status)); + LeaveCriticalSection(&modemsg_mutex); + if (szMsg) + { + netlog("gg_refreshstatus(): Setting status and away message."); + EnterCriticalSection(&sess_mutex); + gg_change_status_descr(sess, status_m2gg(status, szMsg != NULL), szMsg); + LeaveCriticalSection(&sess_mutex); + } + else + { + netlog("gg_refreshstatus(): Setting just status."); + EnterCriticalSection(&sess_mutex); + gg_change_status(sess, status_m2gg(status, 0)); + LeaveCriticalSection(&sess_mutex); + } + // Change status of the contact with our own UIN (if got yourself added to the contact list) + changecontactstatus(db_get_b(NULL, m_szModuleName, GG_KEY_UIN, 0), status_m2gg(status, szMsg != NULL), szMsg, 0, 0, 0, 0); + broadcastnewstatus(status); + mir_free(szMsg); + } + + return TRUE; +} + +////////////////////////////////////////////////////////// +// normalize gg status + +int gg_normalizestatus(int status) +{ + switch(status) { + case ID_STATUS_ONLINE: return ID_STATUS_ONLINE; + case ID_STATUS_DND: return ID_STATUS_DND; + case ID_STATUS_FREECHAT: return ID_STATUS_FREECHAT; + case ID_STATUS_OFFLINE: return ID_STATUS_OFFLINE; + case ID_STATUS_INVISIBLE: return ID_STATUS_INVISIBLE; + } + return ID_STATUS_AWAY; +} + +////////////////////////////////////////////////////////// +// gets avatar capabilities + +INT_PTR GGPROTO::getavatarcaps(WPARAM wParam, LPARAM lParam) +{ + switch (wParam) { + case AF_MAXSIZE: + ((POINT *)lParam)->x = ((POINT *)lParam)->y = 200; + return 0; + case AF_FORMATSUPPORTED: + return (lParam == PA_FORMAT_JPEG || lParam == PA_FORMAT_GIF || lParam == PA_FORMAT_PNG); + case AF_ENABLED: + return db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS); + case AF_DONTNEEDDELAYS: + return 1; + case AF_MAXFILESIZE: + return 307200; + case AF_FETCHALWAYS: + return 1; + } + return 0; +} + +////////////////////////////////////////////////////////// +// gets avatar information + +INT_PTR GGPROTO::getavatarinfo(WPARAM wParam, LPARAM lParam) +{ + PROTO_AVATAR_INFORMATIONT *pai = (PROTO_AVATAR_INFORMATIONT *)lParam; + char *AvatarHash = NULL, *AvatarSavedHash = NULL; + char *AvatarURL = NULL; + INT_PTR result = GAIR_NOAVATAR; + DBVARIANT dbv; + uin_t uin = (uin_t)db_get_b(pai->hContact, m_szModuleName, GG_KEY_UIN, 0); + + netlog("gg_getavatarinfo(): Requesting avatar information for %d.", uin); + + pai->filename[0] = 0; + pai->format = PA_FORMAT_UNKNOWN; + + if (!uin || !db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) + return GAIR_NOAVATAR; + + if (!db_get_b(pai->hContact, m_szModuleName, GG_KEY_AVATARREQUESTED, GG_KEYDEF_AVATARREQUESTED)) { + requestAvatar(pai->hContact, 1); + return (wParam & GAIF_FORCE) != 0 ? GAIR_WAITFOR : GAIR_NOAVATAR; + } + db_unset(pai->hContact, m_szModuleName, GG_KEY_AVATARREQUESTED); + + pai->format = db_get_b(pai->hContact, m_szModuleName, GG_KEY_AVATARTYPE, GG_KEYDEF_AVATARTYPE); + + if (!db_get_s(pai->hContact, m_szModuleName, GG_KEY_AVATARURL, &dbv, DBVT_ASCIIZ)) { + AvatarURL = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + + if (AvatarURL != NULL && strlen(AvatarURL) > 0) { + char *AvatarName = strrchr(AvatarURL, '/'); + AvatarName++; + AvatarHash = gg_avatarhash(AvatarName); + } + + if (!db_get_s(pai->hContact, m_szModuleName, GG_KEY_AVATARHASH, &dbv, DBVT_ASCIIZ)) { + AvatarSavedHash = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + + if (AvatarHash != NULL && AvatarSavedHash != NULL) { + getAvatarFilename(pai->hContact, pai->filename, SIZEOF(pai->filename)); + if (!strcmp(AvatarHash, AvatarSavedHash) && !_taccess(pai->filename, 0)) { + result = GAIR_SUCCESS; + } + else if ((wParam & GAIF_FORCE) != 0) { + netlog("gg_getavatarinfo(): Contact %d changed avatar.", uin); + _tremove(pai->filename); + db_set_s(pai->hContact, m_szModuleName, GG_KEY_AVATARHASH, AvatarHash); + getAvatar(pai->hContact, AvatarURL); + result = GAIR_WAITFOR; + } + } + else if ((wParam & GAIF_FORCE) != 0) { + if (AvatarHash == NULL && AvatarSavedHash != NULL) { + netlog("gg_getavatarinfo(): Contact %d deleted avatar.", uin); + getAvatarFilename(pai->hContact, pai->filename, sizeof(pai->filename)); + _tremove(pai->filename); + db_unset(pai->hContact, m_szModuleName, GG_KEY_AVATARHASH); + db_unset(pai->hContact, m_szModuleName, GG_KEY_AVATARURL); + db_unset(pai->hContact, m_szModuleName, GG_KEY_AVATARTYPE); + } + else if (AvatarHash != NULL && AvatarSavedHash == NULL) { + netlog("gg_getavatarinfo(): Contact %d set avatar.", uin); + db_set_s(pai->hContact, m_szModuleName, GG_KEY_AVATARHASH, AvatarHash); + getAvatar(pai->hContact, AvatarURL); + result = GAIR_WAITFOR; + } + } + + mir_free(AvatarHash); + mir_free(AvatarSavedHash); + mir_free(AvatarURL); + + return result; +} + +////////////////////////////////////////////////////////// +// gets avatar + +INT_PTR GGPROTO::getmyavatar(WPARAM wParam, LPARAM lParam) +{ + TCHAR *szFilename = (TCHAR*)wParam; + int len = (int)lParam; + + netlog("gg_getmyavatar(): Requesting user avatar."); + + if (szFilename == NULL || len <= 0) + return -1; + + if (!db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) + return -2; + + getAvatarFilename(NULL, szFilename, len); + return _taccess(szFilename, 0); +} + +////////////////////////////////////////////////////////// +// sets avatar + +INT_PTR GGPROTO::setmyavatar(WPARAM wParam, LPARAM lParam) +{ + TCHAR *szFilename = (TCHAR*)lParam; + + if (!db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) + return -2; + + if (szFilename == NULL) { + MessageBox(NULL, + TranslateT("To remove your Gadu-Gadu avatar, you must use the MojaGeneracja.pl website."), + m_tszUserName, MB_OK | MB_ICONINFORMATION); + return -1; + } + + TCHAR szMyFilename[MAX_PATH]; + getAvatarFilename(NULL, szMyFilename, SIZEOF(szMyFilename)); + if ( _tcscmp(szFilename, szMyFilename) && !CopyFile(szFilename, szMyFilename, FALSE)) { + netlog("gg_setmyavatar(): Failed to set user avatar. File %s could not be created/overwritten.", szMyFilename); + return -1; + } + + setAvatar(szMyFilename); + return 0; +} + +////////////////////////////////////////////////////////// +// gets protocol status message + +INT_PTR GGPROTO::getmyawaymsg(WPARAM wParam, LPARAM lParam) +{ + INT_PTR res = 0; + char *szMsg; + + EnterCriticalSection(&modemsg_mutex); + szMsg = getstatusmsg(wParam ? gg_normalizestatus(wParam) : m_iStatus); + if (isonline() && szMsg) + res = (lParam & SGMA_UNICODE) ? (INT_PTR)mir_a2u(szMsg) : (INT_PTR)mir_strdup(szMsg); + LeaveCriticalSection(&modemsg_mutex); + return res; +} + +////////////////////////////////////////////////////////// +// gets account manager GUI + +extern INT_PTR CALLBACK gg_acc_mgr_guidlgproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + +INT_PTR GGPROTO::get_acc_mgr_gui(WPARAM wParam, LPARAM lParam) +{ + return (INT_PTR) CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_ACCMGRUI), (HWND)lParam, gg_acc_mgr_guidlgproc, (LPARAM)this); +} + +////////////////////////////////////////////////////////// +// leaves (terminates) conference + +INT_PTR GGPROTO::leavechat(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE)wParam; + if (hContact) + CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0); + + return 0; +} diff --git a/protocols/Gadu-Gadu/sessions.c b/protocols/Gadu-Gadu/sessions.c deleted file mode 100644 index d5ea8b5577..0000000000 --- a/protocols/Gadu-Gadu/sessions.c +++ /dev/null @@ -1,445 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2009-2012 Bartosz Białek -// -// 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 "gg.h" - -#define GGS_CONCUR_SESS "%s/ConcurSess" - -static void gg_clearsessionslist(HWND hwndDlg) -{ - HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); - LV_COLUMN column = {0}; - RECT rc; - int iWidth; - - if (!hList) return; - - ListView_DeleteAllItems(hList); - while (ListView_DeleteColumn(hList, 0)); - - column.mask = LVCF_TEXT; - column.cx = 500; - column.pszText = TranslateT("Client Name"); - ListView_InsertColumn(hList, 1, &column); - - column.pszText=TranslateT("IP Address"); - ListView_InsertColumn(hList, 2, &column); - - column.pszText = TranslateT("Login Time"); - ListView_InsertColumn(hList, 3, &column); - - column.pszText = TranslateT("Action"); - ListView_InsertColumn(hList, 4, &column); - - GetClientRect(hList, &rc); - iWidth = rc.right - rc.left; - ListView_SetColumnWidth(hList, 0, iWidth * 45 / 100); - ListView_SetColumnWidth(hList, 1, iWidth * 20 / 100); - ListView_SetColumnWidth(hList, 2, iWidth * 20 / 100); - ListView_SetColumnWidth(hList, 3, LVSCW_AUTOSIZE_USEHEADER); -} - -static void ListView_SetItemTextA(HWND hwndLV, int i, int iSubItem, char* pszText) -{ - LV_ITEMA _ms_lvi; - _ms_lvi.iSubItem = iSubItem; - _ms_lvi.pszText = pszText; - SendMessageA(hwndLV, LVM_SETITEMTEXTA, i, (LPARAM)&_ms_lvi); -} - -static int gg_insertlistitem(HWND hList, gg_multilogon_id_t* id, const char* clientName, const char* ip, const char* loginTime) -{ - LVITEM item = {0}; - int index; - - item.iItem = ListView_GetItemCount(hList); - item.mask = LVIF_PARAM; - item.lParam = (LPARAM)id; - - index = ListView_InsertItem(hList, &item); - if (index < 0) return index; - - ListView_SetItemTextA(hList, index, 0, (char*)clientName); - ListView_SetItemTextA(hList, index, 1, (char*)ip); - ListView_SetItemTextA(hList, index, 2, (char*)loginTime); - ListView_SetItemText(hList, index, 3, TranslateT("sign out")); - - return index; -} - -static void gg_listsessions(GGPROTO* gg, HWND hwndDlg) -{ - HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); - list_t l; - - if (!hList) return; - - EnterCriticalSection(&gg->sessions_mutex); - for (l = gg->sessions; l; l = l->next) - { - struct gg_multilogon_session* sess = (struct gg_multilogon_session*)l->data; - struct in_addr ia; - char* ip; - char loginTime[20]; - ia.S_un.S_addr = sess->remote_addr; - ip = inet_ntoa(ia); - strftime(loginTime, sizeof(loginTime), "%d-%m-%Y %H:%M:%S", localtime(&sess->logon_time)); - gg_insertlistitem(hList, &sess->id, sess->name, ip, loginTime); - } - LeaveCriticalSection(&gg->sessions_mutex); - EnableWindow(GetDlgItem(hwndDlg, IDC_SIGNOUTALL), ListView_GetItemCount(hList) > 0); -} - -static int sttSessionsDlgResizer(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL* urc) -{ - switch (urc->wId) - { - case IDC_HEADERBAR: - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH; - case IDC_SESSIONS: - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH; - case IDC_SIGNOUTALL: - return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -static BOOL IsOverAction(HWND hwndDlg) -{ - HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); - LVHITTESTINFO hti; - RECT rc; - HDC hdc; - TCHAR szText[256]; - SIZE textSize; - int textPosX; - - GetCursorPos(&hti.pt); - ScreenToClient(hList, &hti.pt); - GetClientRect(hList, &rc); - if (!PtInRect(&rc, hti.pt) || ListView_SubItemHitTest(hList, &hti) == -1 - || hti.iSubItem != 3 || !(hti.flags & LVHT_ONITEMLABEL)) - return FALSE; - - ListView_GetSubItemRect(hList, hti.iItem, hti.iSubItem, LVIR_LABEL, &rc); - szText[0] = 0; - ListView_GetItemText(hList, hti.iItem, hti.iSubItem, szText, SIZEOF(szText)); - hdc = GetDC(hList); - GetTextExtentPoint32(hdc, szText, lstrlen(szText), &textSize); - ReleaseDC(hList, hdc); - textPosX = rc.left + (((rc.right - rc.left) - textSize.cx) / 2); - return (hti.pt.x > textPosX && hti.pt.x < textPosX + textSize.cx); -} - -static HCURSOR hHandCursor = NULL; -#define WM_MULTILOGONINFO (WM_USER + 10) -#define HM_PROTOACK (WM_USER + 11) - -static INT_PTR CALLBACK gg_sessions_viewdlg(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - GGPROTO* gg = (GGPROTO*)GetWindowLongPtr(hwndDlg, DWLP_USER); - switch (message) - { - case WM_INITDIALOG: - { - GGPROTO* gg = (GGPROTO*)lParam; - TCHAR oldTitle[256], newTitle[256]; - HANDLE hProtoAckEvent; - - SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)lParam); - TranslateDialogDefault(hwndDlg); - GetDlgItemText(hwndDlg, IDC_HEADERBAR, oldTitle, SIZEOF(oldTitle)); - mir_snprintf(newTitle, SIZEOF(newTitle), oldTitle, GG_PROTONAME); - SetDlgItemText(hwndDlg, IDC_HEADERBAR, newTitle); - WindowSetIcon(hwndDlg, "sessions"); - gg->hwndSessionsDlg = hwndDlg; - - if (hHandCursor == NULL) - hHandCursor = LoadCursor(NULL, IDC_HAND); - hProtoAckEvent = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_PROTOACK); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)hProtoAckEvent); - - ListView_SetExtendedListViewStyle(GetDlgItem(hwndDlg, IDC_SESSIONS), LVS_EX_FULLROWSELECT); - SendMessage(hwndDlg, WM_MULTILOGONINFO, 0, 0); - return TRUE; - } - - case HM_PROTOACK: - { - ACKDATA* ack = (ACKDATA*)lParam; - if (!strcmp(ack->szModule, GG_PROTO) && !ack->hContact && ack->type == ACKTYPE_STATUS - && ack->result == ACKRESULT_SUCCESS && (ack->lParam == ID_STATUS_OFFLINE - || (ack->hProcess == (HANDLE)ID_STATUS_CONNECTING && ack->lParam != ID_STATUS_OFFLINE - && !ListView_GetItemCount(GetDlgItem(hwndDlg, IDC_SESSIONS))))) - { - gg_clearsessionslist(hwndDlg); - EnableWindow(GetDlgItem(hwndDlg, IDC_SIGNOUTALL), FALSE); - } - break; - } - - case WM_MULTILOGONINFO: - gg_clearsessionslist(hwndDlg); - gg_listsessions(gg, hwndDlg); - break; - - case WM_COMMAND: - switch (LOWORD(wParam)) - { - case IDC_SIGNOUTALL: - { - HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); - LVITEM lvi = {0}; - int iCount = ListView_GetItemCount(hList), i; - lvi.mask = LVIF_PARAM; - for (i = 0; i < iCount; i++) - { - lvi.iItem = i; - ListView_GetItem(hList, &lvi); - EnterCriticalSection(&gg->sess_mutex); - gg_multilogon_disconnect(gg->sess, *((gg_multilogon_id_t*)lvi.lParam)); - LeaveCriticalSection(&gg->sess_mutex); - } - break; - } - } - break; - - case WM_NOTIFY: - if (((LPNMHDR)lParam)->idFrom == IDC_SESSIONS) - { - switch (((LPNMHDR)lParam)->code) - { - case NM_CUSTOMDRAW: - { - LPNMLVCUSTOMDRAW nm = (LPNMLVCUSTOMDRAW)lParam; - switch (nm->nmcd.dwDrawStage) - { - case CDDS_PREPAINT: - if (ListView_GetItemCount(nm->nmcd.hdr.hwndFrom) == 0) - { - const LPCTSTR szText = gg_isonline(gg) - ? TranslateT("There are no active concurrent sessions for this account.") - : TranslateT("You have to be logged in to view concurrent sessions."); - RECT rc; - HWND hwndHeader = ListView_GetHeader(nm->nmcd.hdr.hwndFrom); - SIZE textSize; - int textPosX; - GetClientRect(nm->nmcd.hdr.hwndFrom, &rc); - if (hwndHeader != NULL) - { - RECT rcHeader; - GetClientRect(hwndHeader, &rcHeader); - rc.top += rcHeader.bottom; - } - GetTextExtentPoint32(nm->nmcd.hdc, szText, lstrlen(szText), &textSize); - textPosX = rc.left + (((rc.right - rc.left) - textSize.cx) / 2); - ExtTextOut(nm->nmcd.hdc, textPosX, rc.top + textSize.cy, ETO_OPAQUE, &rc, szText, lstrlen(szText), NULL); - } - // FALL THROUGH - - case CDDS_ITEMPREPAINT: - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT , CDRF_NOTIFYSUBITEMDRAW); - return TRUE; - - case CDDS_SUBITEM | CDDS_ITEMPREPAINT: - { - RECT rc; - ListView_GetSubItemRect(nm->nmcd.hdr.hwndFrom, nm->nmcd.dwItemSpec, nm->iSubItem, LVIR_LABEL, &rc); - if (nm->nmcd.hdr.idFrom == IDC_SESSIONS && nm->iSubItem == 3) - { - TCHAR szText[256]; - szText[0] = 0; - ListView_GetItemText(nm->nmcd.hdr.hwndFrom, nm->nmcd.dwItemSpec, nm->iSubItem, szText, SIZEOF(szText)); - FillRect(nm->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW)); - SetTextColor(nm->nmcd.hdc, RGB(0, 0, 255)); - DrawText(nm->nmcd.hdc, szText, -1, &rc, DT_END_ELLIPSIS | DT_CENTER | DT_NOPREFIX | DT_SINGLELINE | DT_TOP); - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT); - return TRUE; - } - break; - } - } - break; - } - - case NM_CLICK: - if (IsOverAction(hwndDlg)) - { - LPNMITEMACTIVATE nm = (LPNMITEMACTIVATE)lParam; - LVITEM lvi = {0}; - lvi.mask = LVIF_PARAM; - lvi.iItem = nm->iItem; - ListView_GetItem(nm->hdr.hwndFrom, &lvi); - EnterCriticalSection(&gg->sess_mutex); - gg_multilogon_disconnect(gg->sess, *((gg_multilogon_id_t*)lvi.lParam)); - LeaveCriticalSection(&gg->sess_mutex); - } - break; - } - } - break; - - case WM_CONTEXTMENU: - { - HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); - POINT pt = {(short)LOWORD(lParam), (short)HIWORD(lParam)}, ptDlg = pt; - LVHITTESTINFO lvhti = {0}; - - ScreenToClient(hwndDlg, &ptDlg); - if (ChildWindowFromPoint(hwndDlg, ptDlg) == hList) - { - HMENU hMenu; - int iSelection; - - lvhti.pt = pt; - ScreenToClient(hList, &lvhti.pt); - if (ListView_HitTest(hList, &lvhti) == -1) break; - - hMenu = CreatePopupMenu(); - AppendMenu(hMenu, MFT_STRING, 10001, TranslateT("Copy Text")); - AppendMenu(hMenu, MFT_STRING, 10002, TranslateT("Whois")); - iSelection = TrackPopupMenu(hMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL); - switch (iSelection) - { - case 10001: - { - char szText[512], szClientName[256], szIP[64], szLoginTime[64]; - HGLOBAL hData; - if (!OpenClipboard(hwndDlg)) break; - EmptyClipboard(); - szClientName[0] = szIP[0] = szLoginTime[0] = 0; - ListView_GetItemText(hList, lvhti.iItem, 0, szClientName, SIZEOF(szClientName)); - ListView_GetItemText(hList, lvhti.iItem, 1, szIP, SIZEOF(szIP)); - ListView_GetItemText(hList, lvhti.iItem, 2, szLoginTime, SIZEOF(szLoginTime)); - mir_snprintf(szText, SIZEOF(szText), "%s\t%s\t%s", szClientName, szIP, szLoginTime); - if ((hData = GlobalAlloc(GMEM_MOVEABLE, lstrlenA(szText) + 1)) != NULL) - { - lstrcpyA((char*)GlobalLock(hData), szText); - GlobalUnlock(hData); - SetClipboardData(CF_TEXT, hData); - } - CloseClipboard(); - break; - } - - case 10002: - { - char szUrl[256], szIP[64]; - szIP[0] = 0; - ListView_GetItemText(hList, lvhti.iItem, 1, szIP, SIZEOF(szIP)); - mir_snprintf(szUrl, SIZEOF(szUrl), "http://whois.domaintools.com/%s", szIP); - CallService(MS_UTILS_OPENURL, 1, (LPARAM)szUrl); - break; - } - } - DestroyMenu(hMenu); - } - break; - } - - case WM_GETMINMAXINFO: - ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = 620; - ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = 220; - return 0; - - case WM_SIZE: - { - UTILRESIZEDIALOG urd = {0}; - urd.cbSize = sizeof(urd); - urd.hInstance = hInstance; - urd.hwndDlg = hwndDlg; - urd.lpTemplate = MAKEINTRESOURCEA(IDD_SESSIONS); - urd.pfnResizer = sttSessionsDlgResizer; - CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd); - return 0; - } - - case WM_SETCURSOR: - if (LOWORD(lParam) == HTCLIENT && IsOverAction(hwndDlg)) - { - SetCursor(hHandCursor); - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); - return TRUE; - } - break; - - case WM_CLOSE: - DestroyWindow(hwndDlg); - break; - - case WM_DESTROY: - { - HANDLE hProtoAckEvent = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - if (hProtoAckEvent) UnhookEvent(hProtoAckEvent); - gg->hwndSessionsDlg = NULL; - WindowFreeIcon(hwndDlg); - break; - } - } - return FALSE; -} - -INT_PTR gg_sessions_view(GGPROTO* gg, WPARAM wParam, LPARAM lParam) -{ - if (gg->hwndSessionsDlg && IsWindow(gg->hwndSessionsDlg)) - { - ShowWindow(gg->hwndSessionsDlg, SW_SHOWNORMAL); - SetForegroundWindow(gg->hwndSessionsDlg); - SetFocus(gg->hwndSessionsDlg); - } - else - CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_SESSIONS), NULL, gg_sessions_viewdlg, (LPARAM)gg); - return 0; -} - -void gg_sessions_updatedlg(GGPROTO* gg) -{ - if (gg->hwndSessionsDlg && IsWindow(gg->hwndSessionsDlg)) - SendMessage(gg->hwndSessionsDlg, WM_MULTILOGONINFO, 0, 0); -} - -BOOL gg_sessions_closedlg(GGPROTO* gg) -{ - if (gg->hwndSessionsDlg && IsWindow(gg->hwndSessionsDlg)) - return PostMessage(gg->hwndSessionsDlg, WM_CLOSE, 0, 0); - return FALSE; -} - -void gg_sessions_menus_init(GGPROTO* gg, HGENMENU hRoot) -{ - CLISTMENUITEM mi = {0}; - char service[64]; - - mi.cbSize = sizeof(mi); - mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE | CMIF_TCHAR; - mi.hParentMenu = hRoot; - - mir_snprintf(service, sizeof(service), GGS_CONCUR_SESS, GG_PROTO); - CreateProtoServiceFunction(service, gg_sessions_view, gg); - if (gg->hMenuRoot) - mi.position = 2050000001; - else - mi.position = 200003; - mi.icolibItem = GetIconHandle(IDI_SESSIONS); - mi.ptszName = LPGENT("Concurrent &sessions"); - mi.pszService = service; - Menu_AddProtoMenuItem(&mi); -} diff --git a/protocols/Gadu-Gadu/sessions.cpp b/protocols/Gadu-Gadu/sessions.cpp new file mode 100644 index 0000000000..1f8572258a --- /dev/null +++ b/protocols/Gadu-Gadu/sessions.cpp @@ -0,0 +1,447 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2009-2012 Bartosz Białek +// +// 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 "gg.h" + +#define GGS_CONCUR_SESS "%s/ConcurSess" + +static void gg_clearsessionslist(HWND hwndDlg) +{ + HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); + LV_COLUMN column = {0}; + RECT rc; + int iWidth; + + if (!hList) return; + + ListView_DeleteAllItems(hList); + while (ListView_DeleteColumn(hList, 0)); + + column.mask = LVCF_TEXT; + column.cx = 500; + column.pszText = TranslateT("Client Name"); + ListView_InsertColumn(hList, 1, &column); + + column.pszText=TranslateT("IP Address"); + ListView_InsertColumn(hList, 2, &column); + + column.pszText = TranslateT("Login Time"); + ListView_InsertColumn(hList, 3, &column); + + column.pszText = TranslateT("Action"); + ListView_InsertColumn(hList, 4, &column); + + GetClientRect(hList, &rc); + iWidth = rc.right - rc.left; + ListView_SetColumnWidth(hList, 0, iWidth * 45 / 100); + ListView_SetColumnWidth(hList, 1, iWidth * 20 / 100); + ListView_SetColumnWidth(hList, 2, iWidth * 20 / 100); + ListView_SetColumnWidth(hList, 3, LVSCW_AUTOSIZE_USEHEADER); +} + +static void ListView_SetItemTextA(HWND hwndLV, int i, int iSubItem, char* pszText) +{ + LV_ITEMA _ms_lvi; + _ms_lvi.iSubItem = iSubItem; + _ms_lvi.pszText = pszText; + SendMessageA(hwndLV, LVM_SETITEMTEXTA, i, (LPARAM)&_ms_lvi); +} + +static int gg_insertlistitem(HWND hList, gg_multilogon_id_t* id, const char* clientName, const char* ip, const char* loginTime) +{ + LVITEM item = {0}; + int index; + + item.iItem = ListView_GetItemCount(hList); + item.mask = LVIF_PARAM; + item.lParam = (LPARAM)id; + + index = ListView_InsertItem(hList, &item); + if (index < 0) return index; + + ListView_SetItemTextA(hList, index, 0, (char*)clientName); + ListView_SetItemTextA(hList, index, 1, (char*)ip); + ListView_SetItemTextA(hList, index, 2, (char*)loginTime); + ListView_SetItemText(hList, index, 3, TranslateT("sign out")); + + return index; +} + +static void gg_listsessions(GGPROTO* gg, HWND hwndDlg) +{ + HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); + list_t l; + + if (!hList) return; + + EnterCriticalSection(&gg->sessions_mutex); + for (l = gg->sessions; l; l = l->next) + { + struct gg_multilogon_session* sess = (struct gg_multilogon_session*)l->data; + struct in_addr ia; + char* ip; + char loginTime[20]; + ia.S_un.S_addr = sess->remote_addr; + ip = inet_ntoa(ia); + strftime(loginTime, sizeof(loginTime), "%d-%m-%Y %H:%M:%S", localtime(&sess->logon_time)); + gg_insertlistitem(hList, &sess->id, sess->name, ip, loginTime); + } + LeaveCriticalSection(&gg->sessions_mutex); + EnableWindow(GetDlgItem(hwndDlg, IDC_SIGNOUTALL), ListView_GetItemCount(hList) > 0); +} + +static int sttSessionsDlgResizer(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL* urc) +{ + switch (urc->wId) + { + case IDC_HEADERBAR: + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH; + case IDC_SESSIONS: + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH; + case IDC_SIGNOUTALL: + return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; + } + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; +} + +static BOOL IsOverAction(HWND hwndDlg) +{ + HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); + LVHITTESTINFO hti; + RECT rc; + HDC hdc; + TCHAR szText[256]; + SIZE textSize; + int textPosX; + + GetCursorPos(&hti.pt); + ScreenToClient(hList, &hti.pt); + GetClientRect(hList, &rc); + if (!PtInRect(&rc, hti.pt) || ListView_SubItemHitTest(hList, &hti) == -1 + || hti.iSubItem != 3 || !(hti.flags & LVHT_ONITEMLABEL)) + return FALSE; + + ListView_GetSubItemRect(hList, hti.iItem, hti.iSubItem, LVIR_LABEL, &rc); + szText[0] = 0; + ListView_GetItemText(hList, hti.iItem, hti.iSubItem, szText, SIZEOF(szText)); + hdc = GetDC(hList); + GetTextExtentPoint32(hdc, szText, lstrlen(szText), &textSize); + ReleaseDC(hList, hdc); + textPosX = rc.left + (((rc.right - rc.left) - textSize.cx) / 2); + return (hti.pt.x > textPosX && hti.pt.x < textPosX + textSize.cx); +} + +static HCURSOR hHandCursor = NULL; +#define WM_MULTILOGONINFO (WM_USER + 10) +#define HM_PROTOACK (WM_USER + 11) + +static INT_PTR CALLBACK gg_sessions_viewdlg(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + GGPROTO* gg = (GGPROTO*)GetWindowLongPtr(hwndDlg, DWLP_USER); + switch (message) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + + gg = (GGPROTO*)lParam; + gg->hwndSessionsDlg = hwndDlg; + + SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)lParam); + { + TCHAR oldTitle[256], newTitle[256]; + HANDLE hProtoAckEvent; + + GetDlgItemText(hwndDlg, IDC_HEADERBAR, oldTitle, SIZEOF(oldTitle)); + mir_sntprintf(newTitle, SIZEOF(newTitle), oldTitle, gg->m_tszUserName); + SetDlgItemText(hwndDlg, IDC_HEADERBAR, newTitle); + WindowSetIcon(hwndDlg, "sessions"); + + if (hHandCursor == NULL) + hHandCursor = LoadCursor(NULL, IDC_HAND); + hProtoAckEvent = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_PROTOACK); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)hProtoAckEvent); + + ListView_SetExtendedListViewStyle(GetDlgItem(hwndDlg, IDC_SESSIONS), LVS_EX_FULLROWSELECT); + SendMessage(hwndDlg, WM_MULTILOGONINFO, 0, 0); + return TRUE; + } + + case HM_PROTOACK: + { + ACKDATA* ack = (ACKDATA*)lParam; + if (!strcmp(ack->szModule, gg->m_szModuleName) && !ack->hContact && ack->type == ACKTYPE_STATUS + && ack->result == ACKRESULT_SUCCESS && (ack->lParam == ID_STATUS_OFFLINE + || (ack->hProcess == (HANDLE)ID_STATUS_CONNECTING && ack->lParam != ID_STATUS_OFFLINE + && !ListView_GetItemCount(GetDlgItem(hwndDlg, IDC_SESSIONS))))) + { + gg_clearsessionslist(hwndDlg); + EnableWindow(GetDlgItem(hwndDlg, IDC_SIGNOUTALL), FALSE); + } + break; + } + + case WM_MULTILOGONINFO: + gg_clearsessionslist(hwndDlg); + gg_listsessions(gg, hwndDlg); + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_SIGNOUTALL: + { + HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); + LVITEM lvi = {0}; + int iCount = ListView_GetItemCount(hList), i; + lvi.mask = LVIF_PARAM; + for (i = 0; i < iCount; i++) + { + lvi.iItem = i; + ListView_GetItem(hList, &lvi); + EnterCriticalSection(&gg->sess_mutex); + gg_multilogon_disconnect(gg->sess, *((gg_multilogon_id_t*)lvi.lParam)); + LeaveCriticalSection(&gg->sess_mutex); + } + break; + } + } + break; + + case WM_NOTIFY: + if (((LPNMHDR)lParam)->idFrom == IDC_SESSIONS) + { + switch (((LPNMHDR)lParam)->code) + { + case NM_CUSTOMDRAW: + { + LPNMLVCUSTOMDRAW nm = (LPNMLVCUSTOMDRAW)lParam; + switch (nm->nmcd.dwDrawStage) + { + case CDDS_PREPAINT: + if (ListView_GetItemCount(nm->nmcd.hdr.hwndFrom) == 0) + { + const LPCTSTR szText = gg->isonline() + ? TranslateT("There are no active concurrent sessions for this account.") + : TranslateT("You have to be logged in to view concurrent sessions."); + RECT rc; + HWND hwndHeader = ListView_GetHeader(nm->nmcd.hdr.hwndFrom); + SIZE textSize; + int textPosX; + GetClientRect(nm->nmcd.hdr.hwndFrom, &rc); + if (hwndHeader != NULL) + { + RECT rcHeader; + GetClientRect(hwndHeader, &rcHeader); + rc.top += rcHeader.bottom; + } + GetTextExtentPoint32(nm->nmcd.hdc, szText, lstrlen(szText), &textSize); + textPosX = rc.left + (((rc.right - rc.left) - textSize.cx) / 2); + ExtTextOut(nm->nmcd.hdc, textPosX, rc.top + textSize.cy, ETO_OPAQUE, &rc, szText, lstrlen(szText), NULL); + } + // FALL THROUGH + + case CDDS_ITEMPREPAINT: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT , CDRF_NOTIFYSUBITEMDRAW); + return TRUE; + + case CDDS_SUBITEM | CDDS_ITEMPREPAINT: + { + RECT rc; + ListView_GetSubItemRect(nm->nmcd.hdr.hwndFrom, nm->nmcd.dwItemSpec, nm->iSubItem, LVIR_LABEL, &rc); + if (nm->nmcd.hdr.idFrom == IDC_SESSIONS && nm->iSubItem == 3) + { + TCHAR szText[256]; + szText[0] = 0; + ListView_GetItemText(nm->nmcd.hdr.hwndFrom, nm->nmcd.dwItemSpec, nm->iSubItem, szText, SIZEOF(szText)); + FillRect(nm->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW)); + SetTextColor(nm->nmcd.hdc, RGB(0, 0, 255)); + DrawText(nm->nmcd.hdc, szText, -1, &rc, DT_END_ELLIPSIS | DT_CENTER | DT_NOPREFIX | DT_SINGLELINE | DT_TOP); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT); + return TRUE; + } + break; + } + } + break; + } + + case NM_CLICK: + if (IsOverAction(hwndDlg)) + { + LPNMITEMACTIVATE nm = (LPNMITEMACTIVATE)lParam; + LVITEM lvi = {0}; + lvi.mask = LVIF_PARAM; + lvi.iItem = nm->iItem; + ListView_GetItem(nm->hdr.hwndFrom, &lvi); + EnterCriticalSection(&gg->sess_mutex); + gg_multilogon_disconnect(gg->sess, *((gg_multilogon_id_t*)lvi.lParam)); + LeaveCriticalSection(&gg->sess_mutex); + } + break; + } + } + break; + + case WM_CONTEXTMENU: + { + HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS); + POINT pt = {(short)LOWORD(lParam), (short)HIWORD(lParam)}, ptDlg = pt; + LVHITTESTINFO lvhti = {0}; + + ScreenToClient(hwndDlg, &ptDlg); + if (ChildWindowFromPoint(hwndDlg, ptDlg) == hList) + { + HMENU hMenu; + int iSelection; + + lvhti.pt = pt; + ScreenToClient(hList, &lvhti.pt); + if (ListView_HitTest(hList, &lvhti) == -1) break; + + hMenu = CreatePopupMenu(); + AppendMenu(hMenu, MFT_STRING, 10001, TranslateT("Copy Text")); + AppendMenu(hMenu, MFT_STRING, 10002, TranslateT("Whois")); + iSelection = TrackPopupMenu(hMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL); + switch (iSelection) { + case 10001: + { + TCHAR szText[512], szClientName[256], szIP[64], szLoginTime[64]; + HGLOBAL hData; + if (!OpenClipboard(hwndDlg)) + break; + + EmptyClipboard(); + szClientName[0] = szIP[0] = szLoginTime[0] = 0; + ListView_GetItemText(hList, lvhti.iItem, 0, szClientName, SIZEOF(szClientName)); + ListView_GetItemText(hList, lvhti.iItem, 1, szIP, SIZEOF(szIP)); + ListView_GetItemText(hList, lvhti.iItem, 2, szLoginTime, SIZEOF(szLoginTime)); + mir_sntprintf(szText, SIZEOF(szText), _T("%s\t%s\t%s"), szClientName, szIP, szLoginTime); + if ((hData = GlobalAlloc(GMEM_MOVEABLE, lstrlen(szText) + 1)) != NULL) + { + lstrcpy((TCHAR*)GlobalLock(hData), szText); + GlobalUnlock(hData); + SetClipboardData(CF_TEXT, hData); + } + CloseClipboard(); + break; + } + + case 10002: + { + TCHAR szUrl[256], szIP[64]; + szIP[0] = 0; + ListView_GetItemText(hList, lvhti.iItem, 1, szIP, SIZEOF(szIP)); + mir_sntprintf(szUrl, SIZEOF(szUrl), _T("http://whois.domaintools.com/%s"), szIP); + CallService(MS_UTILS_OPENURL, OUF_TCHAR, (LPARAM)szUrl); + break; + } + } + DestroyMenu(hMenu); + } + break; + } + + case WM_GETMINMAXINFO: + ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = 620; + ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = 220; + return 0; + + case WM_SIZE: + { + UTILRESIZEDIALOG urd = {0}; + urd.cbSize = sizeof(urd); + urd.hInstance = hInstance; + urd.hwndDlg = hwndDlg; + urd.lpTemplate = MAKEINTRESOURCEA(IDD_SESSIONS); + urd.pfnResizer = sttSessionsDlgResizer; + CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd); + return 0; + } + + case WM_SETCURSOR: + if (LOWORD(lParam) == HTCLIENT && IsOverAction(hwndDlg)) + { + SetCursor(hHandCursor); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); + return TRUE; + } + break; + + case WM_CLOSE: + DestroyWindow(hwndDlg); + break; + + case WM_DESTROY: + { + HANDLE hProtoAckEvent = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + if (hProtoAckEvent) UnhookEvent(hProtoAckEvent); + gg->hwndSessionsDlg = NULL; + WindowFreeIcon(hwndDlg); + break; + } + } + return FALSE; +} + +INT_PTR GGPROTO::sessions_view(WPARAM wParam, LPARAM lParam) +{ + if (hwndSessionsDlg && IsWindow(hwndSessionsDlg)) + { + ShowWindow(hwndSessionsDlg, SW_SHOWNORMAL); + SetForegroundWindow(hwndSessionsDlg); + SetFocus(hwndSessionsDlg); + } + else + CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_SESSIONS), NULL, gg_sessions_viewdlg, (LPARAM)this); + return 0; +} + +void GGPROTO::sessions_updatedlg() +{ + if (hwndSessionsDlg && IsWindow(hwndSessionsDlg)) + SendMessage(hwndSessionsDlg, WM_MULTILOGONINFO, 0, 0); +} + +BOOL GGPROTO::sessions_closedlg() +{ + if (hwndSessionsDlg && IsWindow(hwndSessionsDlg)) + return PostMessage(hwndSessionsDlg, WM_CLOSE, 0, 0); + return FALSE; +} + +void GGPROTO::sessions_menus_init(HGENMENU hRoot) +{ + CLISTMENUITEM mi = {0}; + char service[64]; + + mi.cbSize = sizeof(mi); + mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE | CMIF_TCHAR; + mi.hParentMenu = hRoot; + + mir_snprintf(service, sizeof(service), GGS_CONCUR_SESS, m_szModuleName); + createProtoService(service, &GGPROTO::sessions_view); + if (hMenuRoot) + mi.position = 2050000001; + else + mi.position = 200003; + mi.icolibItem = GetIconHandle(IDI_SESSIONS); + mi.ptszName = LPGENT("Concurrent &sessions"); + mi.pszService = service; + Menu_AddProtoMenuItem(&mi); +} diff --git a/protocols/Gadu-Gadu/token.c b/protocols/Gadu-Gadu/token.c deleted file mode 100644 index 378766ef4a..0000000000 --- a/protocols/Gadu-Gadu/token.c +++ /dev/null @@ -1,178 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 Adam Strzelecki -// -// 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 "gg.h" - -#define MAX_LOADSTRING 100 -#define HIMETRIC_INCH 2540 -#define MAP_LOGHIM_TO_PIX(x,ppli) ( ((ppli)*(x) + HIMETRIC_INCH/2) / HIMETRIC_INCH ) - -//////////////////////////////////////////////////////////////////////////////// -// User Util Dlg Page : Data - -typedef struct _GGTOKENDLGDATA -{ - int width; - int height; - char id[256]; - char val[256]; - HBITMAP hBitmap; -} GGTOKENDLGDATA; - -INT_PTR CALLBACK gg_tokendlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - GGTOKENDLGDATA *dat = (GGTOKENDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch(msg) - { - case WM_INITDIALOG: - { - RECT rc; - TranslateDialogDefault(hwndDlg); - GetClientRect(GetDlgItem(hwndDlg, IDC_WHITERECT), &rc); - InvalidateRect(hwndDlg, &rc, TRUE); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); - return TRUE; - } - - case WM_COMMAND: - switch(LOWORD(wParam)) - { - case IDOK: - { - GetDlgItemText(hwndDlg, IDC_TOKEN, dat->val, sizeof(dat->val)); - EndDialog(hwndDlg, IDOK); - break; - } - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - } - break; - - case WM_PAINT: - { - PAINTSTRUCT paintStruct; - HDC hdc = BeginPaint(hwndDlg, &paintStruct); - RECT rc; GetClientRect(GetDlgItem(hwndDlg, IDC_WHITERECT), &rc); - FillRect(hdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH)); - - if(dat && dat->hBitmap) - { - HDC hdcBmp = NULL; - int nWidth, nHeight; - BITMAP bmp; - - GetObject(dat->hBitmap, sizeof(bmp), &bmp); - nWidth = bmp.bmWidth; nHeight = bmp.bmHeight; - - if(hdcBmp = CreateCompatibleDC(hdc)) - { - SelectObject(hdcBmp, dat->hBitmap); - SetStretchBltMode(hdc, HALFTONE); - BitBlt(hdc, - (rc.left + rc.right - nWidth) / 2, - (rc.top + rc.bottom - nHeight) / 2, - nWidth, nHeight, - hdcBmp, 0, 0, SRCCOPY); - DeleteDC(hdcBmp); - } - } - EndPaint(hwndDlg, &paintStruct); - return 0; - } - } - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////////// -// Gets GG token -int gg_gettoken(GGPROTO *gg, GGTOKEN *token) -{ - struct gg_http *h = NULL; - struct gg_token *t = NULL; - IMGSRVC_MEMIO memio = {0}; - GGTOKENDLGDATA dat = {0}; - - // Zero tokens - strcpy(token->id, ""); - strcpy(token->val, ""); - - if (!(h = gg_token(0)) || gg_token_watch_fd(h) || h->state == GG_STATE_ERROR || h->state != GG_STATE_DONE) - { - char error[128]; - mir_snprintf(error, sizeof(error), Translate("Token retrieval failed because of error:\n\t%s"), http_error_string(h ? h->error : 0)); - MessageBox( - NULL, - error, - GG_PROTONAME, - MB_OK | MB_ICONSTOP - ); - gg_free_pubdir(h); - return FALSE; - } - - if (!(t = (struct gg_token *)h->data) || (!h->body)) - { - char error[128]; - mir_snprintf(error, sizeof(error), Translate("Token retrieval failed because of error:\n\t%s"), http_error_string(h ? h->error : 0)); - MessageBox( - NULL, - error, - GG_PROTONAME, - MB_OK | MB_ICONSTOP - ); - gg_free_pubdir(h); - return FALSE; - } - - // Return token id - strncpy(dat.id, t->tokenid, sizeof(dat.id)); - dat.width = t->width; - dat.height = t->height; - - // Load bitmap - memio.iLen = h->body_size; - memio.pBuf = (void *)h->body; - memio.fif = -1; /* detect */ - memio.flags = 0; - dat.hBitmap = (HBITMAP) CallService(MS_IMG_LOADFROMMEM, (WPARAM) &memio, 0); - if(dat.hBitmap == NULL) - { - MessageBox( - NULL, - Translate("Could not load token image."), - GG_PROTONAME, - MB_OK | MB_ICONSTOP - ); - gg_free_pubdir(h); - return FALSE; - } - - // Load token dialog - if(DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_TOKEN), NULL, gg_tokendlgproc, (LPARAM)&dat) == IDCANCEL) - return FALSE; - - // Fillup patterns - strncpy(token->id, dat.id, sizeof(token->id)); - strncpy(token->val, dat.val, sizeof(token->val)); - - return TRUE; -} diff --git a/protocols/Gadu-Gadu/token.cpp b/protocols/Gadu-Gadu/token.cpp new file mode 100644 index 0000000000..d2946c12ef --- /dev/null +++ b/protocols/Gadu-Gadu/token.cpp @@ -0,0 +1,161 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 Adam Strzelecki +// +// 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 "gg.h" + +#define MAX_LOADSTRING 100 +#define HIMETRIC_INCH 2540 +#define MAP_LOGHIM_TO_PIX(x,ppli) ( ((ppli)*(x) + HIMETRIC_INCH/2) / HIMETRIC_INCH ) + +//////////////////////////////////////////////////////////////////////////////// +// User Util Dlg Page : Data + +typedef struct _GGTOKENDLGDATA +{ + int width; + int height; + char id[256]; + char val[256]; + HBITMAP hBitmap; +} GGTOKENDLGDATA; + +INT_PTR CALLBACK gg_tokendlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + GGTOKENDLGDATA *dat = (GGTOKENDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch(msg) + { + case WM_INITDIALOG: + { + RECT rc; + TranslateDialogDefault(hwndDlg); + GetClientRect(GetDlgItem(hwndDlg, IDC_WHITERECT), &rc); + InvalidateRect(hwndDlg, &rc, TRUE); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); + return TRUE; + } + + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDOK: + { + GetDlgItemTextA(hwndDlg, IDC_TOKEN, dat->val, sizeof(dat->val)); + EndDialog(hwndDlg, IDOK); + break; + } + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + } + break; + + case WM_PAINT: + { + PAINTSTRUCT paintStruct; + HDC hdc = BeginPaint(hwndDlg, &paintStruct); + RECT rc; GetClientRect(GetDlgItem(hwndDlg, IDC_WHITERECT), &rc); + FillRect(hdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH)); + + if (dat && dat->hBitmap) + { + HDC hdcBmp = NULL; + int nWidth, nHeight; + BITMAP bmp; + + GetObject(dat->hBitmap, sizeof(bmp), &bmp); + nWidth = bmp.bmWidth; nHeight = bmp.bmHeight; + + if (hdcBmp = CreateCompatibleDC(hdc)) + { + SelectObject(hdcBmp, dat->hBitmap); + SetStretchBltMode(hdc, HALFTONE); + BitBlt(hdc, + (rc.left + rc.right - nWidth) / 2, + (rc.top + rc.bottom - nHeight) / 2, + nWidth, nHeight, + hdcBmp, 0, 0, SRCCOPY); + DeleteDC(hdcBmp); + } + } + EndPaint(hwndDlg, &paintStruct); + return 0; + } + } + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////////// +// Gets GG token +int GGPROTO::gettoken(GGTOKEN *token) +{ + struct gg_http *h = NULL; + struct gg_token *t = NULL; + IMGSRVC_MEMIO memio = {0}; + GGTOKENDLGDATA dat = {0}; + + // Zero tokens + strcpy(token->id, ""); + strcpy(token->val, ""); + + if (!(h = gg_token(0)) || gg_token_watch_fd(h) || h->state == GG_STATE_ERROR || h->state != GG_STATE_DONE) { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("Token retrieval failed because of error:\n\t%S"), http_error_string(h ? h->error : 0)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + gg_free_pubdir(h); + return FALSE; + } + + if (!(t = (struct gg_token *)h->data) || (!h->body)) { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("Token retrieval failed because of error:\n\t%S"), http_error_string(h ? h->error : 0)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + gg_free_pubdir(h); + return FALSE; + } + + // Return token id + strncpy(dat.id, t->tokenid, sizeof(dat.id)); + dat.width = t->width; + dat.height = t->height; + + // Load bitmap + memio.iLen = h->body_size; + memio.pBuf = (void *)h->body; + memio.fif = FIF_UNKNOWN; /* detect */ + memio.flags = 0; + dat.hBitmap = (HBITMAP) CallService(MS_IMG_LOADFROMMEM, (WPARAM) &memio, 0); + if (dat.hBitmap == NULL) + { + MessageBox(NULL, TranslateT("Could not load token image."), m_tszUserName, MB_OK | MB_ICONSTOP); + gg_free_pubdir(h); + return FALSE; + } + + // Load token dialog + if (DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_TOKEN), NULL, gg_tokendlgproc, (LPARAM)&dat) == IDCANCEL) + return FALSE; + + // Fillup patterns + strncpy(token->id, dat.id, sizeof(token->id)); + strncpy(token->val, dat.val, sizeof(token->val)); + + return TRUE; +} diff --git a/protocols/Gadu-Gadu/userutils.c b/protocols/Gadu-Gadu/userutils.c deleted file mode 100644 index 721e4d32cf..0000000000 --- a/protocols/Gadu-Gadu/userutils.c +++ /dev/null @@ -1,307 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 Adam Strzelecki -// -// 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 "gg.h" - -//////////////////////////////////////////////////////////////////////////////// -// Create New Account : Proc -void *gg_doregister(GGPROTO *gg, char *newPass, char *newEmail) -{ - // Connection handles - struct gg_http *h = NULL; - struct gg_pubdir *s = NULL; - GGTOKEN token; - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_doregister(): Starting."); -#endif - if (!newPass || !newEmail) return NULL; - - // Load token - if (!gg_gettoken(gg, &token)) return NULL; - - if (!(h = gg_register3(newEmail, newPass, token.id, token.val, 0)) || !(s = h->data) || !s->success || !s->uin) - { - char error[128]; - mir_snprintf(error, sizeof(error), Translate("Cannot register new account because of error:\n\t%s"), - (h && !s) ? http_error_string(h ? h->error : 0) : - (s ? Translate("Registration rejected") : strerror(errno))); - MessageBox( - NULL, - error, - GG_PROTONAME, - MB_OK | MB_ICONSTOP - ); - gg_netlog(gg, "gg_doregister(): Cannot register because of \"%s\".", strerror(errno)); - } - else - { - DBWriteContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, s->uin); - CallService(MS_DB_CRYPT_ENCODESTRING, strlen(newPass) + 1, (LPARAM) newPass); - gg_checknewuser(gg, s->uin, newPass); - DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, newPass); - DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, newEmail); - gg_pubdir_free(h); - gg_netlog(gg, "gg_doregister(): Account registration succesful."); - MessageBox( - NULL, - Translate("You have registered new account.\nPlease fill up your personal details in \"M->View/Change My Details...\""), - GG_PROTONAME, - MB_OK | MB_ICONINFORMATION - ); - } - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_doregister(): End."); -#endif - - return NULL; -} - -//////////////////////////////////////////////////////////////////////////////// -// Remove Account : Proc -void *gg_dounregister(GGPROTO *gg, uin_t uin, char *password) -{ - // Connection handles - struct gg_http *h; - struct gg_pubdir *s; - GGTOKEN token; - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_dounregister(): Starting."); -#endif - if (!uin || !password) return NULL; - - // Load token - if (!gg_gettoken(gg, &token)) return NULL; - - if (!(h = gg_unregister3(uin, password, token.id, token.val, 0)) || !(s = h->data) || !s->success || s->uin != uin) - { - char error[128]; - mir_snprintf(error, sizeof(error), Translate("Your account cannot be removed because of error:\n\t%s"), - (h && !s) ? http_error_string(h ? h->error : 0) : - (s ? Translate("Bad number or password") : strerror(errno))); - MessageBox( - NULL, - error, - GG_PROTONAME, - MB_OK | MB_ICONSTOP - ); - gg_netlog(gg, "gg_dounregister(): Cannot remove account because of \"%s\".", strerror(errno)); - } - else - { - gg_pubdir_free(h); - DBDeleteContactSetting(NULL, GG_PROTO, GG_KEY_PASSWORD); - DBDeleteContactSetting(NULL, GG_PROTO, GG_KEY_UIN); - gg_netlog(gg, "gg_dounregister(): Account %d has been removed.", uin); - MessageBox( - NULL, - Translate("Your account has been removed."), - GG_PROTONAME, - MB_OK | MB_ICONINFORMATION - ); - } - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_dounregister(): End."); -#endif - - return NULL; -} - -//////////////////////////////////////////////////////////////////////////////// -// Change Password Page : Proc -void *gg_dochpass(GGPROTO *gg, uin_t uin, char *password, char *newPass) -{ - // Readup email - char email[255] = "\0"; DBVARIANT dbv_email; - // Connection handles - struct gg_http *h; - struct gg_pubdir *s; - GGTOKEN token; - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_dochpass(): Starting."); -#endif - if (!uin || !password || !newPass) return NULL; - - if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, &dbv_email)) - { - strncpy(email, dbv_email.pszVal, sizeof(email)); - DBFreeVariant(&dbv_email); - } - - // Load token - if (!gg_gettoken(gg, &token)) return NULL; - - if (!(h = gg_change_passwd4(uin, email, password, newPass, token.id, token.val, 0)) || !(s = h->data) || !s->success) - { - char error[128]; - mir_snprintf(error, sizeof(error), Translate("Your password cannot be changed because of error:\n\t%s"), - (h && !s) ? http_error_string(h ? h->error : 0) : - (s ? Translate("Invalid data entered") : strerror(errno))); - MessageBox( - NULL, - error, - GG_PROTONAME, - MB_OK | MB_ICONSTOP - ); - gg_netlog(gg, "gg_dochpass(): Cannot change password because of \"%s\".", strerror(errno)); - } - else - { - gg_pubdir_free(h); - CallService(MS_DB_CRYPT_ENCODESTRING, strlen(newPass) + 1, (LPARAM) newPass); - DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, newPass); - gg_netlog(gg, "gg_dochpass(): Password change succesful."); - MessageBox( - NULL, - Translate("Your password has been changed."), - GG_PROTONAME, - MB_OK | MB_ICONINFORMATION - ); - } - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_dochpass(): End."); -#endif - - return NULL; -} - -//////////////////////////////////////////////////////////////////////////////// -// Change E-mail Page : Proc -void *gg_dochemail(GGPROTO *gg, uin_t uin, char *password, char *email, char *newEmail) -{ - // Connection handles - struct gg_http *h; - struct gg_pubdir *s; - GGTOKEN token; - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_doemail(): Starting."); -#endif - if (!uin || !email || !newEmail) return NULL; - - // Load token - if (!gg_gettoken(gg, &token)) return NULL; - - if (!(h = gg_change_passwd4(uin, newEmail, password, password, token.id, token.val, 0)) || !(s = h->data) || !s->success) - { - char error[128]; - mir_snprintf(error, sizeof(error), Translate("Your e-mail cannot be changed because of error:\n\t%s"), - (h && !s) ? http_error_string(h ? h->error : 0) : - (s ? Translate("Bad old e-mail or password") : strerror(errno))); - MessageBox( - NULL, - error, - GG_PROTONAME, - MB_OK | MB_ICONSTOP - ); - gg_netlog(gg, "gg_dochpass(): Cannot change e-mail because of \"%s\".", strerror(errno)); - } - else - { - gg_pubdir_free(h); - DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, newEmail); - gg_netlog(gg, "gg_doemail(): E-mail change succesful."); - MessageBox( - NULL, - Translate("Your e-mail has been changed."), - GG_PROTONAME, - MB_OK | MB_ICONINFORMATION - ); - } - -#ifdef DEBUGMODE - gg_netlog(gg, "gg_doemail(): End."); -#endif - - return NULL; -} - -//////////////////////////////////////////////////////////////////////////////// -// User Util Dlg Page : Data -INT_PTR CALLBACK gg_userutildlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - GGUSERUTILDLGDATA *dat = (GGUSERUTILDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch (msg) - { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - WindowSetIcon(hwndDlg, "settings"); - dat = (GGUSERUTILDLGDATA *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); - if (dat) SetDlgItemText(hwndDlg, IDC_EMAIL, dat->email); // Readup email - return TRUE; - - case WM_COMMAND: - switch (LOWORD(wParam)) - { - case IDC_PASSWORD: - case IDC_CPASSWORD: - case IDC_CONFIRM: - { - char pass[128], cpass[128]; - BOOL enable; - GetDlgItemText(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); - GetDlgItemText(hwndDlg, IDC_CPASSWORD, cpass, sizeof(cpass)); - enable = strlen(pass) && strlen(cpass) && !strcmp(cpass, pass); - if (dat && dat->mode == GG_USERUTIL_REMOVE) - EnableWindow(GetDlgItem(hwndDlg, IDOK), IsDlgButtonChecked(hwndDlg, IDC_CONFIRM) ? enable : FALSE); - else - EnableWindow(GetDlgItem(hwndDlg, IDOK), enable); - break; - } - - case IDOK: - { - char pass[128], cpass[128], email[128]; - GetDlgItemText(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); - GetDlgItemText(hwndDlg, IDC_CPASSWORD, cpass, sizeof(cpass)); - GetDlgItemText(hwndDlg, IDC_EMAIL, email, sizeof(email)); - EndDialog(hwndDlg, IDOK); - - // Check dialog box mode - if (!dat) break; - switch (dat->mode) - { - case GG_USERUTIL_CREATE: gg_doregister(dat->gg, pass, email); break; - case GG_USERUTIL_REMOVE: gg_dounregister(dat->gg, dat->uin, pass); break; - case GG_USERUTIL_PASS: gg_dochpass(dat->gg, dat->uin, dat->pass, pass); break; - case GG_USERUTIL_EMAIL: gg_dochemail(dat->gg, dat->uin, dat->pass, dat->email, email); break; - } - break; - } - - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - } - break; - - case WM_DESTROY: - WindowFreeIcon(hwndDlg); - break; - } - return FALSE; -} diff --git a/protocols/Gadu-Gadu/userutils.cpp b/protocols/Gadu-Gadu/userutils.cpp new file mode 100644 index 0000000000..c8476f821d --- /dev/null +++ b/protocols/Gadu-Gadu/userutils.cpp @@ -0,0 +1,324 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 Adam Strzelecki +// +// 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 "gg.h" + +//////////////////////////////////////////////////////////////////////////////// +// Create New Account : Proc + +void *gg_doregister(GGPROTO *gg, char *newPass, char *newEmail) +{ + // Connection handles + struct gg_http *h = NULL; + struct gg_pubdir *s = NULL; + GGTOKEN token; + +#ifdef DEBUGMODE + gg->netlog("gg_doregister(): Starting."); +#endif + if (!newPass || !newEmail) return NULL; + + // Load token + if (!gg->gettoken(&token)) return NULL; + + if (!(h = gg_register3(newEmail, newPass, token.id, token.val, 0)) || !(s = (gg_pubdir*)h->data) || !s->success || !s->uin) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("Cannot register new account because of error:\n\t%S"), + (h && !s) ? http_error_string(h ? h->error : 0) : + (s ? Translate("Registration rejected") : strerror(errno))); + MessageBox( + NULL, + error, + gg->m_tszUserName, + MB_OK | MB_ICONSTOP + ); + gg->netlog("gg_doregister(): Cannot register because of \"%s\".", strerror(errno)); + } + else + { + db_set_w(NULL, gg->m_szModuleName, GG_KEY_UIN, s->uin); + CallService(MS_DB_CRYPT_ENCODESTRING, strlen(newPass) + 1, (LPARAM) newPass); + gg->checknewuser(s->uin, newPass); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, newPass); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, newEmail); + gg_pubdir_free(h); + gg->netlog("gg_doregister(): Account registration succesful."); + MessageBox( NULL, + TranslateT("You have registered new account.\nPlease fill up your personal details in \"M->View/Change My Details...\""), + gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + +#ifdef DEBUGMODE + gg->netlog("gg_doregister(): End."); +#endif + + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// Remove Account : Proc +void *gg_dounregister(GGPROTO *gg, uin_t uin, char *password) +{ + // Connection handles + struct gg_http *h; + struct gg_pubdir *s; + GGTOKEN token; + +#ifdef DEBUGMODE + gg->netlog("gg_dounregister(): Starting."); +#endif + if (!uin || !password) return NULL; + + // Load token + if (!gg->gettoken(&token)) return NULL; + + if (!(h = gg_unregister3(uin, password, token.id, token.val, 0)) || !(s = (gg_pubdir*)h->data) || !s->success || s->uin != uin) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("Your account cannot be removed because of error:\n\t%S"), + (h && !s) ? http_error_string(h ? h->error : 0) : + (s ? Translate("Bad number or password") : strerror(errno))); + MessageBox(NULL, error, gg->m_tszUserName, MB_OK | MB_ICONSTOP); + gg->netlog("gg_dounregister(): Cannot remove account because of \"%s\".", strerror(errno)); + } + else + { + gg_pubdir_free(h); + db_unset(NULL, gg->m_szModuleName, GG_KEY_PASSWORD); + db_unset(NULL, gg->m_szModuleName, GG_KEY_UIN); + gg->netlog("gg_dounregister(): Account %d has been removed.", uin); + MessageBox(NULL, TranslateT("Your account has been removed."), gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + +#ifdef DEBUGMODE + gg->netlog("gg_dounregister(): End."); +#endif + + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// Change Password Page : Proc + +void *gg_dochpass(GGPROTO *gg, uin_t uin, char *password, char *newPass) +{ + // Readup email + char email[255] = "\0"; DBVARIANT dbv_email; + // Connection handles + struct gg_http *h; + struct gg_pubdir *s; + GGTOKEN token; + +#ifdef DEBUGMODE + gg->netlog("gg_dochpass(): Starting."); +#endif + if (!uin || !password || !newPass) return NULL; + + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv_email, DBVT_ASCIIZ)) + { + strncpy(email, dbv_email.pszVal, sizeof(email)); + DBFreeVariant(&dbv_email); + } + + // Load token + if (!gg->gettoken(&token)) + return NULL; + + if (!(h = gg_change_passwd4(uin, email, password, newPass, token.id, token.val, 0)) || !(s = (gg_pubdir*)h->data) || !s->success) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("Your password cannot be changed because of error:\n\t%S"), + (h && !s) ? http_error_string(h ? h->error : 0) : + (s ? Translate("Invalid data entered") : strerror(errno))); + MessageBox(NULL, error, gg->m_tszUserName, MB_OK | MB_ICONSTOP); + gg->netlog("gg_dochpass(): Cannot change password because of \"%s\".", strerror(errno)); + } + else + { + gg_pubdir_free(h); + CallService(MS_DB_CRYPT_ENCODESTRING, strlen(newPass) + 1, (LPARAM) newPass); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, newPass); + gg->netlog("gg_dochpass(): Password change succesful."); + MessageBox(NULL, TranslateT("Your password has been changed."), gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + +#ifdef DEBUGMODE + gg->netlog("gg_dochpass(): End."); +#endif + + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// Change E-mail Page : Proc + +void *gg_dochemail(GGPROTO *gg, uin_t uin, char *password, char *email, char *newEmail) +{ + // Connection handles + struct gg_http *h; + struct gg_pubdir *s; + GGTOKEN token; + +#ifdef DEBUGMODE + gg->netlog("gg_doemail(): Starting."); +#endif + if (!uin || !email || !newEmail) return NULL; + + // Load token + if (!gg->gettoken(&token)) return NULL; + + if (!(h = gg_change_passwd4(uin, newEmail, password, password, token.id, token.val, 0)) || !(s = (gg_pubdir*)h->data) || !s->success) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("Your e-mail cannot be changed because of error:\n\t%s"), + (h && !s) ? http_error_string(h ? h->error : 0) : (s ? Translate("Bad old e-mail or password") : strerror(errno))); + MessageBox(NULL, error, gg->m_tszUserName, MB_OK | MB_ICONSTOP); + gg->netlog("gg_dochpass(): Cannot change e-mail because of \"%s\".", strerror(errno)); + } + else + { + gg_pubdir_free(h); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, newEmail); + gg->netlog("gg_doemail(): E-mail change succesful."); + MessageBox(NULL, TranslateT("Your e-mail has been changed."), gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + +#ifdef DEBUGMODE + gg->netlog("gg_doemail(): End."); +#endif + + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// User Util Dlg Page : Data +INT_PTR CALLBACK gg_userutildlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + GGUSERUTILDLGDATA *dat = (GGUSERUTILDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch (msg) + { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + WindowSetIcon(hwndDlg, "settings"); + dat = (GGUSERUTILDLGDATA *)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); + if (dat) SetDlgItemTextA(hwndDlg, IDC_EMAIL, dat->email); // Readup email + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_PASSWORD: + case IDC_CPASSWORD: + case IDC_CONFIRM: + { + char pass[128], cpass[128]; + BOOL enable; + GetDlgItemTextA(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); + GetDlgItemTextA(hwndDlg, IDC_CPASSWORD, cpass, sizeof(cpass)); + enable = strlen(pass) && strlen(cpass) && !strcmp(cpass, pass); + if (dat && dat->mode == GG_USERUTIL_REMOVE) + EnableWindow(GetDlgItem(hwndDlg, IDOK), IsDlgButtonChecked(hwndDlg, IDC_CONFIRM) ? enable : FALSE); + else + EnableWindow(GetDlgItem(hwndDlg, IDOK), enable); + break; + } + + case IDOK: + { + char pass[128], cpass[128], email[128]; + GetDlgItemTextA(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); + GetDlgItemTextA(hwndDlg, IDC_CPASSWORD, cpass, sizeof(cpass)); + GetDlgItemTextA(hwndDlg, IDC_EMAIL, email, sizeof(email)); + EndDialog(hwndDlg, IDOK); + + // Check dialog box mode + if (!dat) break; + switch (dat->mode) + { + case GG_USERUTIL_CREATE: gg_doregister(dat->gg, pass, email); break; + case GG_USERUTIL_REMOVE: gg_dounregister(dat->gg, dat->uin, pass); break; + case GG_USERUTIL_PASS: gg_dochpass(dat->gg, dat->uin, dat->pass, pass); break; + case GG_USERUTIL_EMAIL: gg_dochemail(dat->gg, dat->uin, dat->pass, dat->email, email); break; + } + break; + } + + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + } + break; + + case WM_DESTROY: + WindowFreeIcon(hwndDlg); + break; + } + return FALSE; +} + +////////////////////////////////////////////////////////// +// Hooks protocol event + +HANDLE GGPROTO::hookProtoEvent(const char* szEvent, GGEventFunc handler) +{ + return ::HookEventObj(szEvent, ( MIRANDAHOOKOBJ )*( void** )&handler, this); +} + +////////////////////////////////////////////////////////// +// Adds a new protocol specific service function + +void GGPROTO::createProtoService(const char* szService, GGServiceFunc serviceProc) +{ + char str[MAXMODULELABELLENGTH]; + mir_snprintf(str, sizeof(str), "%s%s", m_szModuleName, szService); + CreateServiceFunctionObj(str, (MIRANDASERVICEOBJ)*( void** )&serviceProc, this); +} + +////////////////////////////////////////////////////////// +// Forks a thread + +void GGPROTO::forkthread(GGThreadFunc pFunc, void *param) +{ + UINT threadId; + CloseHandle( mir_forkthreadowner((pThreadFuncOwner)*(void**)&pFunc, this, param, &threadId)); +} + +////////////////////////////////////////////////////////// +// Forks a thread and returns a pseudo handle for it + +HANDLE GGPROTO::forkthreadex(GGThreadFunc pFunc, void *param, UINT *threadId) +{ + return mir_forkthreadowner((pThreadFuncOwner)*(void**)&pFunc, this, param, threadId); +} + +////////////////////////////////////////////////////////// +// Wait for thread to stop + +void GGPROTO::threadwait(GGTHREAD *thread) +{ + if (!thread->hThread) return; + while (WaitForSingleObjectEx(thread->hThread, INFINITE, TRUE) != WAIT_OBJECT_0); + CloseHandle(thread->hThread); + ZeroMemory(thread, sizeof(GGTHREAD)); +} + -- cgit v1.2.3