summaryrefslogtreecommitdiff
path: root/protocols/Gadu-Gadu/services.c
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Gadu-Gadu/services.c')
-rw-r--r--protocols/Gadu-Gadu/services.c1020
1 files changed, 1020 insertions, 0 deletions
diff --git a/protocols/Gadu-Gadu/services.c b/protocols/Gadu-Gadu/services.c
new file mode 100644
index 0000000000..bbb7434f6b
--- /dev/null
+++ b/protocols/Gadu-Gadu/services.c
@@ -0,0 +1,1020 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2009 Adam Strzelecki <ono+miranda@java.pl>
+// 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 <io.h>
+
+//////////////////////////////////////////////////////////
+// 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 (!DBGetContactSettingString(hContact, "CList", GG_KEY_STATUSDESCR, &dbv))
+ {
+ ProtoBroadcastAck(GG_PROTO, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE) 1, (LPARAM) dbv.pszVal);
+ gg_netlog(gg, "gg_getawaymsg(): Reading away msg \"%s\".", dbv.pszVal);
+ 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_INFORMATION *pai = (PROTO_AVATAR_INFORMATION *)lParam;
+ char *AvatarURL = NULL, *AvatarHash = NULL, *AvatarSavedHash = 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 (!DBGetContactSettingString(pai->hContact, GG_PROTO, GG_KEY_AVATARURL, &dbv)) {
+ AvatarURL = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ if (AvatarURL != NULL && strlen(AvatarURL) > 0) {
+ char *AvatarName = strrchr(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)
+{
+ char *szFilename = (char *)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_GETAVATARINFO, 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);
+}