summaryrefslogtreecommitdiff
path: root/protocols/AimOscar/proto.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/AimOscar/proto.cpp')
-rw-r--r--protocols/AimOscar/proto.cpp920
1 files changed, 920 insertions, 0 deletions
diff --git a/protocols/AimOscar/proto.cpp b/protocols/AimOscar/proto.cpp
new file mode 100644
index 0000000000..e122ebe0ae
--- /dev/null
+++ b/protocols/AimOscar/proto.cpp
@@ -0,0 +1,920 @@
+/*
+Plugin of Miranda IM for communicating with users of the AIM protocol.
+Copyright (c) 2008-2012 Boris Krasnovskiy
+
+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, see <http://www.gnu.org/licenses/>.
+*/
+#include "aim.h"
+
+#include "m_genmenu.h"
+
+CAimProto::CAimProto(const char* aProtoName, const TCHAR* aUserName)
+ : chat_rooms(5)
+{
+ m_iVersion = 2;
+ m_tszUserName = mir_tstrdup(aUserName);
+ m_szModuleName = mir_strdup(aProtoName);
+ m_szProtoName = mir_strdup(aProtoName);
+ _strlwr(m_szProtoName);
+ m_szProtoName[0] = (char)toupper(m_szProtoName[0]);
+ LOG("Setting protocol/module name to '%s/%s'", m_szProtoName, m_szModuleName);
+
+ //create some events
+ hAvatarEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ hChatNavEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ hAdminEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ InitializeCriticalSection(&SendingMutex);
+ InitializeCriticalSection(&connMutex);
+
+ CreateProtoService(PS_CREATEACCMGRUI, &CAimProto::SvcCreateAccMgrUI);
+
+ CreateProtoService(PS_GETMYAWAYMSG, &CAimProto::GetMyAwayMsg);
+
+ CreateProtoService(PS_GETAVATARINFOT, &CAimProto::GetAvatarInfo);
+ CreateProtoService(PS_GETMYAVATART, &CAimProto::GetAvatar);
+ CreateProtoService(PS_SETMYAVATART, &CAimProto::SetAvatar);
+ CreateProtoService(PS_GETAVATARCAPS, &CAimProto::GetAvatarCaps);
+
+ CreateProtoService(PS_JOINCHAT, &CAimProto::OnJoinChat);
+ CreateProtoService(PS_LEAVECHAT, &CAimProto::OnLeaveChat);
+
+ HookProtoEvent(ME_CLIST_PREBUILDCONTACTMENU, &CAimProto::OnPreBuildContactMenu);
+ HookProtoEvent(ME_CLIST_GROUPCHANGE, &CAimProto::OnGroupChange);
+ HookProtoEvent(ME_OPT_INITIALISE, &CAimProto::OnOptionsInit);
+
+ init_custom_folders();
+ offline_contacts();
+
+ TCHAR descr[MAX_PATH];
+
+ NETLIBUSER nlu = {0};
+ nlu.cbSize = sizeof(nlu);
+ nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS | NUF_TCHAR;
+ nlu.szSettingsModule = m_szModuleName;
+ mir_sntprintf(descr, SIZEOF(descr), TranslateT("%s server connection"), m_tszUserName);
+ nlu.ptszDescriptiveName = descr;
+ hNetlib = (HANDLE) CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu);
+
+ char szP2P[128];
+ mir_snprintf(szP2P, sizeof(szP2P), "%sP2P", m_szModuleName);
+ nlu.flags = NUF_OUTGOING | NUF_INCOMING | NUF_TCHAR;
+ mir_sntprintf(descr, SIZEOF(descr), TranslateT("%s Client-to-client connection"), m_tszUserName);
+ nlu.szSettingsModule = szP2P;
+ nlu.minIncomingPorts = 1;
+ hNetlibPeer = (HANDLE) CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu);
+}
+
+CAimProto::~CAimProto()
+{
+ RemoveMainMenus();
+ RemoveContactMenus();
+
+ if(hDirectBoundPort)
+ Netlib_CloseHandle(hDirectBoundPort);
+ if(hServerConn)
+ Netlib_CloseHandle(hServerConn);
+ if(hAvatarConn && hAvatarConn != (HANDLE)1)
+ Netlib_CloseHandle(hAvatarConn);
+ if(hChatNavConn && hChatNavConn != (HANDLE)1)
+ Netlib_CloseHandle(hChatNavConn);
+ if(hAdminConn && hAdminConn != (HANDLE)1)
+ Netlib_CloseHandle(hAdminConn);
+
+ close_chat_conn();
+
+ Netlib_CloseHandle(hNetlib);
+ Netlib_CloseHandle(hNetlibPeer);
+
+ DeleteCriticalSection(&SendingMutex);
+ DeleteCriticalSection(&connMutex);
+
+ CloseHandle(hAvatarEvent);
+ CloseHandle(hChatNavEvent);
+ CloseHandle(hAdminEvent);
+
+ ft_list.destroy();
+
+ for (int i=0; i<9; ++i)
+ mir_free(modeMsgs[i]);
+
+ mir_free(pref2_flags);
+ mir_free(pref2_set_flags);
+
+ mir_free(COOKIE);
+ mir_free(MAIL_COOKIE);
+ mir_free(AVATAR_COOKIE);
+ mir_free(CHATNAV_COOKIE);
+ mir_free(ADMIN_COOKIE);
+ mir_free(username);
+
+ mir_free(m_szModuleName);
+ mir_free(m_tszUserName);
+ mir_free(m_szProtoName);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// OnModulesLoadedEx - performs hook registration
+
+int CAimProto::OnModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ HookProtoEvent(ME_USERINFO_INITIALISE, &CAimProto::OnUserInfoInit);
+ HookProtoEvent(ME_IDLE_CHANGED, &CAimProto::OnIdleChanged);
+ HookProtoEvent(ME_MSG_WINDOWEVENT, &CAimProto::OnWindowEvent);
+
+ chat_register();
+ InitContactMenus();
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// AddToList - adds a contact to the contact list
+
+HANDLE CAimProto::AddToList(int flags, PROTOSEARCHRESULT* psr)
+{
+ if (state != 1) return 0;
+ TCHAR *id = psr->id ? psr->id : psr->nick;
+ char *sn = psr->flags & PSR_UNICODE ? mir_u2a((wchar_t*)id) : mir_strdup((char*)id);
+ HANDLE hContact = contact_from_sn(sn, true, (flags & PALF_TEMPORARY) != 0);
+ mir_free(sn);
+ return hContact; //See authrequest for serverside addition
+}
+
+HANDLE __cdecl CAimProto::AddToListByEvent(int flags, int iContact, HANDLE hDbEvent)
+{
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// AuthAllow - processes the successful authorization
+
+int CAimProto::Authorize(HANDLE hContact)
+{
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// AuthDeny - handles the unsuccessful authorization
+
+int CAimProto::AuthDeny(HANDLE hContact, const TCHAR* szReason)
+{
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PSR_AUTH
+
+int __cdecl CAimProto::AuthRecv(HANDLE hContact, PROTORECVEVENT* evt)
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PSS_AUTHREQUEST
+
+int __cdecl CAimProto::AuthRequest(HANDLE hContact, const TCHAR* szMessage)
+{
+ //Not a real authrequest- only used b/c we don't know the group until now.
+ if (state != 1)
+ return 1;
+
+ DBVARIANT dbv;
+ if (!DBGetContactSettingStringUtf(hContact, MOD_KEY_CL, OTH_KEY_GP, &dbv) && dbv.pszVal[0])
+ {
+ add_contact_to_group(hContact, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else add_contact_to_group(hContact, AIM_DEFAULT_GROUP);
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// ChangeInfo
+
+HANDLE __cdecl CAimProto::ChangeInfo(int iInfoType, void* pInfoData)
+{
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// FileAllow - starts a file transfer
+
+HANDLE __cdecl CAimProto::FileAllow(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath)
+{
+ file_transfer *ft = (file_transfer*)hTransfer;
+ if (ft && ft_list.find_by_ft(ft))
+ {
+ char *path = mir_utf8encodeT(szPath);
+
+ if (ft->pfts.totalFiles > 1 && ft->file[0])
+ {
+ size_t path_len = strlen(path);
+ size_t len = strlen(ft->file) + 2;
+
+ path = (char*)mir_realloc(path, path_len + len);
+ mir_snprintf(&path[path_len], len, "%s\\", ft->file);
+ }
+
+ mir_free(ft->file);
+ ft->file = path;
+
+ ForkThread(&CAimProto::accept_file_thread, ft);
+ return ft;
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// FileCancel - cancels a file transfer
+
+int __cdecl CAimProto::FileCancel(HANDLE hContact, HANDLE hTransfer)
+{
+ file_transfer *ft = (file_transfer*)hTransfer;
+ if (!ft_list.find_by_ft(ft)) return 0;
+
+ LOG("We are cancelling a file transfer.");
+
+ aim_chat_deny(hServerConn, seqno, ft->sn, ft->icbm_cookie);
+
+ if (ft->hConn)
+ {
+ Netlib_Shutdown(ft->hConn);
+ SetEvent(ft->hResumeEvent);
+ }
+ else
+ ft_list.remove_by_ft(ft);
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// FileDeny - denies a file transfer
+
+int __cdecl CAimProto::FileDeny(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* /*szReason*/)
+{
+ file_transfer *ft = (file_transfer*)hTransfer;
+ if (!ft_list.find_by_ft(ft)) return 0;
+
+ LOG("We are denying a file transfer.");
+
+ aim_chat_deny(hServerConn, seqno, ft->sn, ft->icbm_cookie);
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// FileResume - processes file renaming etc
+
+int __cdecl CAimProto::FileResume(HANDLE hTransfer, int* action, const PROTOCHAR** szFilename)
+{
+ file_transfer *ft = (file_transfer*)hTransfer;
+ if (!ft_list.find_by_ft(ft)) return 0;
+
+ switch (*action)
+ {
+ case FILERESUME_RESUME:
+ {
+ struct _stati64 statbuf;
+ _tstati64(ft->pfts.tszCurrentFile, &statbuf);
+ ft->pfts.currentFileProgress = statbuf.st_size;
+ }
+ break;
+
+ case FILERESUME_RENAME:
+ mir_free(ft->pfts.tszCurrentFile);
+ ft->pfts.tszCurrentFile = mir_tstrdup(*szFilename);
+ break;
+
+ case FILERESUME_OVERWRITE:
+ ft->pfts.currentFileProgress = 0;
+ break;
+
+ case FILERESUME_SKIP:
+ mir_free(ft->pfts.tszCurrentFile);
+ ft->pfts.tszCurrentFile = NULL;
+ break;
+
+ default:
+ aim_file_ad(hServerConn, seqno, ft->sn, ft->icbm_cookie, true, ft->max_ver);
+ break;
+ }
+ SetEvent(ft->hResumeEvent);
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// GetCaps - return protocol capabilities bits
+
+DWORD_PTR __cdecl CAimProto::GetCaps(int type, HANDLE hContact)
+{
+ switch (type)
+ {
+ case PFLAGNUM_1:
+ return PF1_IM | PF1_MODEMSG | PF1_BASICSEARCH | PF1_SEARCHBYEMAIL | PF1_FILE;
+
+ case PFLAGNUM_2:
+#ifdef ALLOW_BUSY
+ return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_ONTHEPHONE | PF2_LIGHTDND;
+#else
+ return PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_ONTHEPHONE;
+#endif
+
+ case PFLAGNUM_3:
+#ifdef ALLOW_BUSY
+ return PF2_ONLINE | PF2_SHORTAWAY | PF2_INVISIBLE | PF2_LIGHTDND;
+#else
+ return PF2_ONLINE | PF2_SHORTAWAY | PF2_INVISIBLE;
+#endif
+
+ case PFLAGNUM_4:
+ return PF4_SUPPORTTYPING | PF4_FORCEAUTH | PF4_NOCUSTOMAUTH | PF4_FORCEADDED |
+ PF4_SUPPORTIDLE | PF4_AVATARS | PF4_IMSENDUTF | PF4_IMSENDOFFLINE;
+
+ case PFLAGNUM_5:
+ return PF2_ONTHEPHONE;
+
+ case PFLAG_MAXLENOFMESSAGE:
+ return MAX_MESSAGE_LENGTH;
+
+ case PFLAG_UNIQUEIDTEXT:
+ return (DWORD_PTR) "Screen Name";
+
+ case PFLAG_UNIQUEIDSETTING:
+ return (DWORD_PTR) AIM_KEY_SN;
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// GetIcon - loads an icon for the contact list
+
+HICON __cdecl CAimProto::GetIcon(int iconIndex)
+{
+ if (LOWORD(iconIndex) == PLI_PROTOCOL)
+ {
+ if (iconIndex & PLIF_ICOLIBHANDLE)
+ return (HICON)GetIconHandle("aim");
+
+ bool big = (iconIndex & PLIF_SMALL) == 0;
+ HICON hIcon = LoadIconEx("aim", big);
+
+ if (iconIndex & PLIF_ICOLIB)
+ return hIcon;
+
+ hIcon = CopyIcon(hIcon);
+ ReleaseIconEx("aim", big);
+ return hIcon;
+ }
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// GetInfo - retrieves a contact info
+
+int __cdecl CAimProto::GetInfo(HANDLE hContact, int infoType)
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SearchBasic - searches the contact by JID
+
+void __cdecl CAimProto::basic_search_ack_success(void* p)
+{
+ char *sn = normalize_name((char*)p);
+ if (sn) // normalize it
+ {
+ if (strlen(sn) > 32)
+ {
+ sendBroadcast(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE) 1, 0);
+ }
+ else
+ {
+ PROTOSEARCHRESULT psr = {0};
+ psr.cbSize = sizeof(psr);
+ psr.id = (TCHAR*)sn;
+ sendBroadcast(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE) 1, (LPARAM) & psr);
+ sendBroadcast(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE) 1, 0);
+ }
+ }
+ mir_free(sn);
+ mir_free(p);
+}
+
+HANDLE __cdecl CAimProto::SearchBasic(const PROTOCHAR* szId)
+{
+ if (state != 1)
+ return 0;
+
+ //duplicating the parameter so that it isn't deleted before it's needed- e.g. this function ends before it's used
+ ForkThread(&CAimProto::basic_search_ack_success, mir_t2a(szId));
+ return (HANDLE)1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SearchByEmail - searches the contact by its e-mail
+
+HANDLE __cdecl CAimProto::SearchByEmail(const PROTOCHAR* email)
+{
+ // Maximum email size should really be 320, but the char string is limited to 255.
+ if (state != 1 || email == NULL || _tcslen(email) >= 254)
+ return NULL;
+
+ char* szEmail = mir_t2a(email);
+ aim_search_by_email(hServerConn, seqno, szEmail);
+ mir_free(szEmail);
+ return (HANDLE)1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SearchByName - searches the contact by its first or last name, or by a nickname
+
+HANDLE __cdecl CAimProto::SearchByName(const PROTOCHAR* nick, const PROTOCHAR* firstName, const PROTOCHAR* lastName)
+{
+ return NULL;
+}
+
+HWND __cdecl CAimProto::SearchAdvanced(HWND owner)
+{
+ return NULL;
+}
+
+HWND __cdecl CAimProto::CreateExtendedSearchUI(HWND owner)
+{
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// RecvContacts
+
+int __cdecl CAimProto::RecvContacts(HANDLE hContact, PROTORECVEVENT*)
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// RecvFile
+
+int __cdecl CAimProto::RecvFile(HANDLE hContact, PROTOFILEEVENT* evt)
+{
+ CCSDATA ccs = { hContact, PSR_FILE, 0, (LPARAM)evt };
+ return CallService(MS_PROTO_RECVFILET, 0, (LPARAM)&ccs);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// RecvMsg
+
+int __cdecl CAimProto::RecvMsg(HANDLE hContact, PROTORECVEVENT* pre)
+{
+ CCSDATA ccs = { hContact, PSR_MESSAGE, 0, (LPARAM)pre };
+
+ char *omsg = pre->szMessage;
+ char *bbuf = NULL;
+ if (getByte(AIM_KEY_FI, 1))
+ {
+ LOG("Converting from html to bbcodes then stripping leftover html.");
+ pre->szMessage = bbuf = html_to_bbcodes(pre->szMessage);
+ }
+ LOG("Stripping html.");
+ html_decode(pre->szMessage);
+
+ HANDLE res = (HANDLE)CallService(MS_PROTO_RECVMSG, 0, (LPARAM)&ccs);
+ mir_free(bbuf);
+ pre->szMessage = omsg;
+
+ return ( int )res;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// RecvUrl
+
+int __cdecl CAimProto::RecvUrl(HANDLE hContact, PROTORECVEVENT*)
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SendContacts
+
+int __cdecl CAimProto::SendContacts(HANDLE hContact, int flags, int nContacts, HANDLE* hContactsList)
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SendFile - sends a file
+
+HANDLE __cdecl CAimProto::SendFile(HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles)
+{
+ if (state != 1) return 0;
+
+ if (hContact && szDescription && ppszFiles)
+ {
+ DBVARIANT dbv;
+ if (!getString(hContact, AIM_KEY_SN, &dbv))
+ {
+ file_transfer *ft = new file_transfer(hContact, dbv.pszVal, NULL);
+
+ bool isDir = false;
+ int count = 0;
+ while (ppszFiles[count] != NULL)
+ {
+ struct _stati64 statbuf;
+ if (_tstati64(ppszFiles[count++], &statbuf) == 0)
+ {
+ if (statbuf.st_mode & _S_IFDIR)
+ {
+ if (ft->pfts.totalFiles == 0) isDir = true;
+ }
+ else
+ {
+ ft->pfts.totalBytes += statbuf.st_size;
+ ++ft->pfts.totalFiles;
+ }
+ }
+ }
+
+ if (ft->pfts.totalFiles == 0)
+ {
+ delete ft;
+ return NULL;
+ }
+
+ ft->pfts.flags |= PFTS_SENDING;
+ ft->pfts.ptszFiles = ppszFiles;
+
+ ft->file = ft->pfts.totalFiles == 1 || isDir ? mir_utf8encodeT(ppszFiles[0]) : (char*)mir_calloc(1);
+ ft->sending = true;
+ ft->message = szDescription[0] ? mir_utf8encodeT(szDescription) : NULL;
+ ft->me_force_proxy = getByte(AIM_KEY_FP, 0) != 0;
+ ft->requester = true;
+
+ ft_list.insert(ft);
+
+ if (ft->me_force_proxy)
+ {
+ LOG("We are forcing a proxy file transfer.");
+ ForkThread(&CAimProto::accept_file_thread, ft);
+ }
+ else
+ {
+ aim_send_file(hServerConn, seqno, detected_ip, local_port, false, ft);
+ }
+
+ DBFreeVariant(&dbv);
+
+ return ft;
+ }
+ }
+
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SendMessage - sends a message
+
+void __cdecl CAimProto::msg_ack_success(void* param)
+{
+ msg_ack_param *msg_ack = (msg_ack_param*)param;
+
+ Sleep(150);
+ sendBroadcast(msg_ack->hContact, ACKTYPE_MESSAGE,
+ msg_ack->success ? ACKRESULT_SUCCESS : ACKRESULT_FAILED,
+ (HANDLE)msg_ack->id, (LPARAM)msg_ack->msg);
+
+ mir_free(msg_ack);
+}
+
+
+int __cdecl CAimProto::SendMsg(HANDLE hContact, int flags, const char* pszSrc)
+{
+ if (pszSrc == NULL) return 0;
+
+ if (state != 1)
+ {
+ msg_ack_param *msg_ack = (msg_ack_param*)mir_calloc(sizeof(msg_ack_param));
+ msg_ack->hContact = hContact;
+ msg_ack->msg = "Message cannot be sent, when protocol offline";
+ ForkThread(&CAimProto::msg_ack_success, msg_ack);
+ }
+
+ char *sn = getSetting(hContact, AIM_KEY_SN);
+ if (sn == NULL)
+ {
+ msg_ack_param *msg_ack = (msg_ack_param*)mir_calloc(sizeof(msg_ack_param));
+ msg_ack->hContact = hContact;
+ msg_ack->msg = "Screen Name for the contact not available";
+ ForkThread(&CAimProto::msg_ack_success, msg_ack);
+ }
+
+ char* msg;
+ if (flags & PREF_UNICODE)
+ {
+ const char* p = strchr(pszSrc, '\0');
+ if (p != pszSrc)
+ {
+ while (*(++p) == '\0');
+ }
+ msg = mir_utf8encodeW((wchar_t*)p);
+ }
+ else if (flags & PREF_UTF)
+ msg = mir_strdup(pszSrc);
+ else
+ msg = mir_utf8encode(pszSrc);
+
+ char* smsg = html_encode(msg);
+ mir_free(msg);
+
+ if (getByte(AIM_KEY_FO, 1))
+ {
+ msg = bbcodes_to_html(smsg);
+ mir_free(smsg);
+ }
+ else
+ msg = smsg;
+
+ bool blast = getBool(hContact, AIM_KEY_BLS, false);
+ int res = aim_send_message(hServerConn, seqno, sn, msg, false, blast);
+
+ mir_free(msg);
+ mir_free(sn);
+
+ if (!res || blast || 0 == getByte(AIM_KEY_DC, 1))
+ {
+ msg_ack_param *msg_ack = (msg_ack_param*)mir_alloc(sizeof(msg_ack_param));
+ msg_ack->hContact = hContact;
+ msg_ack->msg = NULL;
+ msg_ack->id = res;
+ msg_ack->success = res != 0;
+ ForkThread(&CAimProto::msg_ack_success, msg_ack);
+ }
+
+ return res;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SendUrl
+
+int __cdecl CAimProto::SendUrl(HANDLE hContact, int flags, const char* url)
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetApparentMode - sets the visibility m_iStatus
+
+int __cdecl CAimProto::SetApparentMode(HANDLE hContact, int mode)
+{
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetStatus - sets the protocol m_iStatus
+
+int __cdecl CAimProto::SetStatus(int iNewStatus)
+{
+ switch (iNewStatus)
+ {
+ case ID_STATUS_FREECHAT:
+ iNewStatus = ID_STATUS_ONLINE;
+ break;
+
+ case ID_STATUS_DND:
+ case ID_STATUS_OCCUPIED:
+ case ID_STATUS_ONTHEPHONE:
+#ifdef ALLOW_BUSY
+ iNewStatus = ID_STATUS_OCCUPIED;
+ break;
+#endif
+
+ case ID_STATUS_OUTTOLUNCH:
+ case ID_STATUS_NA:
+ iNewStatus = ID_STATUS_AWAY;
+ break;
+ }
+
+ if (iNewStatus == m_iStatus)
+ return 0;
+
+ if (iNewStatus == ID_STATUS_OFFLINE)
+ {
+ broadcast_status(ID_STATUS_OFFLINE);
+ return 0;
+ }
+
+ m_iDesiredStatus = iNewStatus;
+ if (m_iStatus == ID_STATUS_OFFLINE)
+ {
+ broadcast_status(ID_STATUS_CONNECTING);
+ ForkThread(&CAimProto::start_connection, (void*)iNewStatus);
+ }
+ else if (m_iStatus > ID_STATUS_OFFLINE)
+ {
+ switch(iNewStatus)
+ {
+ case ID_STATUS_ONLINE:
+ aim_set_status(hServerConn, seqno, AIM_STATUS_ONLINE);
+ broadcast_status(ID_STATUS_ONLINE);
+ break;
+
+ case ID_STATUS_INVISIBLE:
+ aim_set_status(hServerConn, seqno, AIM_STATUS_INVISIBLE);
+ broadcast_status(ID_STATUS_INVISIBLE);
+ break;
+
+ case ID_STATUS_OCCUPIED:
+ aim_set_status(hServerConn, seqno, AIM_STATUS_BUSY | AIM_STATUS_AWAY);
+ broadcast_status(ID_STATUS_OCCUPIED);
+ break;
+
+ case ID_STATUS_AWAY:
+ aim_set_status(hServerConn, seqno, AIM_STATUS_AWAY);
+ broadcast_status(ID_STATUS_AWAY);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// GetAwayMsg - returns a contact's away message
+
+void __cdecl CAimProto::get_online_msg_thread(void* arg)
+{
+ Sleep(150);
+
+ const HANDLE hContact = arg;
+ DBVARIANT dbv;
+ if (!DBGetContactSettingString(hContact, MOD_KEY_CL, OTH_KEY_SM, &dbv))
+ {
+ sendBroadcast(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, (LPARAM)dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else sendBroadcast(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, (LPARAM)0);
+}
+
+HANDLE __cdecl CAimProto::GetAwayMsg(HANDLE hContact)
+{
+ if (state != 1)
+ return 0;
+
+ int status = getWord(hContact, AIM_KEY_ST, ID_STATUS_OFFLINE);
+ switch (status)
+ {
+ case ID_STATUS_AWAY:
+ case ID_STATUS_ONLINE:
+ ForkThread(&CAimProto::get_online_msg_thread, hContact);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return (HANDLE)1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PSR_AWAYMSG
+
+int __cdecl CAimProto::RecvAwayMsg(HANDLE hContact, int statusMode, PROTORECVEVENT* pre)
+{
+ sendBroadcast(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, (LPARAM)pre->szMessage);
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// PSS_AWAYMSG
+
+int __cdecl CAimProto::SendAwayMsg(HANDLE hContact, HANDLE hProcess, const char* msg)
+{
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetAwayMsg - sets the away m_iStatus message
+
+int __cdecl CAimProto::SetAwayMsg(int status, const TCHAR* msg)
+{
+ char** msgptr = get_status_msg_loc(status);
+ if (msgptr == NULL) return 1;
+
+ char* nmsg = mir_utf8encodeT(msg);
+ mir_free(*msgptr); *msgptr = nmsg;
+
+ switch (status)
+ {
+ case ID_STATUS_FREECHAT:
+ status = ID_STATUS_ONLINE;
+ break;
+
+ case ID_STATUS_DND:
+ case ID_STATUS_OCCUPIED:
+ case ID_STATUS_ONTHEPHONE:
+#ifdef ALLOW_BUSY
+ status = ID_STATUS_OCCUPIED;
+ break;
+#endif
+
+ case ID_STATUS_OUTTOLUNCH:
+ case ID_STATUS_NA:
+ status = ID_STATUS_AWAY;
+ break;
+ }
+
+ if (state == 1 && status == m_iStatus)
+ {
+ if (!_strcmps(last_status_msg, nmsg))
+ return 0;
+
+ mir_free(last_status_msg);
+ last_status_msg = mir_strdup(nmsg);
+ aim_set_statusmsg(hServerConn, seqno, nmsg);
+ aim_set_away(hServerConn, seqno, nmsg,
+ status == ID_STATUS_AWAY || status == ID_STATUS_OCCUPIED);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// UserIsTyping - sends a UTN notification
+
+int __cdecl CAimProto::UserIsTyping(HANDLE hContact, int type)
+{
+ if (state != 1) return 0;
+
+ if (getWord(hContact, AIM_KEY_ST, ID_STATUS_OFFLINE) == ID_STATUS_ONTHEPHONE)
+ return 0;
+
+ DBVARIANT dbv;
+ if (!getBool(hContact, AIM_KEY_BLS, false) && !getString(hContact, AIM_KEY_SN, &dbv))
+ {
+ if (type == PROTOTYPE_SELFTYPING_ON)
+ aim_typing_notification(hServerConn, seqno, dbv.pszVal, 0x0002);
+ else if (type == PROTOTYPE_SELFTYPING_OFF)
+ aim_typing_notification(hServerConn, seqno, dbv.pszVal, 0x0000);
+ DBFreeVariant(&dbv);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// OnEvent - maintain protocol events
+
+int __cdecl CAimProto::OnEvent(PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam)
+{
+ switch (eventType)
+ {
+ case EV_PROTO_ONLOAD:
+ return OnModulesLoaded(0, 0);
+
+// case EV_PROTO_ONEXIT:
+// return OnPreShutdown(0, 0);
+
+ case EV_PROTO_ONMENU:
+ InitMainMenus();
+ break;
+
+ case EV_PROTO_ONOPTIONS:
+ return OnOptionsInit(wParam, lParam);
+
+ case EV_PROTO_ONERASE:
+ {
+ char szDbsettings[64];
+ mir_snprintf(szDbsettings, sizeof(szDbsettings), "%sP2P", m_szModuleName);
+ CallService(MS_DB_MODULE_DELETE, 0, (LPARAM)szDbsettings);
+ break;
+ }
+
+ case EV_PROTO_ONRENAME:
+ if (hMenuRoot)
+ {
+ CLISTMENUITEM clmi = { 0 };
+ clmi.cbSize = sizeof(CLISTMENUITEM);
+ clmi.flags = CMIM_NAME | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED;
+ clmi.ptszName = m_tszUserName;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuRoot, (LPARAM)&clmi);
+ }
+ break;
+
+ case EV_PROTO_ONCONTACTDELETED:
+ return OnContactDeleted(wParam, lParam);
+
+ case EV_PROTO_DBSETTINGSCHANGED:
+ return OnDbSettingChanged(wParam, lParam);
+ }
+ return 1;
+}