diff options
Diffstat (limited to 'protocols/Gadu-Gadu/src/gg_proto.cpp')
-rw-r--r-- | protocols/Gadu-Gadu/src/gg_proto.cpp | 804 |
1 files changed, 804 insertions, 0 deletions
diff --git a/protocols/Gadu-Gadu/src/gg_proto.cpp b/protocols/Gadu-Gadu/src/gg_proto.cpp new file mode 100644 index 0000000000..0a0db3a173 --- /dev/null +++ b/protocols/Gadu-Gadu/src/gg_proto.cpp @@ -0,0 +1,804 @@ +////////////////////////////////////////////////////////////////////////////////
+// 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"
+
+GGPROTO::GGPROTO(const char* pszProtoName, const TCHAR* tszUserName)
+{
+ // Init mutexes
+ InitializeCriticalSection(&sess_mutex);
+ InitializeCriticalSection(&ft_mutex);
+ InitializeCriticalSection(&img_mutex);
+ InitializeCriticalSection(&modemsg_mutex);
+ InitializeCriticalSection(&avatar_mutex);
+ InitializeCriticalSection(&sessions_mutex);
+
+ // Init instance names
+ m_szModuleName = mir_strdup(pszProtoName);
+ m_tszUserName = mir_tstrdup(tszUserName);
+ m_szProtoName = GGDEF_PROTONAME;
+ m_iVersion = 2;
+
+ // Register netlib user
+ TCHAR name[128];
+ mir_sntprintf(name, SIZEOF(name), TranslateT("%s connection"), m_tszUserName);
+
+ NETLIBUSER nlu = { 0 };
+ nlu.cbSize = sizeof(nlu);
+ nlu.flags = NUF_TCHAR | NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS;
+ nlu.szSettingsModule = m_szModuleName;
+ nlu.ptszDescriptiveName = name;
+
+ netlib = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu);
+
+ // Register services
+ createProtoService(PS_GETAVATARCAPS, &GGPROTO::getavatarcaps);
+ createProtoService(PS_GETAVATARINFOT, &GGPROTO::getavatarinfo);
+ createProtoService(PS_GETMYAVATAR, &GGPROTO::getmyavatar);
+ createProtoService(PS_SETMYAVATAR, &GGPROTO::setmyavatar);
+
+ createProtoService(PS_GETMYAWAYMSG, &GGPROTO::getmyawaymsg);
+ createProtoService(PS_CREATEACCMGRUI, &GGPROTO::get_acc_mgr_gui);
+
+ createProtoService(PS_LEAVECHAT, &GGPROTO::leavechat);
+
+ // Offline contacts and clear logon time
+ setalloffline();
+ db_set_dw(NULL, m_szModuleName, GG_KEY_LOGONTIME, 0);
+
+ DWORD dwVersion;
+ if ((dwVersion = db_get_dw(NULL, m_szModuleName, GG_PLUGINVERSION, 0)) < pluginInfo.version)
+ cleanuplastplugin(dwVersion);
+
+ links_instance_init();
+ initavatarrequestthread();
+
+ TCHAR szPath[MAX_PATH];
+ TCHAR *tmpPath = Utils_ReplaceVarsT( _T("%miranda_avatarcache%"));
+ mir_sntprintf(szPath, MAX_PATH, _T("%s\\%s"), tmpPath, m_szModuleName);
+ mir_free(tmpPath);
+ hAvatarsFolder = FoldersRegisterCustomPathT(m_szModuleName, "Avatars", szPath);
+
+ tmpPath = Utils_ReplaceVarsT( _T("%miranda_userdata%"));
+ mir_sntprintf(szPath, MAX_PATH, _T("%s\\%s\\ImageCache"), tmpPath, m_szModuleName);
+ mir_free(tmpPath);
+ hImagesFolder = FoldersRegisterCustomPathT(m_szModuleName, "Images", szPath);
+}
+
+GGPROTO::~GGPROTO()
+{
+#ifdef DEBUGMODE
+ netlog("gg_proto_uninit(): destroying protocol interface");
+#endif
+
+ // Destroy modules
+ block_uninit();
+ img_destroy();
+ keepalive_destroy();
+ gc_destroy();
+
+ if (hMenuRoot)
+ CallService(MS_CLIST_REMOVEMAINMENUITEM, (WPARAM)hMenuRoot, 0);
+
+ // Close handles
+ Netlib_CloseHandle(netlib);
+
+ // Destroy mutexes
+ DeleteCriticalSection(&sess_mutex);
+ DeleteCriticalSection(&ft_mutex);
+ DeleteCriticalSection(&img_mutex);
+ DeleteCriticalSection(&modemsg_mutex);
+ DeleteCriticalSection(&avatar_mutex);
+ DeleteCriticalSection(&sessions_mutex);
+
+ // Free status messages
+ if (modemsg.online) mir_free(modemsg.online);
+ if (modemsg.away) mir_free(modemsg.away);
+ if (modemsg.dnd) mir_free(modemsg.dnd);
+ if (modemsg.freechat) mir_free(modemsg.freechat);
+ if (modemsg.invisible) mir_free(modemsg.invisible);
+ if (modemsg.offline) mir_free(modemsg.offline);
+
+ mir_free(m_szModuleName);
+ mir_free(m_tszUserName);
+}
+
+//////////////////////////////////////////////////////////
+// Dummies for function that have to be implemented
+
+HANDLE GGPROTO::AddToListByEvent(int flags, int iContact, HANDLE hDbEvent) { return NULL; }
+int GGPROTO::Authorize(HANDLE hDbEvent) { return 1; }
+int GGPROTO::AuthDeny(HANDLE hDbEvent, const TCHAR *szReason) { return 1; }
+int GGPROTO::AuthRecv(HANDLE hContact, PROTORECVEVENT *pre) { return 1; }
+int GGPROTO::AuthRequest(HANDLE hContact, const TCHAR *szMessage) { return 1; }
+HANDLE GGPROTO::ChangeInfo(int iInfoType, void *pInfoData) { return NULL; }
+int GGPROTO::FileResume(HANDLE hTransfer, int *action, const PROTOCHAR** szFilename) { return 1; }
+HANDLE GGPROTO::SearchByEmail(const PROTOCHAR *email) { return NULL; }
+int GGPROTO::RecvContacts(HANDLE hContact, PROTORECVEVENT *pre) { return 1; }
+int GGPROTO::RecvUrl(HANDLE hContact, PROTORECVEVENT *pre) { return 1; }
+int GGPROTO::SendContacts(HANDLE hContact, int flags, int nContacts, HANDLE *hContactsList) { return 1; }
+int GGPROTO::SendUrl(HANDLE hContact, int flags, const char *url) { return 1; }
+int GGPROTO::RecvAwayMsg(HANDLE hContact, int mode, PROTORECVEVENT *evt) { return 1; }
+int GGPROTO::SendAwayMsg(HANDLE hContact, HANDLE hProcess, const char *msg) { return 1; }
+
+//////////////////////////////////////////////////////////
+// when contact is added to list
+
+HANDLE GGPROTO::AddToList(int flags, PROTOSEARCHRESULT *psr)
+{
+ GGSEARCHRESULT *sr = (GGSEARCHRESULT *)psr;
+ uin_t uin;
+
+ if (psr->cbSize == sizeof(GGSEARCHRESULT))
+ uin = sr->uin;
+ else
+ uin = _ttoi(psr->id);
+
+ return getcontact(uin, 1, flags & PALF_TEMPORARY ? 0 : 1, sr->nick);
+}
+
+//////////////////////////////////////////////////////////
+// checks proto capabilities
+
+DWORD_PTR GGPROTO::GetCaps(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 GGPROTO::GetIcon(int iconIndex)
+{
+ if (LOWORD(iconIndex) == PLI_PROTOCOL)
+ {
+ if (iconIndex & PLIF_ICOLIBHANDLE)
+ return (HICON)GetIconHandle(IDI_GG);
+
+ BOOL big = (iconIndex & PLIF_SMALL) == 0;
+ HICON hIcon = LoadIconEx("main", big);
+
+ if (iconIndex & PLIF_ICOLIB)
+ return hIcon;
+
+ hIcon = CopyIcon(hIcon);
+ ReleaseIconEx("main", big);
+ return hIcon;
+ }
+
+ return (HICON)NULL;
+}
+
+//////////////////////////////////////////////////////////
+// user info request
+
+void __cdecl GGPROTO::cmdgetinfothread(void *hContact)
+{
+ SleepEx(100, FALSE);
+ netlog("gg_cmdgetinfothread(): Failed info retreival.");
+ ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE) 1, 0);
+}
+
+int GGPROTO::GetInfo(HANDLE hContact, int infoType)
+{
+ gg_pubdir50_t req;
+
+ // Custom contact info
+ if (hContact)
+ {
+ if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH)))
+ {
+ forkthread(&GGPROTO::cmdgetinfothread, hContact);
+ return 1;
+ }
+
+ // Add uin and search it
+ gg_pubdir50_add(req, GG_PUBDIR50_UIN, ditoa((uin_t)db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0)));
+ gg_pubdir50_seq_set(req, GG_SEQ_INFO);
+
+ netlog("gg_getinfo(): Requesting user info.", req->seq);
+ if (isonline())
+ {
+ EnterCriticalSection(&sess_mutex);
+ if (!gg_pubdir50(sess, req))
+ {
+ LeaveCriticalSection(&sess_mutex);
+ forkthread(&GGPROTO::cmdgetinfothread, hContact);
+ return 1;
+ }
+ LeaveCriticalSection(&sess_mutex);
+ }
+ }
+ // Own contact info
+ else
+ {
+ if (!(req = gg_pubdir50_new(GG_PUBDIR50_READ)))
+ {
+ forkthread(&GGPROTO::cmdgetinfothread, hContact);
+ return 1;
+ }
+
+ // Add seq
+ gg_pubdir50_seq_set(req, GG_SEQ_CHINFO);
+
+ netlog("gg_getinfo(): Requesting owner info.", req->seq);
+ if (isonline())
+ {
+ EnterCriticalSection(&sess_mutex);
+ if (!gg_pubdir50(sess, req))
+ {
+ LeaveCriticalSection(&sess_mutex);
+ forkthread(&GGPROTO::cmdgetinfothread, hContact);
+ return 1;
+ }
+ LeaveCriticalSection(&sess_mutex);
+ }
+ }
+ netlog("gg_getinfo(): Seq %d.", req->seq);
+ gg_pubdir50_free(req);
+
+ return 1;
+}
+
+//////////////////////////////////////////////////////////
+// when basic search
+
+void __cdecl GGPROTO::searchthread(void *)
+{
+ SleepEx(100, FALSE);
+ netlog("gg_searchthread(): Failed search.");
+ ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)1, 0);
+}
+
+HANDLE GGPROTO::SearchBasic(const PROTOCHAR *id)
+{
+ if (!isonline())
+ return (HANDLE)0;
+
+ gg_pubdir50_t req;
+ if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) {
+ forkthread(&GGPROTO::searchthread, NULL);
+ return (HANDLE)1;
+ }
+
+ TCHAR *ida = mir_tstrdup(id);
+
+ // Add uin and search it
+ gg_pubdir50_add(req, GG_PUBDIR50_UIN, _T2A(ida));
+ gg_pubdir50_seq_set(req, GG_SEQ_SEARCH);
+
+ mir_free(ida);
+
+ EnterCriticalSection(&sess_mutex);
+ if (!gg_pubdir50(sess, req))
+ {
+ LeaveCriticalSection(&sess_mutex);
+ forkthread(&GGPROTO::searchthread, NULL);
+ return (HANDLE)1;
+ }
+ LeaveCriticalSection(&sess_mutex);
+ netlog("gg_basicsearch(): Seq %d.", req->seq);
+ gg_pubdir50_free(req);
+
+ return (HANDLE)1;
+}
+
+//////////////////////////////////////////////////////////
+// search by details
+
+HANDLE GGPROTO::SearchByName(const PROTOCHAR *nick, const PROTOCHAR *firstName, const PROTOCHAR *lastName)
+{
+ gg_pubdir50_t req;
+ unsigned long crc;
+ char data[512] = "\0";
+
+ // Check if connected and if there's a search data
+ if (!isonline())
+ return 0;
+
+ if (!nick && !firstName && !lastName)
+ return 0;
+
+ if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH)))
+ {
+ forkthread(&GGPROTO::searchthread, NULL);
+ return (HANDLE)1;
+ }
+
+ // Add uin and search it
+ if (nick)
+ {
+ char *nickA = mir_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 = mir_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 = mir_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 == last_crc && next_uin)
+ gg_pubdir50_add(req, GG_PUBDIR50_START, ditoa(next_uin));
+ else
+ last_crc = crc;
+
+ gg_pubdir50_seq_set(req, GG_SEQ_SEARCH);
+
+ EnterCriticalSection(&sess_mutex);
+ if (!gg_pubdir50(sess, req))
+ {
+ LeaveCriticalSection(&sess_mutex);
+ forkthread(&GGPROTO::searchthread, NULL);
+ return (HANDLE)1;
+ }
+ LeaveCriticalSection(&sess_mutex);
+ netlog("gg_searchbyname(): Seq %d.", req->seq);
+ gg_pubdir50_free(req);
+
+ return (HANDLE)1;
+}
+
+//////////////////////////////////////////////////////////
+// search by advanced
+
+HWND GGPROTO::SearchAdvanced(HWND hwndDlg)
+{
+ gg_pubdir50_t req;
+ char text[64], data[512] = "\0";
+ unsigned long crc;
+
+ // Check if connected
+ if (!isonline()) return (HWND)0;
+
+ if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH)))
+ {
+ forkthread(&GGPROTO::searchthread, NULL);
+ return (HWND)1;
+ }
+
+ // Fetch search data
+ GetDlgItemTextA(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));
+
+ GetDlgItemTextA(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));
+
+ GetDlgItemTextA(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));
+
+ GetDlgItemTextA(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));
+
+ GetDlgItemTextA(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];
+
+ GetDlgItemTextA(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 == last_crc && next_uin)
+ gg_pubdir50_add(req, GG_PUBDIR50_START, ditoa(next_uin));
+ else
+ last_crc = crc;
+
+ gg_pubdir50_seq_set(req, GG_SEQ_SEARCH);
+
+ if (isonline())
+ {
+ EnterCriticalSection(&sess_mutex);
+ if (!gg_pubdir50(sess, req))
+ {
+ LeaveCriticalSection(&sess_mutex);
+ forkthread(&GGPROTO::searchthread, NULL);
+ return (HWND)1;
+ }
+ LeaveCriticalSection(&sess_mutex);
+ }
+ netlog("gg_searchbyadvanced(): Seq %d.", req->seq);
+ gg_pubdir50_free(req);
+
+ return (HWND)1;
+}
+
+//////////////////////////////////////////////////////////
+// create adv search dialog
+
+static 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)TranslateT("Female")); // 1
+ SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)TranslateT("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;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+HWND GGPROTO::CreateExtendedSearchUI(HWND owner)
+{
+ return CreateDialogParam(hInstance,
+ MAKEINTRESOURCE(IDD_GGADVANCEDSEARCH), owner, gg_advancedsearchdlgproc, (LPARAM)this);
+}
+
+//////////////////////////////////////////////////////////
+// when messsage received
+
+int GGPROTO::RecvMsg(HANDLE hContact, PROTORECVEVENT *pre)
+{
+ return Proto_RecvMessage(hContact, pre);
+}
+
+//////////////////////////////////////////////////////////
+// when messsage sent
+
+typedef struct
+{
+ HANDLE hContact;
+ int seq;
+} GG_SEQ_ACK;
+
+void __cdecl GGPROTO::sendackthread(void *ack)
+{
+ SleepEx(100, FALSE);
+ ProtoBroadcastAck(m_szModuleName, ((GG_SEQ_ACK *)ack)->hContact,
+ ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE) ((GG_SEQ_ACK *)ack)->seq, 0);
+ mir_free(ack);
+}
+
+int GGPROTO::SendMsg(HANDLE hContact, int flags, const char *msg)
+{
+ uin_t uin;
+
+ if (msg && isonline() && (uin = (uin_t)db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0)))
+ {
+ int seq;
+ EnterCriticalSection(&sess_mutex);
+ seq = gg_send_message(sess, GG_CLASS_CHAT, uin, (BYTE*)msg);
+ LeaveCriticalSection(&sess_mutex);
+ if (!db_get_b(NULL, m_szModuleName, GG_KEY_MSGACK, GG_KEYDEF_MSGACK))
+ {
+ // Auto-ack message without waiting for server ack
+ GG_SEQ_ACK *ack = (GG_SEQ_ACK*)mir_alloc(sizeof(GG_SEQ_ACK));
+ if (ack)
+ {
+ ack->seq = seq;
+ ack->hContact = hContact;
+ forkthread(&GGPROTO::sendackthread, ack);
+ }
+ }
+ return seq;
+ }
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// visible lists
+
+int GGPROTO::SetApparentMode(HANDLE hContact, int mode)
+{
+ db_set_w(hContact, m_szModuleName, GG_KEY_APPARENT, (WORD)mode);
+ notifyuser(hContact, 1);
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// sets protocol status
+
+int GGPROTO::SetStatus(int iNewStatus)
+{
+ int nNewStatus = gg_normalizestatus(iNewStatus);
+
+ EnterCriticalSection(&modemsg_mutex);
+ m_iDesiredStatus = nNewStatus;
+ LeaveCriticalSection(&modemsg_mutex);
+
+ // If waiting for connection retry attempt then signal to stop that
+ if (hConnStopEvent) SetEvent(hConnStopEvent);
+
+ if (m_iStatus == nNewStatus) return 0;
+ netlog("gg_setstatus(): PS_SETSTATUS(%d) normalized to %d.", iNewStatus, nNewStatus);
+ refreshstatus(nNewStatus);
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// when away message is requested
+
+void __cdecl GGPROTO::getawaymsgthread(void *hContact)
+{
+ DBVARIANT dbv;
+
+ SleepEx(100, FALSE);
+ if (!db_get_s(hContact, "CList", GG_KEY_STATUSDESCR, &dbv, DBVT_TCHAR))
+ {
+ ProtoBroadcastAck(m_szProtoName, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE) 1, (LPARAM) dbv.ptszVal);
+ netlog("gg_getawaymsg(): Reading away msg <" TCHAR_STR_PARAM ">.", dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ ProtoBroadcastAck(m_szProtoName, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE) 1, (LPARAM) NULL);
+}
+
+HANDLE GGPROTO::GetAwayMsg(HANDLE hContact)
+{
+ forkthread(&GGPROTO::getawaymsgthread, hContact);
+ return (HANDLE)1;
+}
+
+//////////////////////////////////////////////////////////
+// when away message is being set
+
+int GGPROTO::SetAwayMsg(int iStatus, const PROTOCHAR *msgt)
+{
+ int status = gg_normalizestatus(iStatus);
+ char **szMsg;
+ char *msg = mir_t2a(msgt);
+
+ netlog("gg_setawaymsg(): PS_SETAWAYMSG(%d, \"%s\").", iStatus, msg);
+
+ EnterCriticalSection(&modemsg_mutex);
+ // Select proper msg
+ switch(status)
+ {
+ case ID_STATUS_ONLINE:
+ szMsg = &modemsg.online;
+ break;
+ case ID_STATUS_AWAY:
+ szMsg = &modemsg.away;
+ break;
+ case ID_STATUS_DND:
+ szMsg = &modemsg.dnd;
+ break;
+ case ID_STATUS_FREECHAT:
+ szMsg = &modemsg.freechat;
+ break;
+ case ID_STATUS_INVISIBLE:
+ szMsg = &modemsg.invisible;
+ break;
+ default:
+ LeaveCriticalSection(&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 == m_iDesiredStatus && m_iDesiredStatus == m_iStatus)
+ {
+ netlog("gg_setawaymsg(): Message hasn't been changed, return.");
+ LeaveCriticalSection(&modemsg_mutex);
+ mir_free(msg);
+ return 0;
+ }
+ }
+ else
+ {
+ if (*szMsg)
+ mir_free(*szMsg);
+ *szMsg = msg && *msg ? mir_strdup(msg) : NULL;
+#ifdef DEBUGMODE
+ netlog("gg_setawaymsg(): Message changed.");
+#endif
+ }
+ LeaveCriticalSection(&modemsg_mutex);
+
+ // Change the status if it was desired by PS_SETSTATUS
+ if (status == m_iDesiredStatus)
+ refreshstatus(status);
+
+ mir_free(msg);
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// sends a notification that the user is typing a message
+
+int GGPROTO::UserIsTyping(HANDLE hContact, int type)
+{
+ uin_t uin = db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0);
+
+ if (!uin || !isonline()) return 0;
+
+ if (type == PROTOTYPE_SELFTYPING_ON || type == PROTOTYPE_SELFTYPING_OFF) {
+ EnterCriticalSection(&sess_mutex);
+ gg_typing_notification(sess, uin, (type == PROTOTYPE_SELFTYPING_ON));
+ LeaveCriticalSection(&sess_mutex);
+ }
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// Custom protocol event
+
+int GGPROTO::OnEvent(PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam)
+{
+ switch( eventType ) {
+ case EV_PROTO_ONLOAD:
+ {
+ hookProtoEvent(ME_OPT_INITIALISE, &GGPROTO::options_init);
+ hookProtoEvent(ME_USERINFO_INITIALISE, &GGPROTO::details_init);
+
+ // Init misc stuff
+ gg_icolib_init();
+ initpopups();
+ gc_init();
+ keepalive_init();
+ img_init();
+ block_init();
+
+ // Try to fetch user avatar
+ getUserAvatar();
+ break;
+ }
+ case EV_PROTO_ONEXIT:
+ // Stop avatar request thread
+ uninitavatarrequestthread();
+
+ // Stop main connection session thread
+ threadwait(&pth_sess);
+ img_shutdown();
+ sessions_closedlg();
+ break;
+
+ case EV_PROTO_ONOPTIONS:
+ return options_init(wParam, lParam);
+
+ case EV_PROTO_ONMENU:
+ menus_init();
+ break;
+
+ case EV_PROTO_ONRENAME:
+ if (hMenuRoot) {
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_NAME | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED;
+ mi.ptszName = m_tszUserName;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuRoot, (LPARAM)&mi);
+ }
+ break;
+
+ case EV_PROTO_ONCONTACTDELETED:
+ return contactdeleted(wParam, lParam);
+
+ case EV_PROTO_DBSETTINGSCHANGED:
+ return dbsettingchanged(wParam, lParam);
+ }
+ return TRUE;
+}
|