/* 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 <http://www.gnu.org/licenses/>. */ #include "aim.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; i<user_id_array_size; ++i) user_id_array[i] = _htons(group_list[i].item_id); } LOG("Modifying group %s:%u on the serverside list",group, group_id); aim_mod_group(hServerConn, seqno, group, group_id, (char*)user_id_array, user_id_array_size * sizeof(unsigned short)); mir_free(user_id_array); } void CAimProto::add_contact_to_group(HANDLE hContact, const char* new_group) { if (new_group == NULL) return; if (strcmp(new_group, "MetaContacts Hidden Group") == 0) return; unsigned short old_group_id = getGroupId(hContact, 1); char* old_group = group_list.find_name(old_group_id); if (old_group && strcmp(new_group, old_group) == 0) return; DBVARIANT dbv; char *nick = NULL; if (!DBGetContactSettingStringUtf(hContact, MOD_KEY_CL, "MyHandle", &dbv)) { nick = NEWSTR_ALLOCA(dbv.pszVal); DBFreeVariant(&dbv); } if (getString(hContact, AIM_KEY_SN, &dbv)) return; unsigned short item_id = getBuddyId(hContact, 1); unsigned short new_item_id = search_for_free_item_id(hContact); unsigned short new_group_id = group_list.find_id(new_group); if (!item_id) LOG("Contact %u not on list.", hContact); setGroupId(hContact, 1, new_group_id); if (new_group && strcmp(new_group, AIM_DEFAULT_GROUP)) DBWriteContactSettingStringUtf(hContact, MOD_KEY_CL, OTH_KEY_GP, new_group); else DBDeleteContactSetting(hContact, MOD_KEY_CL, OTH_KEY_GP); aim_ssi_update(hServerConn, seqno, true); if (new_group_id == 0) { create_group(new_group); LOG("Group %s not on list.", new_group); new_group_id = group_list.add(new_group); LOG("Adding group %s:%u to the serverside list", new_group, new_group_id); aim_add_contact(hServerConn, seqno, new_group, 0, new_group_id, 1);//add the group server-side even if it exist } LOG("Adding buddy %s:%u %s:%u to the serverside list", dbv.pszVal, new_item_id, new_group, new_group_id); aim_add_contact(hServerConn, seqno, dbv.pszVal, new_item_id, new_group_id, 0, nick); update_server_group(new_group, new_group_id); if (old_group_id && item_id) { bool is_not_in_list = getBool(hContact, AIM_KEY_NIL, false); LOG("Removing buddy %s:%u %s:%u from the serverside list", dbv.pszVal, item_id, old_group, old_group_id); aim_delete_contact(hServerConn, seqno, dbv.pszVal, item_id, old_group_id, 0, is_not_in_list); update_server_group(old_group, old_group_id); deleteSetting(hContact, AIM_KEY_NIL); } aim_ssi_update(hServerConn, seqno, false); DBFreeVariant(&dbv); } void CAimProto::offline_contact(HANDLE hContact, bool remove_settings) { if (remove_settings) { //We need some of this stuff if we are still online. for(int i=1;;++i) { if (deleteBuddyId(hContact, i)) break; deleteGroupId(hContact, i); } DBDeleteContactSetting(hContact, MOD_KEY_CL, OTH_KEY_SM); } setWord(hContact, AIM_KEY_ST, ID_STATUS_OFFLINE); } void CAimProto::offline_contacts(void) { HANDLE hContact = db_find_first(); while (hContact) { if (is_my_contact(hContact)) offline_contact(hContact,true); hContact = db_find_next(hContact); } allow_list.destroy(); block_list.destroy(); group_list.destroy(); } char *normalize_name(const char *s) { if (s == NULL) return NULL; char* buf = mir_strdup(s); _strlwr(buf); /* char *p = strchr(buf, ' '); if (p) { char *q = p; while (*p) { if (*p != ' ') *(q++) = *p; ++p; } *q = '\0'; } */ return buf; } char* trim_str(char* s) { if (s == NULL) return NULL; size_t len = strlen(s); while (len) { if (isspace(s[len-1])) --len; else break; } s[len]=0; char* sc = s; while (isspace(*sc)) ++sc; memcpy(s,sc,strlen(sc)+1); return s; } void create_group(const char *group) { if (strcmp(group, AIM_DEFAULT_GROUP) == 0) return; TCHAR* szGroupName = mir_utf8decodeT(group); CallService(MS_CLIST_GROUPCREATE, 0, (LPARAM)szGroupName); mir_free(szGroupName); } unsigned short CAimProto::search_for_free_item_id(HANDLE hbuddy)//returns a free item id and links the id to the buddy { unsigned short id; retry: id = get_random(); HANDLE hContact = db_find_first(); while (hContact) { if (is_my_contact(hContact)) { for(int i=1; ;++i) { unsigned short item_id = getBuddyId(hContact, i); if (item_id == 0) break; if (item_id == id) goto retry; //found one no need to look through anymore } } hContact = db_find_next(hContact); } setBuddyId(hbuddy, 1, id); return id; } //returns the size of the list array aquired with data unsigned short* CAimProto::get_members_of_group(unsigned short group_id, unsigned short &size) { unsigned short* list = NULL; size = 0; HANDLE hContact = db_find_first(); while (hContact) { if (is_my_contact(hContact)) { for(int i=1; ;++i) { unsigned short user_group_id = getGroupId(hContact, i); if (user_group_id == 0) break; if (group_id == user_group_id) { unsigned short buddy_id = getBuddyId(hContact, i); if (buddy_id) { list = (unsigned short*)mir_realloc(list, ++size*sizeof(list[0])); list[size-1] = _htons(buddy_id); } } } } hContact = db_find_next(hContact); } return list; } void CAimProto::upload_nicks(void) { HANDLE hContact = db_find_first(); while (hContact) { DBVARIANT dbv; if (is_my_contact(hContact) && !DBGetContactSettingStringUtf(hContact, MOD_KEY_CL, "MyHandle", &dbv)) { set_local_nick(hContact, dbv.pszVal, NULL); DBFreeVariant(&dbv); } hContact = db_find_next(hContact); } } void CAimProto::set_local_nick(HANDLE hContact, char* nick, char* note) { DBVARIANT dbv; if (getString(hContact, AIM_KEY_SN, &dbv)) return; for(int i=1; ;++i) { unsigned short group_id = getGroupId(hContact, i); if (group_id == 0) break; unsigned short buddy_id = getBuddyId(hContact, i); if (buddy_id == 0) break; aim_mod_buddy(hServerConn, seqno, dbv.pszVal, group_id, buddy_id, nick, note); } DBFreeVariant(&dbv); } ///////////////////////////////////////////////////////////////////////////////////////// unsigned short BdList::get_free_id(void) { unsigned short id; retry: id = get_random(); for (int i=0; i<count; ++i) if (items[i]->item_id == id) goto retry; return id; } unsigned short BdList::find_id(const char* name) { for (int i=0; i<count; ++i) { if (_stricmp(items[i]->name, name) == 0) return items[i]->item_id; } return 0; } char* BdList::find_name(unsigned short id) { for (int i=0; i<count; ++i) { if (items[i]->item_id == id) return items[i]->name; } return NULL; } void BdList::remove_by_id(unsigned short id) { for (int i=0; i<count; ++i) { if (items[i]->item_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, "<h3>", 4); _write(fid, sn, (unsigned)strlen(sn)); _write(fid, "'s Away Message:</h3>", 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, "<h3>", 4); _write(fid, sn, (unsigned)strlen(sn)); _write(fid, "'s Profile:</h3>", 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)); }