/*
Plugin of Miranda IM for communicating with users of the AIM protocol.
Copyright (c) 2008-2012 Boris Krasnovskiy
Copyright (C) 2005-2006 Aaron Myles Landwehr
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 .
*/
#include "aim.h"
#include "utility.h"
void CAimProto::broadcast_status(int status)
{
LOG("Broadcast Status: %d",status);
int old_status = m_iStatus;
m_iStatus = status;
if (m_iStatus == ID_STATUS_OFFLINE)
{
shutdown_file_transfers();
shutdown_chat_conn();
if (hServerConn)
{
aim_sendflap(hServerConn,0x04,0,NULL,seqno);
Netlib_Shutdown(hServerConn);
}
if (hMailConn && hMailConn != (HANDLE)1)
{
aim_sendflap(hMailConn,0x04,0,NULL,mail_seqno);
Netlib_Shutdown(hMailConn);
}
else if (hMailConn == (HANDLE)1)
hMailConn = NULL;
if (hAvatarConn && hAvatarConn != (HANDLE)1)
{
aim_sendflap(hAvatarConn,0x04,0,NULL,avatar_seqno);
Netlib_Shutdown(hAvatarConn);
}
else if (hAvatarConn == (HANDLE)1)
hAvatarConn = NULL;
if (hChatNavConn && hChatNavConn != (HANDLE)1)
{
aim_sendflap(hChatNavConn,0x04,0,NULL,chatnav_seqno);
Netlib_Shutdown(hChatNavConn);
}
else if (hChatNavConn == (HANDLE)1)
hChatNavConn = NULL;
idle = false;
instantidle = false;
list_received = false;
state = 0;
m_iDesiredStatus = ID_STATUS_OFFLINE;
mir_free(last_status_msg); last_status_msg = NULL;
avatar_id_lg = 0;
avatar_id_sm = 0;
mir_free(hash_lg); hash_lg = NULL;
mir_free(hash_sm); hash_sm = NULL;
pd_flags = 0;
pd_info_id = 0;
pd_mode = 0;
seqno = 0;
mail_seqno = 0;
avatar_seqno = 0;
chatnav_seqno = 0;
admin_seqno = 0;
}
sendBroadcast(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
}
void CAimProto::start_connection(void *arg)
{
int status = (int)arg;
if(m_iStatus<=ID_STATUS_OFFLINE)
{
offline_contacts();
DBVARIANT dbv;
if (!getString(AIM_KEY_SN, &dbv))
DBFreeVariant(&dbv);
else
{
ShowPopup(LPGEN("Please, enter a username in the options dialog."), 0);
broadcast_status(ID_STATUS_OFFLINE);
return;
}
if (!getString(AIM_KEY_PW, &dbv))
DBFreeVariant(&dbv);
else
{
ShowPopup(LPGEN("Please, enter a password in the options dialog."), 0);
broadcast_status(ID_STATUS_OFFLINE);
return;
}
bool use_ssl = !getByte(AIM_KEY_DSSL, 0);
char* login_url = getSetting(AIM_KEY_HN);
if (login_url == NULL) login_url = mir_strdup(use_ssl ? AIM_DEFAULT_SERVER : AIM_DEFAULT_SERVER_NS);
hServerConn = aim_connect(login_url, get_default_port(), use_ssl, login_url);
mir_free(login_url);
pref1_flags = 0x77ffff;
pref1_set_flags = 0x77ffff;
mir_free(pref2_flags); pref2_flags = NULL; pref2_len = 0;
mir_free(pref2_set_flags); pref2_set_flags = NULL; pref2_set_len = 0;
if (hServerConn)
aim_connection_authorization();
else
broadcast_status(ID_STATUS_OFFLINE);
}
}
bool CAimProto::wait_conn(HANDLE& hConn, HANDLE& hEvent, unsigned short service)
{
if (m_iStatus == ID_STATUS_OFFLINE)
return false;
EnterCriticalSection(&connMutex);
if (hConn == NULL && hServerConn)
{
LOG("Starting Connection.");
hConn = (HANDLE)1; //set so no additional service request attempts are made while aim is still processing the request
aim_new_service_request(hServerConn, seqno, service) ;//general service connection!
}
LeaveCriticalSection(&connMutex);
if (WaitForSingleObjectEx(hEvent, 10000, TRUE) != WAIT_OBJECT_0)
return false;
if (Miranda_Terminated() || m_iStatus == ID_STATUS_OFFLINE)
return false;
return true;
}
unsigned short CAimProto::get_default_port(void)
{
return getWord(AIM_KEY_PN, getByte(AIM_KEY_DSSL, 0) ? AIM_DEFAULT_PORT : AIM_DEFAULT_SSL_PORT);
}
bool CAimProto::is_my_contact(HANDLE hContact)
{
const char* szProto = GetContactProto(hContact);
return szProto != NULL && strcmp(m_szModuleName, szProto) == 0;
}
HANDLE CAimProto::find_chat_contact(const char* room)
{
HANDLE hContact = db_find_first();
while (hContact)
{
if (is_my_contact(hContact))
{
DBVARIANT dbv;
if (!getString(hContact, "ChatRoomID", &dbv))
{
bool found = !strcmp(room, dbv.pszVal);
DBFreeVariant(&dbv);
if (found) return hContact;
}
}
hContact = db_find_next(hContact);
}
return NULL;
}
HANDLE CAimProto::contact_from_sn(const char* sn, bool addIfNeeded, bool temporary)
{
char* norm_sn = normalize_name(sn);
HANDLE hContact = db_find_first();
while (hContact)
{
if (is_my_contact(hContact))
{
DBVARIANT dbv;
if (!getString(hContact, AIM_KEY_SN, &dbv))
{
bool found = !strcmp(norm_sn, dbv.pszVal);
DBFreeVariant(&dbv);
if (found)
{
mir_free(norm_sn);
return hContact;
}
}
}
hContact = db_find_next(hContact);
}
if (addIfNeeded)
{
hContact = (HANDLE)CallService(MS_DB_CONTACT_ADD, 0, 0);
if (hContact)
{
if (CallService(MS_PROTO_ADDTOCONTACT, (WPARAM) hContact, (LPARAM) m_szModuleName) == 0)
{
setString(hContact, AIM_KEY_SN, norm_sn);
setString(hContact, AIM_KEY_NK, sn);
LOG("Adding contact %s to client side list.",norm_sn);
if (temporary)
DBWriteContactSettingByte(hContact, "CList", "NotOnList", 1);
mir_free(norm_sn);
return hContact;
}
else
CallService(MS_DB_CONTACT_DELETE, (WPARAM) hContact, 0);
}
}
mir_free(norm_sn);
return NULL;
}
void CAimProto::update_server_group(const char* group, unsigned short group_id)
{
unsigned short user_id_array_size;
unsigned short* user_id_array;
if (group_id)
user_id_array = get_members_of_group(group_id, user_id_array_size);
else
{
user_id_array_size = (unsigned short)group_list.getCount();
user_id_array = (unsigned short*)mir_alloc(user_id_array_size * sizeof(unsigned short));
for (unsigned short i=0; iitem_id == id) goto retry;
return id;
}
unsigned short BdList::find_id(const char* name)
{
for (int i=0; iname, name) == 0)
return items[i]->item_id;
}
return 0;
}
char* BdList::find_name(unsigned short id)
{
for (int i=0; iitem_id == id)
return items[i]->name;
}
return NULL;
}
void BdList::remove_by_id(unsigned short id)
{
for (int i=0; iitem_id == id)
{
remove(i);
break;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
unsigned short CAimProto::getBuddyId(HANDLE hContact, int i)
{
char item[sizeof(AIM_KEY_BI)+10];
mir_snprintf(item, sizeof(AIM_KEY_BI)+10, AIM_KEY_BI"%d", i);
return getWord(hContact, item, 0);
}
void CAimProto::setBuddyId(HANDLE hContact, int i, unsigned short id)
{
char item[sizeof(AIM_KEY_BI)+10];
mir_snprintf(item, sizeof(AIM_KEY_BI)+10, AIM_KEY_BI"%d", i);
setWord(hContact, item, id);
}
int CAimProto::deleteBuddyId(HANDLE hContact, int i)
{
char item[sizeof(AIM_KEY_BI)+10];
mir_snprintf(item, sizeof(AIM_KEY_BI)+10, AIM_KEY_BI"%d", i);
return deleteSetting(hContact, item);
}
unsigned short CAimProto::getGroupId(HANDLE hContact, int i)
{
char item[sizeof(AIM_KEY_GI)+10];
mir_snprintf(item, sizeof(AIM_KEY_GI)+10, AIM_KEY_GI"%d", i);
return getWord(hContact, item, 0);
}
void CAimProto::setGroupId(HANDLE hContact, int i, unsigned short id)
{
char item[sizeof(AIM_KEY_GI)+10];
mir_snprintf(item, sizeof(AIM_KEY_GI)+10, AIM_KEY_GI"%d", i);
setWord(hContact, item, id);
}
int CAimProto::deleteGroupId(HANDLE hContact, int i)
{
char item[sizeof(AIM_KEY_GI)+10];
mir_snprintf(item, sizeof(AIM_KEY_GI)+10, AIM_KEY_GI"%d", i);
return deleteSetting(hContact, item);
}
/////////////////////////////////////////////////////////////////////////////////////////
int CAimProto::open_contact_file(const char* sn, const TCHAR* file, const char* mode, TCHAR* &path, bool contact_dir)
{
path = (TCHAR*)mir_alloc(MAX_PATH * sizeof(TCHAR));
TCHAR *tmpPath = Utils_ReplaceVarsT(_T("%miranda_userdata%"));
TCHAR *sztModuleName = mir_a2t(m_szModuleName);
int pos = mir_sntprintf(path, MAX_PATH, _T("%s\\%s"), tmpPath, sztModuleName);
mir_free(sztModuleName);
mir_free(tmpPath);
if (contact_dir)
{
char* norm_sn = normalize_name(sn);
TCHAR *norm_snt = mir_a2t(m_szModuleName);
pos += mir_sntprintf(path + pos, MAX_PATH - pos, _T("\\%s"), norm_snt);
mir_free(norm_snt);
mir_free(norm_sn);
}
if (_taccess(path, 0))
CallService(MS_UTILS_CREATEDIRTREET, 0, (LPARAM)path);
mir_sntprintf(path + pos, MAX_PATH - pos, _T("\\%s"), file);
int fid = _topen(path, _O_CREAT | _O_RDWR | _O_BINARY, _S_IREAD);
if (fid < 0)
{
TCHAR errmsg[512];
#if _MSC_VER > 1200
mir_sntprintf(errmsg, SIZEOF(errmsg), TranslateT("Failed to open file: %s %s"), path, __tcserror(NULL));
#else
TCHAR* err = mir_a2t(_strerror(NULL));
mir_sntprintf(errmsg, SIZEOF(errmsg), TranslateT("Failed to open file: %s %s"), path, err);
mir_free(err);
#endif
ShowPopup((char*)errmsg, ERROR_POPUP | TCHAR_POPUP);
}
return fid;
}
void CAimProto::write_away_message(const char* sn, const char* msg, bool utf)
{
TCHAR* path;
int fid = open_contact_file(sn, _T("away.html"), "wb", path, 1);
if (fid >= 0)
{
if (utf) _write(fid, "\xEF\xBB\xBF", 3);
char* s_msg=process_status_msg(msg, sn);
_write(fid, "", 4);
_write(fid, sn, (unsigned)strlen(sn));
_write(fid, "'s Away Message:
", 21);
_write(fid, s_msg, (unsigned)strlen(s_msg));
_close(fid);
ShellExecute(NULL, _T("open"), path, NULL, NULL, SW_SHOW);
mir_free(path);
mir_free(s_msg);
}
}
void CAimProto::write_profile(const char* sn, const char* msg, bool utf)
{
TCHAR* path;
int fid = open_contact_file(sn, _T("profile.html"),"wb", path, 1);
if (fid >= 0)
{
if (utf) _write(fid, "\xEF\xBB\xBF", 3);
char* s_msg=process_status_msg(msg, sn);
_write(fid, "", 4);
_write(fid, sn, (unsigned)strlen(sn));
_write(fid, "'s Profile:
", 16);
_write(fid, s_msg, (unsigned)strlen(s_msg));
_close(fid);
ShellExecute(NULL, _T("open"), path, NULL, NULL, SW_SHOW);
mir_free(path);
mir_free(s_msg);
}
}
unsigned long aim_oft_checksum_chunk(unsigned long dwChecksum, const unsigned char *buffer, int len)
{
unsigned long checksum = (dwChecksum >> 16) & 0xffff;
for (int i = 0; i < len; i++)
{
unsigned val = buffer[i];
if ((i & 1) == 0)
val <<= 8;
if (checksum < val) ++val;
checksum -= val;
}
checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
return checksum << 16;
}
unsigned int aim_oft_checksum_file(TCHAR *filename, unsigned __int64 size)
{
unsigned long checksum = 0xffff0000;
int fid = _topen(filename, _O_RDONLY | _O_BINARY, _S_IREAD);
if (fid >= 0)
{
unsigned __int64 sz = _filelengthi64(fid);
if (size > sz) size = sz;
while (size)
{
unsigned char buffer[8912];
int bytes = (int)min(size, sizeof(buffer));
bytes = _read(fid, buffer, bytes);
size -= bytes;
checksum = aim_oft_checksum_chunk(checksum, buffer, bytes);
}
_close(fid);
}
return checksum;
}
char* long_ip_to_char_ip(unsigned long host, char* ip)
{
host = _htonl(host);
unsigned char* bytes = (unsigned char*)&host;
size_t buf_loc = 0;
for(int i=0; i<4; i++)
{
char store[16];
_itoa(bytes[i], store, 10);
size_t len = strlen(store);
memcpy(&ip[buf_loc], store, len);
buf_loc += len;
ip[buf_loc++] = '.';
}
ip[buf_loc - 1] = '\0';
return ip;
}
unsigned long char_ip_to_long_ip(char* ip)
{
unsigned char chost[4] = {0};
char *c = ip;
for(int i=0; i<4; ++i)
{
chost[i] = (unsigned char)atoi(c);
c = strchr(c, '.');
if (c) ++c;
else break;
}
return *(unsigned long*)&chost;
}
unsigned short get_random(void)
{
unsigned short id;
CallService(MS_UTILS_GETRANDOM, sizeof(id), (LPARAM)&id);
id &= 0x7fff;
return id;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Standard functions
int CAimProto::deleteSetting(HANDLE hContact, const char* setting)
{ return DBDeleteContactSetting(hContact, m_szModuleName, setting);
}
bool CAimProto::getBool(HANDLE hContact, const char* name, bool defaultValue)
{ return DBGetContactSettingByte(hContact, m_szModuleName, name, defaultValue) != 0;
}
int CAimProto::getByte(const char* name, BYTE defaultValue)
{ return DBGetContactSettingByte(NULL, m_szModuleName, name, defaultValue);
}
int CAimProto::getByte(HANDLE hContact, const char* name, BYTE defaultValue)
{ return DBGetContactSettingByte(hContact, m_szModuleName, name, defaultValue);
}
int CAimProto::getDword(const char* name, DWORD defaultValue)
{ return DBGetContactSettingDword(NULL, m_szModuleName, name, defaultValue);
}
int CAimProto::getDword(HANDLE hContact, const char* name, DWORD defaultValue)
{ return DBGetContactSettingDword(hContact, m_szModuleName, name, defaultValue);
}
int CAimProto::getString(const char* name, DBVARIANT* result)
{ return DBGetContactSettingString(NULL, m_szModuleName, name, result);
}
int CAimProto::getString(HANDLE hContact, const char* name, DBVARIANT* result)
{ return DBGetContactSettingString(hContact, m_szModuleName, name, result);
}
int CAimProto::getTString(const char* name, DBVARIANT* result)
{ return DBGetContactSettingTString(NULL, m_szModuleName, name, result);
}
int CAimProto::getTString(HANDLE hContact, const char* name, DBVARIANT* result)
{ return DBGetContactSettingTString(hContact, m_szModuleName, name, result);
}
WORD CAimProto::getWord(const char* name, WORD defaultValue)
{ return (WORD)DBGetContactSettingWord(NULL, m_szModuleName, name, defaultValue);
}
WORD CAimProto::getWord(HANDLE hContact, const char* name, WORD defaultValue)
{ return (WORD)DBGetContactSettingWord(hContact, m_szModuleName, name, defaultValue);
}
char* CAimProto::getSetting(HANDLE hContact, const char* setting)
{
DBVARIANT dbv;
return DBGetContactSettingString(hContact, m_szModuleName, setting, &dbv) ?
NULL : dbv.pszVal;
}
char* CAimProto::getSetting(const char* setting)
{
DBVARIANT dbv;
return DBGetContactSettingString(NULL, m_szModuleName, setting, &dbv) ?
NULL : dbv.pszVal;
}
void CAimProto::setByte(const char* name, BYTE value)
{ DBWriteContactSettingByte(NULL, m_szModuleName, name, value);
}
void CAimProto::setByte(HANDLE hContact, const char* name, BYTE value)
{ DBWriteContactSettingByte(hContact, m_szModuleName, name, value);
}
void CAimProto::setDword(const char* name, DWORD value)
{ DBWriteContactSettingDword(NULL, m_szModuleName, name, value);
}
void CAimProto::setDword(HANDLE hContact, const char* name, DWORD value)
{ DBWriteContactSettingDword(hContact, m_szModuleName, name, value);
}
void CAimProto::setString(const char* name, const char* value)
{ DBWriteContactSettingString(NULL, m_szModuleName, name, value);
}
void CAimProto::setString(HANDLE hContact, const char* name, const char* value)
{ DBWriteContactSettingString(hContact, m_szModuleName, name, value);
}
void CAimProto::setTString(const char* name, const TCHAR* value)
{ DBWriteContactSettingTString(NULL, m_szModuleName, name, value);
}
void CAimProto::setTString(HANDLE hContact, const char* name, const TCHAR* value)
{ DBWriteContactSettingTString(hContact, m_szModuleName, name, value);
}
void CAimProto::setWord(const char* name, WORD value)
{ DBWriteContactSettingWord(NULL, m_szModuleName, name, value);
}
void CAimProto::setWord(HANDLE hContact, const char* name, WORD value)
{ DBWriteContactSettingWord(hContact, m_szModuleName, name, value);
}
int CAimProto::sendBroadcast(HANDLE hContact, int type, int result, HANDLE hProcess, LPARAM lParam)
{
return ProtoBroadcastAck(m_szModuleName, hContact, type, result, hProcess, lParam);
}
/////////////////////////////////////////////////////////////////////////////////////////
void CAimProto::CreateProtoService(const char* szService, AimServiceFunc serviceProc)
{
char temp[MAX_PATH*2];
mir_snprintf(temp, sizeof(temp), "%s%s", m_szModuleName, szService);
CreateServiceFunctionObj(temp, (MIRANDASERVICEOBJ)*(void**)&serviceProc, this);
}
void CAimProto::HookProtoEvent(const char* szEvent, AimEventFunc pFunc)
{
::HookEventObj(szEvent, (MIRANDAHOOKOBJ)*(void**)&pFunc, this);
}
void CAimProto::ForkThread(AimThreadFunc pFunc, void* param)
{
UINT threadID;
CloseHandle((HANDLE)mir_forkthreadowner((pThreadFuncOwner)*(void**)&pFunc, this, param, &threadID));
}