From 9814933f4bc5a7a4320819de54e313d8fc0ceffe Mon Sep 17 00:00:00 2001 From: sje Date: Wed, 3 Oct 2007 05:26:48 +0000 Subject: initial revision of new metacontacts git-svn-id: https://server.scottellis.com.au/svn/mim_plugs@338 4f64403b-2f21-0410-a795-97e2b3489a10 --- meta2/proto.cpp | 547 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 547 insertions(+) create mode 100644 meta2/proto.cpp (limited to 'meta2/proto.cpp') diff --git a/meta2/proto.cpp b/meta2/proto.cpp new file mode 100644 index 0000000..61ff69d --- /dev/null +++ b/meta2/proto.cpp @@ -0,0 +1,547 @@ +#include "common.h" +#include "proto.h" +#include "resource.h" +#include "core_functions.h" +#include "api.h" +#include "priorities.h" + +bool firstSetOnline = true; +DWORD status = ID_STATUS_OFFLINE; +DWORD setStatusTimerId = 0; + +DWORD next_meta_id = 1; +int meta_count = 0; + +// a 'fake' module name for copied subcontact events - used to prevent infinite recursion when creating event copies +#define META_COPY_MODULE "MetaCopy" + +HANDLE NewMetaContact() { + HANDLE hMeta = (HANDLE) CallService(MS_DB_CONTACT_ADD, 0, 0); + DBWriteContactSettingDword(hMeta, MODULE, META_ID, next_meta_id++); + CallService( MS_PROTO_ADDTOCONTACT, ( WPARAM )hMeta, ( LPARAM )MODULE); + meta_count++; + return hMeta; +} + + +int GetCaps(WPARAM wParam,LPARAM lParam) { + int ret = 0; + switch (wParam) { + case PFLAGNUM_1: + ret = PF1_NUMERICUSERID | PF1_IM | PF1_MODEMSGRECV | PF1_FILESEND; + break; + case PFLAGNUM_2: + ret = PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND + | PF2_HEAVYDND | PF2_FREECHAT | PF2_OUTTOLUNCH | PF2_ONTHEPHONE; + break; + case PFLAGNUM_3: + ret = PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND + | PF2_HEAVYDND | PF2_FREECHAT | PF2_OUTTOLUNCH | PF2_ONTHEPHONE; + break; + case PFLAGNUM_4: + ret = PF4_SUPPORTTYPING | PF4_AVATARS | PF4_SUPPORTIDLE | PF4_IMSENDUTF | PF4_IMSENDOFFLINE; + break; + case PFLAGNUM_5: + ret = PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND + | PF2_HEAVYDND | PF2_FREECHAT | PF2_OUTTOLUNCH | PF2_ONTHEPHONE; + break; + case PFLAG_UNIQUEIDTEXT: + ret = (int) Translate("Meta ID"); + break; + case PFLAG_MAXLENOFMESSAGE: + ret = 2048; + break; + case PFLAG_UNIQUEIDSETTING: + ret = (int) META_ID; + break; + } + return ret; +} + +int GetName(WPARAM wParam,LPARAM lParam) { + char *name = (char *)Translate(MODULE); + size_t size = min(strlen(name),wParam-1); // copy only the first size bytes. + if(strncpy((char *)lParam,name,size)==NULL) + return 1; + ((char *)lParam)[size]='\0'; + return 0; +} + +int LoadIcon(WPARAM wParam,LPARAM lParam) { + + UINT id; + switch (wParam & 0xFFFF) + { + case PLI_PROTOCOL: + id = IDI_MCMENU; + break; + case PLI_ONLINE: + id = IDI_MCMENU; + break; + case PLI_OFFLINE: + id = IDI_MCMENUOFF; + break; + default: + return (int) (HICON) NULL; + } + + return (int) LoadImage(hInst, MAKEINTRESOURCE(id), IMAGE_ICON, + GetSystemMetrics(wParam & PLIF_SMALL ? SM_CXSMICON : SM_CXICON), + GetSystemMetrics(wParam & PLIF_SMALL ? SM_CYSMICON : SM_CYICON), 0); + return 0; +} + +int ProtoGetInfo(WPARAM wParam,LPARAM lParam) { + CCSDATA *ccs = ( CCSDATA* )lParam; + + ccs->hContact = Meta_GetMostOnline(ccs->hContact); + char *proto = ContactProto(ccs->hContact); + if(!proto) + return 1; + + char szServiceName[256]; + mir_snprintf(szServiceName, 256, "%s%s", proto, PSS_GETINFO); + if (ServiceExists(szServiceName)) { + return CallContactService(ccs->hContact, PSS_GETINFO, ccs->wParam, ccs->lParam); + } + return 1; +} + +int ProtoGetAwayMsg(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = ( CCSDATA* )lParam; + + ccs->hContact = Meta_GetMostOnline(ccs->hContact); + char *proto = ContactProto(ccs->hContact); + if(!proto) + return 0; + + char szServiceName[256]; + mir_snprintf(szServiceName, 256, "%s%s", proto, PSS_GETAWAYMSG); + if (ServiceExists(szServiceName)) { + return CallContactService(ccs->hContact, PSS_GETAWAYMSG, ccs->wParam, ccs->lParam); + } + return 0; +} + +void CALLBACK SetStatusProc(HWND hWnd, UINT msg, UINT_PTR id, DWORD dw) +{ + int previousMode = status; + status = (int)ID_STATUS_ONLINE; + ProtoBroadcastAck(MODULE, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)previousMode, status); + + KillTimer(0, setStatusTimerId); +} + +int SetStatus(WPARAM wParam,LPARAM lParam) +{ + // firstSetOnline starts out true - used to delay metacontact's 'onlineness' to prevent double status notifications on startup + if(status == ID_STATUS_OFFLINE && firstSetOnline) { + // causes crash on exit if miranda is closed in under options.set_status_from_offline milliseconds! + //CloseHandle( CreateThread( NULL, 0, SetStatusThread, (void *)wParam, 0, 0 )); + setStatusTimerId = SetTimer(0, 0, 10000, SetStatusProc); + firstSetOnline = FALSE; + } else { + int previousMode = status; + status = (int)wParam; + ProtoBroadcastAck(MODULE, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)previousMode, status); + } + return 0; +} + +int GetStatus(WPARAM wParam,LPARAM lParam) { + return status; +} + +int ProtoSendMessage(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = (CCSDATA *) lParam; + char *message = (char *)ccs->lParam; + int flags = ccs->wParam; + char *buff = 0; + + HANDLE most_online = Meta_GetMostOnline(ccs->hContact); + char *proto = ContactProto(most_online); + if(ContactStatus(most_online, proto) == ID_STATUS_OFFLINE) { + most_online = Meta_GetMostOnlineSupporting(ccs->hContact, PFLAGNUM_4, PF4_IMSENDOFFLINE); + proto = ContactProto(most_online); + } + + char szServiceName[256]; + mir_snprintf(szServiceName, 256, "%s", PSS_MESSAGE); + if((flags & PREF_UNICODE) && proto != 0) { + char szTemp[256]; + mir_snprintf(szTemp, 256, "%s%sW", proto, PSS_MESSAGE); + if (ServiceExists(szTemp)) + strncpy(szServiceName, PSS_MESSAGE "W", sizeof(szServiceName)); + } + if((flags & PREF_UTF) && !(CallContactService(most_online, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_IMSENDUTF)) { + ccs->wParam &= ~PREF_UTF; + ccs->wParam |= PREF_UNICODE; + wchar_t *unicode = mir_utf8decodeW(message); + char *ascii = mir_u2a(unicode); + + char *buff = new char[strlen(ascii) + 1 + (wcslen(unicode) + 1) * sizeof(wchar_t)]; + strcpy(buff, ascii); + wcscpy((wchar_t *)(buff + strlen(ascii) + 1), unicode); + mir_free(unicode); + mir_free(ascii); + ccs->lParam = (LPARAM)buff; + } + + int ret = (int)CallContactService(most_online, szServiceName, ccs->wParam, ccs->lParam); + if(buff) { + ccs->lParam = (LPARAM)message; + delete[] buff; + } + return ret; +} + +int ProtoSendMessageW(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = (CCSDATA *) lParam; + if(!(ccs->wParam & PREF_UTF)) + ccs->wParam |= PREF_UNICODE; + + return ProtoSendMessage(wParam, lParam); +} + +int ProtoRecvMessage(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = (CCSDATA *) lParam; + PROTORECVEVENT *pre = (PROTORECVEVENT *) ccs->lParam; + + // use the subcontact's protocol to add to the db (AIMOSCAR removes HTML here!) + HANDLE most_online = Meta_GetMostOnline(ccs->hContact); + char *proto = ContactProto(most_online); + if(proto) { + char service[256]; + mir_snprintf(service, 256, "%s%s", proto, PSR_MESSAGE); + if(ServiceExists(service)) + return CallService(service, wParam, lParam); + } + + return 0; +} + +int RedirectACKs(WPARAM wParam, LPARAM lParam) +{ + ACKDATA *ack = (ACKDATA*) lParam; + HANDLE hMeta; + + if(ack->hContact == 0 || (hMeta = (HANDLE)DBGetContactSettingDword(ack->hContact, MODULE, "Handle", 0)) == 0) + return 0; // Can't find the MetaID, let through the protocol chain + + if(!strcmp(ack->szModule, MODULE)) { + return 0; // don't rebroadcast our own acks + } + + // if it's for something we don't support, ignore + if(ack->type != ACKTYPE_MESSAGE && ack->type != ACKTYPE_CHAT && ack->type != ACKTYPE_FILE && ack->type != ACKTYPE_AWAYMSG + && ack->type != ACKTYPE_AVATAR && ack->type != ACKTYPE_GETINFO) + { + return 0; + } + + // change the hContact in the avatar info struct, if it's the avatar we're using - else drop it + if(ack->type == ACKTYPE_AVATAR) { + if(ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED || ack->result == ACKRESULT_STATUS) { + if(ack->hContact == 0) { + return 0; + } + + if(ack->hProcess) { + PROTO_AVATAR_INFORMATION AI; + memcpy(&AI, (PROTO_AVATAR_INFORMATION *)ack->hProcess, sizeof(PROTO_AVATAR_INFORMATION)); + if(AI.hContact) + AI.hContact = hMeta; + + return ProtoBroadcastAck(MODULE,hMeta,ack->type,ack->result, (HANDLE)&AI, ack->lParam); + } else + return ProtoBroadcastAck(MODULE,hMeta,ack->type,ack->result, 0, ack->lParam); + } + } + + return ProtoBroadcastAck(MODULE, hMeta, ack->type, ack->result, ack->hProcess, ack->lParam); +} + +int ProtoGetAvatarInfo(WPARAM wParam, LPARAM lParam) { + PROTO_AVATAR_INFORMATION *AI = (PROTO_AVATAR_INFORMATION *) lParam; + + HANDLE hMeta = AI->hContact; + + // find the most online contact supporting avatars, according to priorities + HANDLE most_online = (HANDLE)Meta_GetMostOnlineSupporting(hMeta, PFLAGNUM_4, PF4_AVATARS); + char *most_online_proto = ContactProto(most_online); + + AI->hContact = most_online; + char szServiceName[100]; + mir_snprintf(szServiceName, sizeof(szServiceName), "%s%s", most_online_proto, PS_GETAVATARINFO); + int result = CallService(szServiceName, wParam, lParam); + AI->hContact = hMeta; + if (result != CALLSERVICE_NOTFOUND) return result; + + return GAIR_NOAVATAR; // fail +} + +int ProtoFileSend(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = (CCSDATA *) lParam; + HANDLE hMeta = ccs->hContact; + + // find the most online contact supporting file send, according to priorities + HANDLE most_online = (HANDLE)Meta_GetMostOnlineSupporting(hMeta, PFLAGNUM_1, PF1_FILESEND); + char *most_online_proto = ContactProto(most_online); + + ccs->hContact = most_online; + char szServiceName[100]; + mir_snprintf(szServiceName, sizeof(szServiceName), "%s%s", most_online_proto, PSS_FILE); + int result = CallService(szServiceName, wParam, lParam); + ccs->hContact = hMeta; + if(result != CALLSERVICE_NOTFOUND) return result; + + return 0; // fail +} + +int ProtoUserIsTyping(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta = (HANDLE)wParam; + // find the most online contact supporting file send, according to priorities + HANDLE most_online = (HANDLE)Meta_GetMostOnlineSupporting(hMeta, PFLAGNUM_4, PF4_SUPPORTTYPING); + char *most_online_proto = ContactProto(most_online); + + CallContactService(most_online, PSS_USERISTYPING, (WPARAM)most_online, lParam); + return 0; +} + +int EventContactIsTyping(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta; + + if((hMeta = (HANDLE)DBGetContactSettingDword((HANDLE)wParam, MODULE, "Handle", 0)) != 0 && MetaEnabled()) { + // try to remove any clist events added for subcontact + CallServiceSync(MS_CLIST_REMOVEEVENT, wParam, (LPARAM) 1); + + CallService(MS_PROTO_CONTACTISTYPING, (WPARAM)hMeta, lParam); + + // stop processing of event + return 1; + } + + return 0; +} + +int SendNudge(WPARAM wParam,LPARAM lParam) +{ + HANDLE hMeta = (HANDLE)wParam, + hSubContact = Meta_GetMostOnline(hMeta); + + char servicefunction[256]; + char *protoName = ContactProto(hSubContact); + mir_snprintf(servicefunction, 256, "%s/SendNudge", protoName); + + return CallService(servicefunction, (WPARAM)hSubContact, lParam); +} + +int ContactDeleted(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + if(IsSubcontact(hContact)) + Meta_Remove(hContact); + else if(IsMetacontact(hContact)) { + SubcontactList::Iterator i = metaMap[hContact].start(); + HANDLE hSub; + while(i.has_val()) { + // same functionality as Meta_Remove - except the meta is not deleted when all subcontacts are removed here since it's already happening, + // and the 'subcontacts changed' event isn't fired + hSub = i.val().handle(); + DBDeleteContactSetting(hSub, MODULE, "ParentMetaID"); + // deleting these (resident) settings doesn't work :( [25/9/07] + DBWriteContactSettingDword(hSub, MODULE, "Handle", 0); + DBWriteContactSettingByte(hSub, MODULE, "IsSubcontact", 0); + if(!meta_group_hack_disabled) DBWriteContactSettingByte(hSub, "CList", "Hidden", 0); + + i.next(); + } + metaMap.remove(hContact); + meta_count--; + } + return 0; +} + +int SettingChanged(WPARAM wParam, LPARAM lParam) { + if(wParam == 0 || !IsSubcontact((HANDLE)wParam)) return 0; + + DBCONTACTWRITESETTING *dcws = (DBCONTACTWRITESETTING *)lParam; + if(strcmp(dcws->szSetting, "Status") == 0) { + // subcontact status has changed + Meta_CalcStatus((HANDLE)DBGetContactSettingDword((HANDLE)wParam, MODULE, "Handle", 0)); + } + + // keep subcontacts hidden if the clist doesn't do it for us + if(strcmp(dcws->szSetting, "Hidden") == 0 && strcmp(dcws->szModule, "CList") == 0 && MetaEnabled() && !meta_group_hack_disabled) { + if(dcws->value.type == DBVT_DELETED || dcws->value.bVal == 0) { + DBWriteContactSettingByte((HANDLE)wParam, "CList", "Hidden", 1); + } + } + + return 0; +} + +int MetaChanged(WPARAM wParam, LPARAM lParam) { + Meta_CalcStatus((HANDLE)wParam); + return 0; +} + +int WindowEvent(WPARAM wParam, LPARAM lParam) { + MessageWindowEventData *mwed = (MessageWindowEventData *)lParam; + if(IsMetacontact(mwed->hContact) || IsSubcontact(mwed->hContact)) { + if(mwed->uType == MSG_WINDOW_EVT_OPEN || mwed->uType == MSG_WINDOW_EVT_OPENING) { + DBWriteContactSettingByte(mwed->hContact, MODULE, "WindowOpen", 1); + if(IsMetacontact(mwed->hContact)) + CallService(MS_CLIST_REMOVEEVENT, (WPARAM)mwed->hContact, (LPARAM)EVENTTYPE_MESSAGE); + } else if(mwed->uType == MSG_WINDOW_EVT_CLOSE || mwed->uType == MSG_WINDOW_EVT_CLOSING) + DBWriteContactSettingByte(mwed->hContact, MODULE, "WindowOpen", 0); + + } + return 0; +} + +void RegisterProto() { + PROTOCOLDESCRIPTOR pd = {0}; + pd.cbSize = sizeof(pd); + pd.szName = MODULE; + pd.type = PROTOTYPE_PROTOCOL; + CallService(MS_PROTO_REGISTERMODULE,0,(LPARAM)&pd); +} + +// redirect events to subcontact (except the ones we add in the EventAdded handler below - i.e. dbei->szModule == META_COPY_MODULE) +int EventFilterAdd(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + DBEVENTINFO *dbei = (DBEVENTINFO *)lParam; + + if(IsMetacontact(hContact) && strcmp(dbei->szModule, MODULE) == 0) { + // add the event to the subcontact instead + HANDLE most_online = Meta_GetMostOnline(hContact); + dbei->szModule = ContactProto(most_online); + CallService(MS_DB_EVENT_ADD, (WPARAM)most_online, (LPARAM)dbei); + return 1; // don't add original event + } + if(IsSubcontact(hContact)) { + if(dbei->eventType == EVENTTYPE_MESSAGE && !(dbei->flags & DBEF_SENT) && DBGetContactSettingByte(hContact, MODULE, "WindowOpen", 0) == 0) { + dbei->flags |= DBEF_READ; + } + } + + return 0; +} + +// add subcontact events to metacontacts +int EventAdded(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam, + hDbEvent = (HANDLE)lParam, + hMeta; + + if(MetaEnabled() && (hMeta = (HANDLE)DBGetContactSettingDword(hContact, MODULE, "Handle", 0)) != 0) { + DBEVENTINFO dbei = {0}; + dbei.cbSize = sizeof(dbei); + dbei.cbBlob = (int)CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hDbEvent, 0); + dbei.pBlob = (PBYTE)mir_alloc(dbei.cbBlob); + if(CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&dbei) == 0) { + if(dbei.eventType < EVENTTYPE_ADDED) { + DBEVENTINFO dbeiMeta = {0}; + dbeiMeta.cbSize = sizeof(dbeiMeta); + dbeiMeta.eventType = dbei.eventType; + dbeiMeta.flags = dbei.flags & ~DBEF_READ; + dbeiMeta.cbBlob = dbei.cbBlob; + dbeiMeta.pBlob = dbei.pBlob; + dbeiMeta.szModule = dbei.szModule; + dbeiMeta.timestamp = dbei.timestamp; + + if(dbei.eventType == EVENTTYPE_MESSAGE) { + if(!(dbeiMeta.flags & DBEF_SENT) + && DBGetContactSettingByte(hContact, MODULE, "WindowOpen", 0) == 1 + && DBGetContactSettingByte(hMeta, MODULE, "WindowOpen", 0) == 0) + { + dbeiMeta.flags |= DBEF_READ; + } + + // set default + int num = metaMap[hMeta].index_of(hContact); + if(num != -1 && num != DBGetContactSettingByte(hMeta, MODULE, "Default", -1)) + MetaAPI_SetDefaultContactNum((WPARAM)hMeta, (LPARAM)num); + + // set meta nick + char *proto = ContactProto(hContact); + DBVARIANT dbv; + if(proto && !DBGetContactSettingUTF8String(0, proto, "Nick", &dbv)) { + DBWriteContactSettingUTF8String(0, MODULE, "Nick", dbv.pszVal); + DBFreeVariant(&dbv); + } + } + CallService(MS_DB_EVENT_ADD, (WPARAM)hMeta, (LPARAM)&dbeiMeta); + } + } + mir_free(dbei.pBlob); + } + + return 0; +} + +HANDLE hEventMessageWindow = 0; +int ModulesLoadedProto(WPARAM wParam, LPARAM lParam) { + hEventMessageWindow = (HANDLE)HookEvent(ME_MSG_WINDOWEVENT, WindowEvent); + + Meta_Hide(DBGetContactSettingByte(0, MODULE, "Enabled", 1) == 0); + return 0; +} + +#define NUM_SERVICES 14 +HANDLE hServices[NUM_SERVICES] = {0}, hEventContactDeleted = 0, hEventSettingChanged = 0; +#define NUM_HOOKS_INTERNAL 8 +HANDLE hHooksInternal[NUM_HOOKS_INTERNAL] = {0}; + +void InitProto() { + RegisterProto(); + + // create our services + int i = 0; + + hServices[i++] = CreateProtoServiceFunction(MODULE, PS_GETCAPS, GetCaps); + hServices[i++] = CreateProtoServiceFunction(MODULE, PS_GETNAME, GetName); + hServices[i++] = CreateProtoServiceFunction(MODULE, PS_LOADICON, LoadIcon); + hServices[i++] = CreateProtoServiceFunction(MODULE, PS_SETSTATUS, SetStatus); + hServices[i++] = CreateProtoServiceFunction(MODULE, PS_GETSTATUS, GetStatus); + + hServices[i++] = CreateProtoServiceFunction(MODULE, PSS_GETINFO, ProtoGetInfo); + + hServices[i++] = CreateProtoServiceFunction(MODULE, PSS_MESSAGE, ProtoSendMessage); + hServices[i++] = CreateProtoServiceFunction(MODULE, PSS_MESSAGE"W", ProtoSendMessageW); + hServices[i++] = CreateProtoServiceFunction(MODULE, PSR_MESSAGE, ProtoRecvMessage); + + hServices[i++] = CreateProtoServiceFunction(MODULE, PSS_GETAWAYMSG, ProtoGetAwayMsg); + + hServices[i++] = CreateProtoServiceFunction(MODULE, PS_GETAVATARINFO, ProtoGetAvatarInfo); + hServices[i++] = CreateProtoServiceFunction(MODULE, PSS_FILE, ProtoFileSend); + hServices[i++] = CreateProtoServiceFunction(MODULE, PSS_USERISTYPING, ProtoUserIsTyping); + + // REMEMBER to modify the NUM_SERVICES #define above if you add more services! + + hServices[i++] = CreateProtoServiceFunction(MODULE, "/SendNudge", SendNudge); + + hEventContactDeleted = HookEvent(ME_DB_CONTACT_DELETED, ContactDeleted); + hEventSettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingChanged); + + i = 0; + hHooksInternal[i++] = (HANDLE)HookEvent(ME_MC_DEFAULTTCHANGED, MetaChanged ); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_MC_FORCESEND, MetaChanged ); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_MC_UNFORCESEND, MetaChanged ); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_PROTO_ACK, RedirectACKs); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoadedProto); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_DB_EVENT_ADDED, EventAdded); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_DB_EVENT_FILTER_ADD, EventFilterAdd); + hHooksInternal[i++] = (HANDLE)HookEvent(ME_PROTO_CONTACTISTYPING, EventContactIsTyping); +} + +void DeinitProto() { + UnhookEvent(hEventMessageWindow); + UnhookEvent(hEventSettingChanged); + UnhookEvent(hEventContactDeleted); + for(int i = 0; i < NUM_SERVICES; i++) + DestroyServiceFunction(hServices[i]); + for(int i = 0; i < NUM_HOOKS_INTERNAL; i++) + UnhookEvent(hHooksInternal[i]); + +} \ No newline at end of file -- cgit v1.2.3