From 200aa11bbd25189047b388b88b3fc1f50b785d7c Mon Sep 17 00:00:00 2001 From: sje Date: Wed, 1 Nov 2006 14:37:48 +0000 Subject: git-svn-id: https://server.scottellis.com.au/svn/mim_plugs@8 4f64403b-2f21-0410-a795-97e2b3489a10 --- metacontacts/meta_services.c | 2010 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2010 insertions(+) create mode 100644 metacontacts/meta_services.c (limited to 'metacontacts/meta_services.c') diff --git a/metacontacts/meta_services.c b/metacontacts/meta_services.c new file mode 100644 index 0000000..8bd17ad --- /dev/null +++ b/metacontacts/meta_services.c @@ -0,0 +1,2010 @@ +/* +MetaContacts Plugin for Miranda IM. + +Copyright © 2004 Universite Louis PASTEUR, STRASBOURG. + +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. +*/ + +/** @file meta_services.c +* +* Functions specific to the protocol part of the plugin. +* Centralizes all the functions called by Miranda to make +* the plugin work as a protocol. +*/ + +#include "metacontacts.h" + +#define NB_SERVICES 61 //!< Number of services registered in Miranda (see Meta_CloseHandles()). +#define NB_HOOKS 17 //!< Number of hooks set up (see Meta_CloseHandles()). + +#define PREF_METANODB 0x2000 //!< Flag to indicate message should not be added to db by filter when sending + +char *pendingACK = 0; //!< Name of the protocol in which an ACK is about to come. + +int previousMode, //!< Previous status of the MetaContacts Protocol + mcStatus; //!< Current status of the MetaContacts Protocol + +HANDLE hServices[NB_SERVICES] = {0}, //!< list of all the services registered (see Meta_CloseHandles()). +hHooks[NB_HOOKS] = {0}; //!< list of all hooks set up (see Meta_CloseHandles()). + +HANDLE hMenuConvert, //!< \c HANDLE to the convert menu item. + hMenuAdd, //!< \c HANDLE to the add to menu item. + hMenuEdit, //!< \c HANDLE to the edit menu item. + hMenuDelete, //!< \c HANDLE to the delete menu item. + hMenuDefault, //!< \c HANDLE to the delete menu item. + hMenuForceDefault; //!< \c HANDLE to the delete menu item. + +HANDLE hMenuOnOff; //!< \c HANDLE to the enable/disable menu item. + +HANDLE hEventDefaultChanged, //!< \c HANDLE to the 'default changed' event + hEventForceSend, //!< \c HANDLE to the 'force send' event + hEventUnforceSend, //!< \c HANDLE to the 'unforce send' event + hSubcontactsChanged; //!< \c HANDLE to the 'contacts changed' event + +struct MM_INTERFACE mmi; //!< Reference to the structure allowing memory management with Miranda. + +DWORD nextMetaID; //!< Global variable specifying the ID value the next MetaContact will have. + +BOOL message_window_api_enabled = FALSE; //!< Global variable specifying whether the message window api ver 0.0.0.1+ is available + +// stuff for mw_clist extra icon +int proto_count = 0; +HANDLE hExtraImage[MAX_PROTOCOLS * 2]; // online and offline icons +char proto_names[MAX_PROTOCOLS * 128]; +HANDLE hProtoIcons[MAX_PROTOCOLS * 2]; // online and offline icons + +/** Get the capabilities of the "MetaContacts" protocol. +* +* @param wParam : equals to one of the following values :\n + PFLAGNUM_1 | PFLAGNUM_2 | PFLAGNUM_3 | PFLAGNUM_4 | PFLAG_UNIQUEIDTEXT | PFLAG_MAXLENOFMESSAGE | PFLAG_UNIQUEIDSETTING . +* @param lParam : Allways set to 0. +* +* @return Depending on the \c WPARAM. +*/ +int Meta_GetCaps(WPARAM wParam,LPARAM lParam) +{ + int ret = 0; + switch (wParam) { + case PFLAGNUM_1: + //ret = PF1_IM | PF1_URL | PF1_FILE | PF1_MODEMSG | PF1_AUTHREQ | PF1_ADDED; + //ret = PF1_IMSEND | PF1_URLSEND | PF1_FILESEND | PF1_MODEMSGSEND; + ret = PF1_IM | PF1_CHAT | PF1_FILESEND | PF1_MODEMSGRECV | PF1_NUMERICUSERID; + break; + case PFLAGNUM_2: + if(!options.suppress_proto) { + ret = PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND + | PF2_HEAVYDND | PF2_FREECHAT | PF2_OUTTOLUNCH | PF2_ONTHEPHONE; + } + //ret = PF2_ONLINE; + break; + case PFLAGNUM_3: + //ret = PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND; + 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_FORCEAUTH; + ret = PF4_SUPPORTTYPING | PF4_AVATARS; + break; + case PFLAG_UNIQUEIDTEXT: + ret = (int) Translate("Meta ID"); + break; + case PFLAG_MAXLENOFMESSAGE: + ret = 2000; + break; + case PFLAG_UNIQUEIDSETTING: + ret = (int) META_ID; + break; + } + return ret; +} + +/** Copy the name of the protocole into lParam +* @param wParam : max size of the name +* @param lParam : reference to a char *, which will hold the name +*/ +int Meta_GetName(WPARAM wParam,LPARAM lParam) +{ + char *name = (char *)Translate(META_PROTO); + 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; +} + +/** Loads the icon corresponding to the status +* Called by the CList when the status changes. +* @param wParam : one of the following values : \n + PLI_PROTOCOL | PLI_ONLINE | PLI_OFFLINE +* @return an \c HICON in which the icon has been loaded. +*/ +int Meta_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_MCMENU; + break; + default: + return (int) (HICON) NULL; + } + + return (int) LoadImage(hInstance, MAKEINTRESOURCE(id), IMAGE_ICON, + GetSystemMetrics(wParam & PLIF_SMALL ? SM_CXSMICON : SM_CXICON), + GetSystemMetrics(wParam & PLIF_SMALL ? SM_CYSMICON : SM_CYICON), 0); + +} + + +static DWORD CALLBACK sttSetStatusThread( LPVOID param ) +{ + + previousMode = mcStatus; + + Sleep(options.set_status_from_offline_delay); + + mcStatus = (int)param; + ProtoBroadcastAck(META_PROTO,NULL,ACKTYPE_STATUS,ACKRESULT_SUCCESS, (HANDLE)previousMode, mcStatus); + + return 0; +} + +/** Changes the status and notifies everybody +* @param wParam : The new mode +* @param lParam : Allways set to 0. +*/ +int Meta_SetStatus(WPARAM wParam,LPARAM lParam) +{ + if(mcStatus == ID_STATUS_OFFLINE) { + DWORD dwThreadId; + CloseHandle( CreateThread( NULL, 0, sttSetStatusThread, (void *)wParam, 0, &dwThreadId )); + } else { + previousMode = mcStatus; + mcStatus = (int)wParam; + ProtoBroadcastAck(META_PROTO,NULL,ACKTYPE_STATUS,ACKRESULT_SUCCESS, (HANDLE)previousMode, mcStatus); + } + return 0; +} + +/** Returns the current status +*/ +int Meta_GetStatus(WPARAM wParam,LPARAM lParam) +{ + return mcStatus; +} + +////////////////////////////////////////////////////////// +/// Copied from MSN plugin - sent acks need to be from different thread :( +////////////////////////////////////////////////////////// +typedef struct tag_TFakeAckParams +{ + HANDLE hEvent; + HANDLE hContact; + LONG id; + char msg[512]; +} TFakeAckParams; + +/* +static DWORD CALLBACK sttFakeAckSuccess( LPVOID param ) +{ + TFakeAckParams *tParam = ( TFakeAckParams* )param; + WaitForSingleObject( tParam->hEvent, INFINITE ); + + Sleep( 100 ); + ProtoBroadcastAck(META_PROTO, tParam->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, ( HANDLE )tParam->id, 0 ); + + CloseHandle( tParam->hEvent ); + free(tParam); + return 0; +} +*/ + +static DWORD CALLBACK sttFakeAckFail( LPVOID param ) +{ + TFakeAckParams *tParam = ( TFakeAckParams* )param; + WaitForSingleObject( tParam->hEvent, INFINITE ); + + Sleep( 100 ); + ProtoBroadcastAck(META_PROTO, tParam->hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, ( HANDLE )tParam->id, (WPARAM)tParam->msg ); + + CloseHandle( tParam->hEvent ); + mir_free(tParam); + return 0; +} + +/** Filter messages sent by subcontacts +* +* When groups are disabled, add an event to the DB for the metacontact to maintain history +* +* @param wParam : index of the protocol in the protocol chain. +* @param lParam : \c CCSDATA structure holding all the information about the message. +* +* @return 0 on success, 1 otherwise. +*/ + +int MetaFilter_SendMessage(WPARAM wParam,LPARAM lParam) +{ + DBEVENTINFO dbei; + CCSDATA *ccs = (CCSDATA *) lParam; + HANDLE hMeta; + + if((hMeta = (HANDLE)DBGetContactSettingDword(ccs->hContact,META_PROTO, "Handle", (DWORD)0)) == (DWORD)0) { + return CallService(MS_PROTO_CHAINSEND, wParam, lParam); // Can't find the MetaID of the metacontact linked to + } + + // if subcontact sending, add db event to keep metacontact history correct + if(options.metahistory && !(ccs->wParam & PREF_METANODB)) { + + // reject "file As Message" messages + if(strlen((char *)ccs->lParam) > 5 && strncmp((char *)ccs->lParam, "<%fAM", 5) == 0) + return CallService(MS_PROTO_CHAINSEND, wParam, lParam); // continue processing + + // reject "data As Message" messages + if(strlen((char *)ccs->lParam) > 5 && strncmp((char *)ccs->lParam, "<%dAM", 5) == 0) + return CallService(MS_PROTO_CHAINSEND, wParam, lParam); // continue processing + + + ZeroMemory(&dbei, sizeof(dbei)); + dbei.cbSize = sizeof(dbei); + dbei.szModule = META_PROTO; + dbei.flags = DBEF_SENT; + dbei.timestamp = time(NULL); + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.cbBlob = strlen((char *)ccs->lParam) + 1; + if ( ccs->wParam & PREF_UNICODE ) + dbei.cbBlob *= ( sizeof( wchar_t )+1 ); + dbei.pBlob = (PBYTE)ccs->lParam; + + CallService(MS_DB_EVENT_ADD, (WPARAM) hMeta, (LPARAM)&dbei); + } + + return CallService(MS_PROTO_CHAINSEND, wParam, lParam); +} + +///////////////////////////////////////////////////////////////// + +/** Send a message to the protocol specific network. +* +* Call the function specific to the protocol that belongs +* to the contact chosen to send the message. +* +* @param wParam : index of the protocol in the protocol chain. +* @param lParam : \c CCSDATA structure holding all the information abour rhe message. +* +* @return 0 on success, 1 otherwise. +*/ +int Meta_SendMessage(WPARAM wParam,LPARAM lParam) +{ + DBEVENTINFO dbei; + CCSDATA *ccs = (CCSDATA *) lParam; + char *proto = 0; + DWORD default_contact_number; + + + if((default_contact_number = DBGetContactSettingDword(ccs->hContact,META_PROTO,"Default",(DWORD)-1)) == (DWORD)-1) + { + // This is a simple contact, let through the stack of protocols + // (this should normally not happen, since linked contacts do not appear on the list.) + return CallService(MS_PROTO_CHAINSEND, wParam, lParam); + } + else + { + char szServiceName[100]; + HANDLE most_online; + + most_online = Meta_GetMostOnline(ccs->hContact); + //DBEVENTINFO dbei; + + if(!most_online) { + DWORD dwThreadId; + HANDLE hEvent; + TFakeAckParams *tfap; + + // send failure to notify user of reason + hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + + tfap = (TFakeAckParams *)mir_alloc(sizeof(TFakeAckParams)); + tfap->hContact = ccs->hContact; + tfap->hEvent = hEvent; + tfap->id = 10; + strcpy(tfap->msg, Translate("No online contacts found.")); + + CloseHandle( CreateThread( NULL, 0, sttFakeAckFail, tfap, 0, &dwThreadId )); + SetEvent( hEvent ); + + return 10; + } + + Meta_CopyContactNick(ccs->hContact, most_online); + + ccs->hContact = most_online; + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online, 0); + Meta_SetNick(proto); // (no matter what was there before) + + // don't bypass filters etc + strncpy(szServiceName, PSS_MESSAGE, sizeof(szServiceName)); + + if(ccs->wParam & PREF_UNICODE) { + char szTemp[100]; + _snprintf(szTemp, sizeof(szTemp), "%s%sW", proto, PSS_MESSAGE); + if (ServiceExists(szTemp)) + strncpy(szServiceName, PSS_MESSAGE "W", sizeof(szServiceName)); + } + + if(options.subhistory) { + // add sent event to subcontact + ZeroMemory(&dbei, sizeof(dbei)); + dbei.cbSize = sizeof(dbei); + dbei.szModule = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0); + dbei.flags = DBEF_SENT; + dbei.timestamp = time(NULL); + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.cbBlob = strlen((char *)ccs->lParam) + 1; + if ( ccs->wParam & PREF_UNICODE ) + dbei.cbBlob *= ( sizeof( wchar_t )+1 ); + dbei.pBlob = (PBYTE)ccs->lParam; + + CallService(MS_DB_EVENT_ADD, (WPARAM) ccs->hContact, (LPARAM)&dbei); + } + + // prevent send filter from adding another copy of this send event to the db + ccs->wParam |= PREF_METANODB; + + return (int)(HANDLE)CallContactService(ccs->hContact, szServiceName, ccs->wParam, ccs->lParam); + } +} + +/** Transmit a message received by a contact. +* +* Forward the message received by a contact linked to a MetaContact +* to that MetaContact and inhibit the further reception of this message +* by the standard protocol of the contact. +* +* @param wParam : index of the protocol in the protocol chain. +* @param lParam : \c CCSDATA structure holding all the information about the message. +* +* @return 0 on success, 1 otherwise. +*/ +int MetaFilter_RecvMessage(WPARAM wParam,LPARAM lParam) +{ + DBEVENTINFO dbei; + CCSDATA *ccs = (CCSDATA *) lParam; + PROTORECVEVENT *pre = (PROTORECVEVENT *) ccs->lParam; + HANDLE hMeta; + + if((hMeta = (HANDLE)DBGetContactSettingDword(ccs->hContact,META_PROTO, "Handle", (DWORD)0)) == (DWORD)0) { + CallService(MS_PROTO_CHAINRECV, wParam, (LPARAM)ccs); // Can't find the MetaID of the metacontact linked to + // this contact, let through the protocol chain + return 0; + } + + if(options.set_default_on_recv) { + if(options.temp_default && DBGetContactSettingDword(hMeta, META_PROTO, "SavedDefault", (DWORD)-1) == (DWORD)-1) + DBWriteContactSettingDword(hMeta, META_PROTO, "SavedDefault", DBGetContactSettingDword(hMeta, META_PROTO, "Default", 0)); + DBWriteContactSettingDword(hMeta, META_PROTO, "Default", DBGetContactSettingDword(ccs->hContact, META_PROTO, "ContactNumber", 0)); + NotifyEventHooks(hEventDefaultChanged, (WPARAM)hMeta, (LPARAM)ccs->hContact); // nick set in event handler + } + + // if meta disabled (now message api) or window open (message api), or using subcontact windows, + // let through but add db event for metacontact history + if(!Meta_IsEnabled() + || DBGetContactSettingByte(ccs->hContact, META_PROTO, "WindowOpen", 0) == 1 + || options.subcontact_windows) + { + + // add a clist event, so that e.g. there is an icon flashing + // (only add it when message api available, 'cause then we can remove the event when the message window is opened) + if(message_window_api_enabled + && DBGetContactSettingByte(ccs->hContact, META_PROTO, "WindowOpen", 0) == 0 + && DBGetContactSettingByte(hMeta, META_PROTO, "WindowOpen", 0) == 0 + && options.flash_meta_message_icon) + { + CLISTEVENT cle; + char toolTip[256], *contactName; + ZeroMemory(&cle, sizeof(cle)); + + cle.cbSize = sizeof(cle); + cle.hContact = hMeta; + cle.hDbEvent = ccs->hContact; // use subcontact handle as key - then we can remove all events if the subcontact window is opened + cle.hIcon = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE); + cle.pszService = "MetaContacts/CListMessageEvent"; + contactName = (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hMeta, 0); + _snprintf(toolTip, sizeof(toolTip), Translate("Message from %s"), contactName); + cle.pszTooltip = toolTip; + CallService(MS_CLIST_ADDEVENT, 0, (LPARAM) & cle); + } + + if(options.metahistory) { + + BOOL added = FALSE; + + // should be able to do this, but some protos mess with the memory + if(options.use_proto_recv) + { + // use the subcontact's protocol 'recv' service to add the db (AIMOSCAR removes HTML here!) if possible + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0); + if(proto) { + char service[256]; + HANDLE hSub = ccs->hContact; + mir_snprintf(service, 256, "%s%s", proto, PSR_MESSAGE); + ccs->hContact = hMeta; + if(ServiceExists(service) && !CallService(service, 0, (LPARAM)ccs)) + added = TRUE; + ccs->hContact = hSub; + } + } + + if(!added) { + // otherwise add raw db event + ZeroMemory(&dbei, sizeof(dbei)); + dbei.cbSize = sizeof(dbei); + dbei.szModule = META_PROTO; + dbei.timestamp = pre->timestamp; + dbei.flags = (DBGetContactSettingByte(hMeta, META_PROTO, "WindowOpen", 0) ? 0 : DBEF_READ); + if(pre->flags & PREF_RTL) dbei.flags |= DBEF_RTL; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.cbBlob = strlen(pre->szMessage) + 1; + if ( pre->flags & PREF_UNICODE ) + dbei.cbBlob *= ( sizeof( wchar_t )+1 ); + dbei.pBlob = (PBYTE) pre->szMessage; + + CallService(MS_DB_EVENT_ADD, (WPARAM) hMeta, (LPARAM)&dbei); + } + } + + CallService(MS_PROTO_CHAINRECV, wParam, (LPARAM)ccs); + return 0; + } // else: + + /* + // add event to subcontact history (would do it in meta_recvmessage, but here we have the hcontact) + // should be able to use the method below, except some protos can mess with the memory + if(options.subhistory) { + ZeroMemory(&dbei, sizeof(dbei)); + dbei.cbSize = sizeof(dbei); + dbei.szModule = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0); + dbei.timestamp = pre->timestamp; + dbei.flags = (DBGetContactSettingByte(ccs->hContact, META_PROTO, "WindowOpen", 0) ? 0 : DBEF_READ); + if(pre->flags & PREF_RTL) dbei.flags |= DBEF_RTL; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.cbBlob = strlen(pre->szMessage) + 1; + if ( pre->flags & PREF_UNICODE ) + dbei.cbBlob *= ( sizeof( wchar_t )+1 ); + dbei.pBlob = (PBYTE) pre->szMessage; + + CallService(MS_DB_EVENT_ADD, (WPARAM) ccs->hContact, (LPARAM)&dbei); + } + */ + + + { + HANDLE hSub = ccs->hContact; + ccs->hContact = hMeta; // Forward to the associated MetaContact. + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)ccs); + ccs->hContact = hSub; + } + + if(options.subhistory) { + // allow event pass through and thereby be added to subcontact history + pre->flags |= (DBGetContactSettingByte(ccs->hContact, META_PROTO, "WindowOpen", 0) ? 0 : PREF_CREATEREAD); + return 0; + } + + return 1; // Stop further processing. +} + +/** Receive a message for a MetaContact +* +* @return 0 +*/ +int Meta_RecvMessage(WPARAM wParam, LPARAM lParam) +{ + DBEVENTINFO dbei; + CCSDATA *ccs = (CCSDATA *) lParam; + PROTORECVEVENT *pre = (PROTORECVEVENT *) ccs->lParam; + + char *proto; + + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0); + + // contact is not a meta proto contact - just leave it + if(!proto || strcmp(proto, META_PROTO)) { + return 0; + } + + if(options.use_proto_recv) + { + // use the subcontact's protocol to add the db if possible (AIMOSCAR removes HTML here!) + HANDLE most_online = Meta_GetMostOnline(ccs->hContact); + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online, 0); + if(proto) { + char service[256]; + mir_snprintf(service, 256, "%s%s", proto, PSR_MESSAGE); + if(ServiceExists(service) && !CallService(service, wParam, lParam)) + return 0; + } + } + + + // otherwise, add event to db directly + ZeroMemory(&dbei, sizeof(dbei)); + dbei.cbSize = sizeof(dbei); + dbei.szModule = META_PROTO; + dbei.timestamp = pre->timestamp; + dbei.flags = (pre->flags & PREF_CREATEREAD ? DBEF_READ : 0); + if(pre->flags & PREF_RTL) dbei.flags |= DBEF_RTL; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.cbBlob = strlen(pre->szMessage) + 1; + if ( pre->flags & PREF_UNICODE ) + dbei.cbBlob *= ( sizeof( wchar_t )+1 ); + dbei.pBlob = (PBYTE) pre->szMessage; + + CallService(MS_DB_EVENT_ADD, (WPARAM) ccs->hContact, (LPARAM)&dbei); + + return 0; +} + + + +/** Called when an ACK is received. +* +* Retransmit the ACK sent by a simple contact so that it +* looks like it was the MetaContact that sends the ACK. +* +* @param wParam : Allways set to 0. +* @param lParam : Reference to a ACKDATA that contains + information about the ACK. +* @return 0 on success, 1 otherwise. +*/ +int Meta_HandleACK(WPARAM wParam, LPARAM lParam) +{ + ACKDATA *ack = (ACKDATA*) lParam; + HANDLE hUser; + + if(ack->hContact == 0 || (hUser = (HANDLE)DBGetContactSettingDword(ack->hContact,META_PROTO,"Handle",0)) == 0) + return 0; // Can't find the MetaID, let through the protocol chain + + + if(!strcmp(ack->szModule, META_PROTO)) { + 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) { + HANDLE most_online; + DBVARIANT dbv; + + // change avatar if the most online supporting avatars changes, or if we don't have one + most_online = Meta_GetMostOnlineSupporting(hUser, PFLAGNUM_4, PF4_AVATARS); + //if(AI.hContact == 0 || AI.hContact != most_online) { + if(ack->hContact == 0 || ack->hContact != most_online) { + return 0; + } + + //if(!DBGetContactSetting(AI.hContact, "ContactPhoto", "File", &dbv)) { + if(!DBGetContactSetting(ack->hContact, "ContactPhoto", "File", &dbv)) { + DBWriteContactSettingString(hUser, "ContactPhoto", "File", dbv.pszVal); + DBFreeVariant(&dbv); + } + + if(ack->hProcess) { + PROTO_AVATAR_INFORMATION AI; + memcpy(&AI, (PROTO_AVATAR_INFORMATION *)ack->hProcess, sizeof(PROTO_AVATAR_INFORMATION)); + if(AI.hContact) + AI.hContact = hUser; + + return ProtoBroadcastAck(META_PROTO,hUser,ack->type,ack->result, (HANDLE)&AI, ack->lParam); + } else + return ProtoBroadcastAck(META_PROTO,hUser,ack->type,ack->result, 0, ack->lParam); + } + } + + return ProtoBroadcastAck(META_PROTO,hUser,ack->type,ack->result,ack->hProcess,ack->lParam); +} + +// hiding contacts on "CList/UseGroups" setting changed can cause a crash - do it in a seperate thread during idle time +static DWORD CALLBACK sttHideContacts( LPVOID param ) +{ + Meta_HideMetaContacts((int)param); + return 0; +} + +/** Call whenever a contact changes one of its settings (for example, the status) +** +* @param wParam \c HANDLE to the contact that has change of its setting. +* @param lParam Reference to a structure that contains the setting that has changed (not used) +*/ +int Meta_SettingChanged(WPARAM wParam, LPARAM lParam) +{ + DBCONTACTWRITESETTING *dcws = (DBCONTACTWRITESETTING *)lParam; + char buffer[512], buffer2[512]; + int contact_number; + HANDLE hMeta, most_online; + + + // hide metacontacts when groups disabled + if(wParam == 0 + && ((strcmp(dcws->szModule, "CList") == 0 && strcmp(dcws->szSetting, "UseGroups") == 0) + || (strcmp(dcws->szModule, META_PROTO) == 0 && strcmp(dcws->szSetting, "Enabled") == 0))) + { + QueueUserAPC((PAPCFUNC)sttHideContacts, metaMainThread, (ULONG)(!Meta_IsEnabled())); + return 0; + } + + if(wParam == 0 + && strcmp(dcws->szModule, "Import") == 0 && strcmp(dcws->szSetting, "Completed") == 0) + { + // import process has just been run...call startup routines... + Meta_SetHandles(); + { + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + int meta_id; + while ( hContact != NULL ) { + if((meta_id = DBGetContactSettingDword(hContact,META_PROTO,META_ID,(DWORD)-1))!=(DWORD)-1) { + Meta_CopyData(hContact); + } + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + } + + Meta_HideLinkedContacts(); + Meta_SuppressStatus(options.suppress_status); + } + + if(wParam == 0 + && strcmp(dcws->szModule, "CListGroups") == 0 && dcws->value.type != DBVT_DELETED && strcmp(dcws->value.pszVal, META_HIDDEN_GROUP) == 0) + { + // someone is creating our hidden group!! + + } + + // from here on, we're just interested in contact settings + if(wParam == 0) return 0; + + if((hMeta=(HANDLE)DBGetContactSettingDword((HANDLE)wParam,META_PROTO,"Handle",0))!=0 + && CallService(MS_DB_CONTACT_IS, (WPARAM)hMeta, 0)) // just to be safe + + { // This contact is attached to a MetaContact. + + contact_number = Meta_GetContactNumber((HANDLE)wParam); + if(contact_number == -1) return 0; // exit - db corruption + + if(!meta_group_hack_disabled && !strcmp(dcws->szModule, "CList") && !strcmp(dcws->szSetting, "Group") && DBGetContactSettingByte((HANDLE)wParam, META_PROTO, "Hidden", 0) == 0) { + if((dcws->value.type == DBVT_ASCIIZ || dcws->value.type == DBVT_UTF8) && !Meta_IsHiddenGroup(dcws->value.pszVal)) { + // subcontact group reassigned - copy to saved group + MyDBWriteContactSetting((HANDLE)wParam, META_PROTO, "OldCListGroup", &dcws->value); + DBWriteContactSettingString((HANDLE)wParam, "CList", "Group", META_HIDDEN_GROUP); + } else if(dcws->value.type == DBVT_DELETED) { + DBDeleteContactSetting((HANDLE)wParam, META_PROTO, "OldCListGroup"); + DBWriteContactSettingString((HANDLE)wParam, "CList", "Group", META_HIDDEN_GROUP); + } + } else + + // copy IP + if(!strcmp(dcws->szSetting, "IP")) { + if(dcws->value.type == DBVT_DWORD) + DBWriteContactSettingDword(hMeta, META_PROTO, "IP", dcws->value.dVal); + else + DBDeleteContactSetting(hMeta, META_PROTO, "IP"); + } else + + // copy RealIP + if(!strcmp(dcws->szSetting, "RealIP")) { + if(dcws->value.type == DBVT_DWORD) + DBWriteContactSettingDword(hMeta, META_PROTO, "RealIP", dcws->value.dVal); + else + DBDeleteContactSetting(hMeta, META_PROTO, "RealIP"); + + } else + // copy ListeningTo + if(!strcmp(dcws->szSetting, "ListeningTo")) { + switch(dcws->value.type) { + case DBVT_ASCIIZ: + DBWriteContactSettingString(hMeta, META_PROTO, "ListeningTo", dcws->value.pszVal); + break; + case DBVT_UTF8: + DBWriteContactSettingStringUtf(hMeta, META_PROTO, "ListeningTo", dcws->value.pszVal); + break; + case DBVT_WCHAR: + DBWriteContactSettingWString(hMeta, META_PROTO, "ListeningTo", dcws->value.pwszVal); + break; + case DBVT_DELETED: + DBDeleteContactSetting(hMeta, META_PROTO, "ListeningTo"); + break; + } + } else + + if(!strcmp(dcws->szSetting, "Nick") && !dcws->value.type == DBVT_DELETED) { + DBVARIANT dbv; + HANDLE most_online; + + // subcontact nick has changed - update metacontact + strcpy(buffer, "Nick"); + strcat(buffer, _itoa(contact_number, buffer2, 10)); + MyDBWriteContactSetting(hMeta, META_PROTO, buffer, &dcws->value); + + if(MyDBGetContactSetting((HANDLE)wParam, "CList", "MyHandle", &dbv)) { + strcpy(buffer, "CListName"); + strcat(buffer, _itoa(contact_number, buffer2, 10)); + MyDBWriteContactSetting(hMeta, META_PROTO, buffer, &dcws->value); + } else { + DBFreeVariant(&dbv); + } + + // copy nick to metacontact, if it's the most online + most_online = Meta_GetMostOnline(hMeta); + Meta_CopyContactNick(hMeta, most_online); + + return 0; + } else + + if(!strcmp(dcws->szSetting, "IdleTS")) { + if(dcws->value.type == DBVT_DWORD) + DBWriteContactSettingDword(hMeta, META_PROTO, "IdleTS", dcws->value.dVal); + else if(dcws->value.type == DBVT_DELETED) + DBWriteContactSettingDword(hMeta, META_PROTO, "IdleTS", 0); + } else + + if(!strcmp(dcws->szSetting, "LogonTS")) { + if(dcws->value.type == DBVT_DWORD) + DBWriteContactSettingDword(hMeta, META_PROTO, "LogonTS", dcws->value.dVal); + else if(dcws->value.type == DBVT_DELETED) + DBWriteContactSettingDword(hMeta, META_PROTO, "LogonTS", 0); + } else + + if(!strcmp(dcws->szModule, "CList") && !strcmp(dcws->szSetting, "MyHandle")) { + HANDLE most_online; + + if(dcws->value.type == DBVT_DELETED) { + DBVARIANT dbv; + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0); + strcpy(buffer, "CListName"); + strcat(buffer, _itoa(contact_number, buffer2, 10)); + if(proto && !MyDBGetContactSetting((HANDLE)wParam, proto, "Nick", &dbv)) { + MyDBWriteContactSetting(hMeta, META_PROTO, buffer, &dbv); + DBFreeVariant(&dbv); + } else { + DBDeleteContactSetting(hMeta, META_PROTO, buffer); + } + } else { + // subcontact clist displayname has changed - update metacontact + strcpy(buffer, "CListName"); + strcat(buffer, _itoa(contact_number, buffer2, 10)); + + DBWriteContactSettingString(hMeta, META_PROTO, buffer, dcws->value.pszVal); + } + + // copy nick to metacontact, if it's the most online + most_online = Meta_GetMostOnline(hMeta); + Meta_CopyContactNick(hMeta, most_online); + + return 0; + } else + + if(!strcmp(dcws->szSetting, "Status") && !dcws->value.type == DBVT_DELETED) { + // subcontact changing status + + // update subcontact status setting + strcpy(buffer, "Status"); + strcat(buffer, _itoa(contact_number, buffer2, 10)); + DBWriteContactSettingWord(hMeta, META_PROTO, buffer, dcws->value.wVal); + strcpy(buffer, "StatusString"); + strcat(buffer, _itoa(contact_number, buffer2, 10)); + Meta_GetStatusString(dcws->value.wVal, buffer2, 512); + DBWriteContactSettingString(hMeta, META_PROTO, buffer, buffer2); + + // if the contact was forced, unforce it (which updates status) + if((HANDLE)DBGetContactSettingDword(hMeta, META_PROTO, "ForceSend", 0) == (HANDLE)wParam) { + MetaAPI_UnforceSendContact((WPARAM)hMeta, 0); + } else { + // set status to that of most online contact + most_online = Meta_GetMostOnline(hMeta); + Meta_CopyContactNick(hMeta, most_online); + + Meta_FixStatus(hMeta); + + Meta_CopyData(hMeta); + } + + // most online contact with avatar support might have changed - update avatar + most_online = Meta_GetMostOnlineSupporting(hMeta, PFLAGNUM_4, PF4_AVATARS); + if(most_online) { + PROTO_AVATAR_INFORMATION AI; + + AI.cbSize = sizeof(AI); + AI.hContact = hMeta; + AI.format = PA_FORMAT_UNKNOWN; + strcpy(AI.filename, "X"); + + if((int)CallProtoService(META_PROTO, PS_GETAVATARINFO, 0, (LPARAM)&AI) == GAIR_SUCCESS) + DBWriteContactSettingString(hMeta, "ContactPhoto", "File",AI.filename); + } + } else + + if(strcmp(dcws->szSetting, "XStatusId") == 0 || strcmp(dcws->szSetting, "XStatusMsg") == 0 || strcmp(dcws->szSetting, "XStatusName") == 0 || strcmp(dcws->szSetting, "StatusMsg") == 0) { + Meta_CopyData(hMeta); + } else + + if(strcmp(dcws->szSetting, "MirVer") == 0) { + Meta_CopyData(hMeta); + } else + + if(!meta_group_hack_disabled && !strcmp(dcws->szModule, "CList") && !strcmp(dcws->szSetting, "Hidden")) { + if((dcws->value.type == DBVT_DELETED || DBGetContactSettingByte((HANDLE)wParam, "CList", "Hidden", 0) == 0) + && DBGetContactSettingByte((HANDLE)wParam, META_PROTO, "Hidden", 0) == 1) + { + // a subcontact we hid (e.g. jabber) has been unhidden - hide it again :( + DBWriteContactSettingByte((HANDLE)wParam, "CList", "Hidden", 1); + } + } + } + + return 0; +} + +int Meta_ContactDeleted(WPARAM wParam, LPARAM lParam) { + HANDLE hMeta; + + // is a subcontact - update meta contact + hMeta = (HANDLE)DBGetContactSettingDword((HANDLE)wParam, META_PROTO, "Handle", 0); + if(hMeta) { + Meta_RemoveContactNumber(hMeta, DBGetContactSettingDword((HANDLE)wParam, META_PROTO, "ContactNumber", -1)); + NotifyEventHooks(hSubcontactsChanged, (WPARAM)hMeta, 0); + return 0; + } else { + // not a subcontact - is it a metacontact? + int num_contacts = DBGetContactSettingDword((HANDLE)wParam, META_PROTO, "NumContacts", 0); + int i; + HANDLE hContact; + + if(num_contacts) NotifyEventHooks(hSubcontactsChanged, (WPARAM)wParam, 0); + + // remove & restore all subcontacts + for(i = 0; i < num_contacts; i++) { + hContact = Meta_GetContactHandle((HANDLE)wParam, i); + + if(hContact && (HANDLE)DBGetContactSettingDword(hContact, META_PROTO, "Handle", 0) == (HANDLE)wParam) { + if(DBGetContactSettingByte(hContact, META_PROTO, "IsSubcontact", 0) == 1) + DBDeleteContactSetting(hContact,META_PROTO,"IsSubcontact"); + DBDeleteContactSetting(hContact,META_PROTO,META_LINK); + DBDeleteContactSetting(hContact,META_PROTO,"Handle"); + DBDeleteContactSetting(hContact,META_PROTO,"ContactNumber"); + Meta_RestoreGroup(hContact); + DBDeleteContactSetting(hContact,META_PROTO,"OldCListGroup"); + + CallService(MS_PROTO_REMOVEFROMCONTACT, (WPARAM)hContact, (LPARAM)META_FILTER); + // stop ignoring, if we were + if(options.suppress_status) + CallService(MS_IGNORE_UNIGNORE, (WPARAM)hContact, (WPARAM)IGNOREEVENT_USERONLINE); + } + } + return 0; + } + + + return 0; +} + +/** Call when we want to send a user is typing message +* +* @param wParam \c HANDLE to the contact that we are typing to +* @param lParam either PROTOTYPE_SELFTYPING_ON or PROTOTYPE_SELFTYPING_OFF +*/ +int Meta_UserIsTyping(WPARAM wParam, LPARAM lParam) +{ + char *proto; + char buff[512]; + + if(DBGetContactSettingDword((HANDLE)wParam,META_PROTO,META_ID,(DWORD)-1) == (DWORD)-1) + { + // This is a simple contact, let through the stack of protocols + return 0; + } + else + { + // forward to sending protocol, if supported + + HANDLE most_online = Meta_GetMostOnline((HANDLE)wParam); + Meta_CopyContactNick((HANDLE)wParam, most_online); + + if(!most_online) return 0; + + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online, 0); + if(proto) { + strncpy(buff, proto, 512); + strncpy(buff + strlen(proto), PSS_USERISTYPING, 512 - strlen(proto)); + + if(ServiceExists(buff)) { + CallService(buff, (WPARAM)most_online, (LPARAM)lParam); + } + } + } + + return 0; +} + +/** Call when we want to receive a user is typing message +* +* @param wParam \c HANDLE to the contact that is typing or not +* @param lParam either PROTOTYPE_SELFTYPING_ON or PROTOTYPE_SELFTYPING_OFF +*/ +int Meta_ContactIsTyping(WPARAM wParam, LPARAM lParam) +{ + HANDLE hMeta; + + if((hMeta = (HANDLE)DBGetContactSettingDword((HANDLE)wParam,META_PROTO,"Handle",(DWORD)0)) != 0 + // check metacontacts enabled + && Meta_IsEnabled() + ) + { // This contact is attached to a MetaContact. + if(!options.subcontact_windows) { // we don't want clicking on the clist notification icon to open the metacontact message window + + // try to remove any clist events we 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; +} + +/** Called when user info is about to be shown +* +* Returns 1 to stop event processing and opens page for metacontact default contact (returning 1 to stop it doesn't work!) +* +*/ +int Meta_UserInfo(WPARAM wParam, LPARAM lParam) +{ + DWORD default_contact_number = DBGetContactSettingDword((HANDLE)lParam, META_PROTO, "Default", (DWORD)-1); + + if(default_contact_number == -1) // not a meta contact + return 0; + + CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)Meta_GetContactHandle((HANDLE)lParam, default_contact_number), 0); + + return 1; +} + +// handle message window api ver 0.0.0.1+ events - record window open/close status for subcontacts, so we know whether to +// let received messages through and add db history to metacontact, or vice versa +int Meta_MessageWindowEvent(WPARAM wParam, LPARAM lParam) { + MessageWindowEventData *mwed = (MessageWindowEventData *)lParam; + HANDLE hMeta = 0; + + message_window_api_enabled = TRUE; + + if((hMeta = (HANDLE)DBGetContactSettingDword(mwed->hContact, META_PROTO, "Handle", 0)) != 0 + || DBGetContactSettingDword(mwed->hContact, META_PROTO, META_ID, (DWORD)-1) != (DWORD)-1) + { + // contact is subcontact of metacontact, or an actual metacontact - record whether window is open or closed + if(mwed->uType == MSG_WINDOW_EVT_OPEN || mwed->uType == MSG_WINDOW_EVT_OPENING) { + DBWriteContactSettingByte(mwed->hContact, META_PROTO, "WindowOpen", 1); + + if(hMeta) { // subcontact window opened - remove clist events we added for metacontact + while(!CallService(MS_CLIST_REMOVEEVENT, (WPARAM)hMeta, (LPARAM)mwed->hContact)); + } + } else if(mwed->uType == MSG_WINDOW_EVT_CLOSE || mwed->uType == MSG_WINDOW_EVT_CLOSING) { + DBWriteContactSettingByte(mwed->hContact, META_PROTO, "WindowOpen", 0); + if(!hMeta) { // hMeta is 0 for metacontact (sorry) + DWORD saved_def; + + MetaAPI_UnforceSendContact((WPARAM)mwed->hContact, 0); + + // restore saved default contact + saved_def = DBGetContactSettingDword(mwed->hContact, META_PROTO, "SavedDefault", -1); + if(options.temp_default && saved_def != -1) { + DBWriteContactSettingDword(mwed->hContact, META_PROTO, "Default", saved_def); + DBWriteContactSettingDword(mwed->hContact, META_PROTO, "SavedDefault", (DWORD)-1); + NotifyEventHooks(hEventDefaultChanged, (WPARAM)mwed->hContact, (LPARAM)Meta_GetContactHandle(hMeta, saved_def)); // nick set in event handler + } + } + } + + } + + return 0; +} +/* +int Meta_LoadIcons(WPARAM wParam, LPARAM lParam) { + PROTOCOLDESCRIPTOR **protos; + + //MessageBox(0, "LoadIcons", "Event", MB_OK); + + if(ServiceExists(MS_CLIST_EXTRA_ADD_ICON)) { + int index = 0, i; + + CallService(MS_PROTO_ENUMPROTOCOLS,(WPARAM)&proto_count,(LPARAM)&protos); + for(i = 0; i < proto_count && i < MAX_PROTOCOLS; i++) { + if(protos[i]->type!=PROTOTYPE_PROTOCOL || CallProtoService(protos[i]->szName,PS_GETCAPS,PFLAGNUM_2,0)==0) + continue; + + strncpy(proto_names + (index * 128), protos[i]->szName, 128); + hProtoIcons[index * 2] = LoadSkinnedProtoIcon(protos[i]->szName,ID_STATUS_ONLINE); + hProtoIcons[index * 2 + 1] = LoadSkinnedProtoIcon(protos[i]->szName,ID_STATUS_OFFLINE); + hExtraImage[index * 2] = 0; + hExtraImage[index * 2 + 1] = 0; + + //sprintf(buff, "Added icon (hIcon = %d, hImage = %d) for protocol %s.", hProtoIcons[index], hExtraImage[index], protos[i]->szName); + //MessageBox(0, buff, "Added Extra Icon", MB_OK); + + index++; + } + proto_count = index; + + //Meta_CListMW_ExtraIconsRebuild(0, 0); + + } + + + return 0; +} + +int Meta_CListMW_ExtraIconsRebuild(WPARAM wParam, LPARAM lParam) { + int i; + + //MessageBox(0, "IconsRebuild", "Event", MB_OK); + Meta_LoadIcons(0, 0); + + if(ServiceExists(MS_CLIST_EXTRA_ADD_ICON)) { + for(i = 0; i < proto_count; i++) { + hExtraImage[i * 2] = (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hProtoIcons[i * 2], 0); + hExtraImage[i * 2 + 1] = (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hProtoIcons[i * 2 + 1], 0); + } + } + + return 0; +} + +int Meta_CListMW_ExtraIconsApply(WPARAM wParam, LPARAM lParam) { + + //MessageBox(0, "IconsApply", "Event", MB_OK); + + if(DBGetContactSettingDword((HANDLE)wParam, META_PROTO, META_ID, (DWORD)-1) != (DWORD)-1) { + if(ServiceExists(MS_CLIST_EXTRA_SET_ICON)) { + IconExtraColumn iec; + HANDLE most_online_im = Meta_GetMostOnline((HANDLE)wParam); + int i; + + if(most_online_im) { + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online_im, 0); + if(proto) { + WORD status = DBGetContactSettingWord(most_online_im, proto, "Status", ID_STATUS_OFFLINE); + iec.cbSize = sizeof(iec); + for(i = 0; i < proto_count; i++) { + if(!strcmp((proto_names + i * 128), proto)) { + if(hExtraImage[i * 2 + (status == ID_STATUS_OFFLINE ? 1 : 0)]) { + iec.hImage = hExtraImage[i * 2 + (status == ID_STATUS_OFFLINE ? 1 : 0)]; + iec.ColumnType = EXTRA_ICON_ADV2; + CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)wParam, (LPARAM)&iec); + iec.ColumnType = EXTRA_ICON_PROTO; + CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)wParam, (LPARAM)&iec); + } + break; + } + } + } + } + } + } + return 0; +} +*/ +int Meta_ClistDoubleClicked(WPARAM wParam, LPARAM lParam) { + + if(DBGetContactSettingDword((HANDLE)wParam,META_PROTO,"Default",(WORD)-1) == (WORD)-1) + { + // This is a simple contact + return 0; + } + else + { + HANDLE most_online = Meta_GetMostOnlineSupporting((HANDLE)wParam, PFLAGNUM_1, 0); + //DBEVENTINFO dbei; + char *proto; + char buffer[512]; + int caps; + + if(!most_online) + return 0; + + if(options.subcontact_windows) { + if(lParam) { + // contact from incoming message in lParam via (at this point) clist message event + CallService(MS_CLIST_CONTACTDOUBLECLICKED, (WPARAM)lParam, 0); + } else { + // simulate double click on most_online contact and stop event processing + CallService(MS_CLIST_CONTACTDOUBLECLICKED, (WPARAM)most_online, 0); + } + return 1; + } else { + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online, 0); + + if(proto) { + strcpy(buffer, proto); + strcat(buffer, PS_GETCAPS); + + // get the contacts messaging capabilities + caps = CallService(buffer, (WPARAM)PFLAGNUM_1, 0); + + if((caps & PF1_IMSEND) || (caps & PF1_CHAT) || (proto && strcmp(proto, "IRC") == 0)) + // let event process normally + return 0; + else { + // simulate double click on most_online contact and stop event processing + CallService(MS_CLIST_CONTACTDOUBLECLICKED, (WPARAM)most_online, 0); + return 1; + } + } else + return 0; + } + } + + return 0; +} + +int Meta_ClistMessageEventClicked(WPARAM wParam, LPARAM lParam) { + + HANDLE hContact = ((CLISTEVENT *)lParam)->hContact; + + // hdbevent contains the id of the subcontact + return Meta_ClistDoubleClicked((WPARAM)hContact, (LPARAM)((CLISTEVENT *)lParam)->hDbEvent); +} + +/** Called when all the plugin are loaded into Miranda. +* +* Initializes the 4 menus present in the context-menu +* and the initial value of nextMetaID +*/ +int Meta_ModulesLoaded(WPARAM wParam, LPARAM lParam) +{ + CLISTMENUITEM menu; + char buffer[512], buffer2[512], buffer3[512]; + int i; + + if(ServiceExists(MS_MSG_GETWINDOWAPI)) { + message_window_api_enabled = TRUE; + } + + if(ServiceExists(MS_UPDATE_REGISTER)) { + // register with updater + Update update = {0}; + char szVersion[16]; + + update.cbSize = sizeof(Update); + + update.szComponentName = pluginInfo.shortName; + update.pbVersion = (BYTE *)CreateVersionString(pluginInfo.version, szVersion); + update.cpbVersion = strlen((char *)update.pbVersion); + + update.szUpdateURL = UPDATER_AUTOREGISTER; + + // these are the three lines that matter - the archive, the page containing the version string, and the text (or data) + // before the version that we use to locate it on the page + // (note that if the update URL and the version URL point to standard file listing entries, the backend xml + // data will be used to check for updates rather than the actual web page - this is not true for beta urls) + update.szBetaUpdateURL = "http://www.scottellis.com.au/miranda_plugins/MetaContacts.zip"; + update.szBetaVersionURL = "http://www.scottellis.com.au/miranda_plugins/ver_MetaContacts.html"; + update.pbBetaVersionPrefix = (BYTE *)"MetaContacts Plugin, version "; + + update.cpbBetaVersionPrefix = strlen((char *)update.pbBetaVersionPrefix); + + CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update); + } + + + // disable group hack for older nicer versions without the fix + if(ServiceExists(MS_CLUI_GETVERSION)) { + char *version = (char *)CallService(MS_CLUI_GETVERSION, 0, 0); + if(version && strlen(version) >= strlen("CList Nicer+") && strncmp(version, "CList Nicer+", strlen("CList Nicer+")) == 0) + meta_group_hack_disabled = TRUE; + } + + // for database editor++ ver 3+ + if(ServiceExists("DBEditorpp/RegisterSingleModule")) + CallService("DBEditorpp/RegisterSingleModule",(WPARAM)META_PROTO,0); + + + hHooks[11] = (HANDLE)HookEvent(ME_CLIST_PREBUILDCONTACTMENU, Meta_ModifyMenu); + hHooks[12] = (HANDLE)HookEvent(ME_CLIST_DOUBLECLICKED, Meta_ClistDoubleClicked ); + //hHooks[13] = (HANDLE)HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, Meta_CListMW_ExtraIconsRebuild); + //hHooks[14] = (HANDLE)HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, Meta_CListMW_ExtraIconsApply); + + // icons are erased on this event... + // (BUT, the me_clist_extra_list_rebuild is send FIRST...so, we ignore this one...) + hHooks[15] = 0;//(HANDLE)HookEvent(ME_SKIN_ICONSCHANGED, Meta_LoadIcons); + + InitIcons(); + + ZeroMemory(&menu,sizeof(menu)); + menu.cbSize=sizeof(menu); + menu.flags = CMIM_ALL; + + menu.hIcon=hIconToggleOff; + + // main menu item + menu.pszName = (char *)Translate("Toggle MetaContacts Off"); + menu.pszService="MetaContacts/OnOff"; + menu.position = 500010000; + hMenuOnOff = (HANDLE)CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&menu); + + // contact menu items + menu.position = -200010; + menu.pszName = (char *)Translate("Convert to MetaContact"); + menu.hIcon=hIconConvert; + menu.pszService="MetaContacts/Convert"; + hMenuConvert = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&menu); + menu.position = -200009; + menu.pszName = (char *)Translate("Add to existing MetaContact..."); + menu.hIcon=hIconAdd; + menu.pszService="MetaContacts/AddTo"; + hMenuAdd = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&menu); + + menu.position = -200010; + menu.pszName = (char *)Translate("Edit MetaContact..."); + menu.hIcon=hIconEdit; + menu.pszService="MetaContacts/Edit"; + hMenuEdit = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&menu); + menu.position = -200009; + menu.pszName = (char *)Translate("Set as MetaContact default"); + menu.hIcon=hIconDefault; + menu.pszService="MetaContacts/Default"; + hMenuDefault = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&menu); + menu.position = -200008; + menu.pszName = (char *)Translate("Delete MetaContact"); + menu.hIcon=hIconRemove; + menu.pszService="MetaContacts/Delete"; + hMenuDelete = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&menu); + //menu.pszName = (char *)Translate("Force Default"); + //menu.pszService="MetaContacts/ForceDefault"; + //hMenuForceDefault = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&menu); + + menu.flags |= CMIF_HIDDEN; + menu.pszContactOwner = META_PROTO; + + menu.position = -99000; + for(i = 0; i < MAX_CONTACTS; i++) { + menu.position--; + strcpy(buffer3, (char *)Translate("Context")); + strcat(buffer3, _itoa(i, buffer2, 10)); + menu.pszName = buffer3; + + strcpy(buffer, "MetaContacts/MenuFunc"); + strcat(buffer, _itoa(i, buffer2, 10)); + menu.pszService= buffer; + + hMenuContact[i] = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM,0,(LPARAM)&menu); + } + + nextMetaID = DBGetContactSettingDword(NULL,META_PROTO,"NextMetaID",(DWORD)0); + + // attemp to subsume userinfo...(returning 1 does not prevent dialog - so disabled) + //hHooks[] = (HANDLE)HookEvent(ME_USERINFO_INITIALISE, Meta_UserInfo); + + // loop and copy data from subcontacts + if(options.copydata) { + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + int meta_id; + while ( hContact != NULL ) { + if((meta_id = DBGetContactSettingDword(hContact,META_PROTO,META_ID,(DWORD)-1))!=(DWORD)-1) { + Meta_CopyData(hContact); + } + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + } + + Meta_HideLinkedContacts(); + + if(!Meta_IsEnabled()) + { + + // modify main menu item + menu.flags = CMIM_NAME | CMIM_ICON; + menu.hIcon=hIconToggleOn; + menu.pszName = (char *)Translate("Toggle MetaContacts On"); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuOnOff, (LPARAM)&menu); + + + Meta_HideMetaContacts(TRUE); + } else { + Meta_SuppressStatus(options.suppress_status); + } + + // hook srmm window close/open events - message api ver 0.0.0.1+ + hHooks[16] = (HANDLE)HookEvent(ME_MSG_WINDOWEVENT, Meta_MessageWindowEvent); + if(hHooks[16]) // message api available + message_window_api_enabled = TRUE; + + return 0; +} + +static DWORD CALLBACK sttMenuThread( LPVOID param ) +{ + HMENU hMenu; + TPMPARAMS tpmp; + BOOL menuRet; + + hMenu = (HMENU)CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM)param, 0); + + ZeroMemory(&tpmp, sizeof(tpmp)); + tpmp.cbSize = sizeof(tpmp); + + menuRet = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, menuMousePoint.x, menuMousePoint.y, (HWND)CallService(MS_CLUI_GETHWND, 0, 0), &tpmp); + + CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(menuRet), MPCF_CONTACTMENU), (LPARAM)param); + + DestroyMenu(hMenu); + + return 0; +} + +int Meta_ContactMenuFunc(WPARAM wParam, LPARAM lParam) { + HANDLE hContact; + hContact = Meta_GetContactHandle((HANDLE)wParam, (int)lParam); + + if(options.menu_function == FT_MSG) { + // open message window if protocol supports message sending or chat, else simulate double click + + int caps; + char *proto; + char buffer[512]; + + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + + if(proto) { + strcpy(buffer, proto); + strcat(buffer, PS_GETCAPS); + + caps = CallService(buffer, (WPARAM)PFLAGNUM_1, 0); + + if((caps & PF1_IMSEND) || (caps & PF1_CHAT) || (proto && strcmp(proto, "IRC") == 0)) { + // set default contact for sending/status and open message window + DBWriteContactSettingDword((HANDLE)wParam, META_PROTO, "Default", (DWORD)(int)lParam); + NotifyEventHooks(hEventDefaultChanged, wParam, (LPARAM)hContact); + CallService(MS_MSG_SENDMESSAGE, wParam, 0); + } else + // protocol does not support messaging - simulate double click + CallService(MS_CLIST_CONTACTDOUBLECLICKED, (WPARAM)hContact, 0); + } else + // protocol does not support messaging - simulate double click + CallService(MS_CLIST_CONTACTDOUBLECLICKED, (WPARAM)hContact, 0); + + } else if(options.menu_function == FT_MENU) { + // show contact's context menu + QueueUserAPC((PAPCFUNC)sttMenuThread, metaMainThread, (ULONG)hContact); + } else if(options.menu_function == FT_INFO) { + // show user info for subcontact + CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)hContact, 0); + } + + return 0; +} + +//////////////////// +// file transfer support - mostly not required, since subcontacts do the receiving +//////////////////// +/* +int Meta_FileResume(WPARAM wParam, LPARAM lParam) +{ + DBVARIANT dbv; + CCSDATA *ccs = (CCSDATA *) lParam; + char *proto = 0; + + if(DBGetContactSetting(ccs->hContact,META_PROTO,"Default",&dbv)) + { + // This is a simple contact + // (this should normally not happen, since linked contacts do not appear on the list.) + return 1; + } + else + { + HANDLE most_online = Meta_GetMostOnlineSupporting(ccs->hContact, PFLAGNUM_1, PF1_FILERESUME); + //DBEVENTINFO dbei; + char szServiceName[100]; + + DBFreeVariant(&dbv); + + if(!most_online) + return 0; + + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online, 0); + + ccs->hContact = most_online; + Meta_SetNick(proto); + + _snprintf(szServiceName, sizeof(szServiceName), "%s%s", proto, PS_FILERESUME); + if (ServiceExists(szServiceName)) { + strncpy(szServiceName, PS_FILERESUME, sizeof(szServiceName)); + return (int)(CallContactService(ccs->hContact, szServiceName, ccs->wParam, ccs->lParam)); + } + } + return 1; // fail +} + +int Meta_FileAllow(WPARAM wParam, LPARAM lParam) +{ + DBVARIANT dbv; + CCSDATA *ccs = (CCSDATA *) lParam; + char *proto = 0; + + if(DBGetContactSetting(ccs->hContact,META_PROTO,"Default",&dbv)) + { + // This is a simple contact + // (this should normally not happen, since linked contacts do not appear on the list.) + return 0; + } + else + { + HANDLE most_online = Meta_GetMostOnlineSupporting(ccs->hContact, PFLAGNUM_1, PF1_FILE); + char szServiceName[100]; + + DBFreeVariant(&dbv); + + if(!most_online) + return 0; + + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online, 0); + + ccs->hContact = most_online; + Meta_SetNick(proto); + + _snprintf(szServiceName, sizeof(szServiceName), "%s%s", proto, PSS_FILEALLOW); + if (ServiceExists(szServiceName)) { + strncpy(szServiceName, PSS_FILEALLOW, sizeof(szServiceName)); + return (int)(CallContactService(ccs->hContact, szServiceName, ccs->wParam, ccs->lParam)); + } + } + return 0; // fail +} + +int Meta_FileDeny(WPARAM wParam, LPARAM lParam) +{ + DBVARIANT dbv; + CCSDATA *ccs = (CCSDATA *) lParam; + char *proto = 0; + + if(DBGetContactSetting(ccs->hContact,META_PROTO,"Default",&dbv)) + { + // This is a simple contact + // (this should normally not happen, since linked contacts do not appear on the list.) + return 1; + } + else + { + HANDLE most_online = Meta_GetMostOnlineSupporting(ccs->hContact, PFLAGNUM_1, PF1_FILE); + //DBEVENTINFO dbei; + char szServiceName[100]; + + DBFreeVariant(&dbv); + + if(!most_online) + return 1; + + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online, 0); + + ccs->hContact = most_online; + Meta_SetNick(proto); + + _snprintf(szServiceName, sizeof(szServiceName), "%s%s", proto, PSS_FILEDENY); + if (ServiceExists(szServiceName)) { + strncpy(szServiceName, PSS_FILEDENY, sizeof(szServiceName)); + return (int)(CallContactService(ccs->hContact, szServiceName, ccs->wParam, ccs->lParam)); + } + } + return 1; // fail +} + +int Meta_FileRecv(WPARAM wParam, LPARAM lParam) +{ + DBVARIANT dbv; + CCSDATA *ccs = (CCSDATA *) lParam; + char *proto = 0; + + if(DBGetContactSetting(ccs->hContact,META_PROTO,"Default",&dbv)) + { + // This is a simple contact + // (this should normally not happen, since linked contacts do not appear on the list.) + return 0; + } + else + { + HANDLE most_online = Meta_GetMostOnlineSupporting(ccs->hContact, PFLAGNUM_1, PF1_FILE); + //DBEVENTINFO dbei; + char szServiceName[100]; + + DBFreeVariant(&dbv); + + if(!most_online) + return 0; + + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online, 0); + + ccs->hContact = most_online; + Meta_SetNick(proto); + + _snprintf(szServiceName, sizeof(szServiceName), "%s%s", proto, PSR_FILE); + if (ServiceExists(szServiceName)) { + strncpy(szServiceName, PSR_FILE, sizeof(szServiceName)); + return (int)(CallContactService(ccs->hContact, szServiceName, ccs->wParam, ccs->lParam)); + } + } + + return 0; +} + +int Meta_FileCancel(WPARAM wParam, LPARAM lParam) +{ + DBVARIANT dbv; + CCSDATA *ccs = (CCSDATA *) lParam; + char *proto = 0; + + if(DBGetContactSetting(ccs->hContact,META_PROTO,"Default",&dbv)) + { + // This is a simple contact + // (this should normally not happen, since linked contacts do not appear on the list.) + return 0; + } + else + { + HANDLE most_online = Meta_GetMostOnlineSupporting(ccs->hContact, PFLAGNUM_1, PF1_FILE); + //DBEVENTINFO dbei; + char szServiceName[100]; + + DBFreeVariant(&dbv); + + if(!most_online) + return 0; + + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online, 0); + + ccs->hContact = most_online; + Meta_SetNick(proto); + + _snprintf(szServiceName, sizeof(szServiceName), "%s%s", proto, PSS_FILECANCEL); + if (ServiceExists(szServiceName)) { + strncpy(szServiceName, PSS_FILECANCEL, sizeof(szServiceName)); + return (int)(CallContactService(ccs->hContact, szServiceName, ccs->wParam, ccs->lParam)); + } + } + return 0; +} +*/ + +int Meta_FileSend(WPARAM wParam, LPARAM lParam) +{ + CCSDATA *ccs = (CCSDATA *) lParam; + char *proto = 0; + DWORD default_contact_number; + + if((default_contact_number = DBGetContactSettingDword(ccs->hContact,META_PROTO,"Default",(DWORD)-1)) == (DWORD)-1) + { + // This is a simple contact + // (this should normally not happen, since linked contacts do not appear on the list.) + return 0; + } + else + { + HANDLE most_online; + //DBEVENTINFO dbei; + char szServiceName[100]; + + most_online = Meta_GetMostOnlineSupporting(ccs->hContact, PFLAGNUM_1, PF1_FILESEND); + + if(!most_online) + return 0; + + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online, 0); + //Meta_CopyContactNick(ccs->hContact, most_online, proto); + + if(proto) { + ccs->hContact = most_online; + //Meta_SetNick(proto); + + _snprintf(szServiceName, sizeof(szServiceName), "%s%s", proto, PSS_FILE); + if (ServiceExists(szServiceName)) { + strncpy(szServiceName, PSS_FILE, sizeof(szServiceName)); + return (int)(CallContactService(ccs->hContact, szServiceName, ccs->wParam, ccs->lParam)); + } + } + } + return 0; // fail +} + +int Meta_GetAwayMsg(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = (CCSDATA *) lParam; + char *proto = 0; + DWORD default_contact_number; + + if((default_contact_number = DBGetContactSettingDword(ccs->hContact,META_PROTO,"Default",(DWORD)-1)) == (DWORD)-1) + { + // This is a simple contact + // (this should normally not happen, since linked contacts do not appear on the list.) + return 0; + } + else + { + HANDLE most_online; + //DBEVENTINFO dbei; + char szServiceName[100]; + + most_online = Meta_GetMostOnlineSupporting(ccs->hContact, PFLAGNUM_1, PF1_MODEMSGRECV); + + if(!most_online) + return 0; + + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online, 0); + if(!proto) return 0; + + //Meta_CopyContactNick(ccs->hContact, most_online, proto); + + ccs->hContact = most_online; + //Meta_SetNick(proto); + + _snprintf(szServiceName, sizeof(szServiceName), "%s%s", proto, PSS_GETAWAYMSG); + if (ServiceExists(szServiceName)) { + strncpy(szServiceName, PSS_GETAWAYMSG, sizeof(szServiceName)); + return (int)(CallContactService(ccs->hContact, szServiceName, ccs->wParam, ccs->lParam)); + } + } + return 0; // fail +} + +int Meta_GetAvatarInfo(WPARAM wParam, LPARAM lParam) { + PROTO_AVATAR_INFORMATION *AI = (PROTO_AVATAR_INFORMATION *) lParam; + char *proto = 0; + DWORD default_contact_number; + + if((default_contact_number = DBGetContactSettingDword(AI->hContact,META_PROTO,"Default",(DWORD)-1)) == (DWORD)-1) + { + // This is a simple contact + // (this should normally not happen, since linked contacts do not appear on the list.) + return 0; + } + else + { + HANDLE most_online; + //DBEVENTINFO dbei; + char szServiceName[100]; + + most_online = Meta_GetMostOnlineSupporting(AI->hContact, PFLAGNUM_4, PF4_AVATARS); + + if(!most_online) + return GAIR_NOAVATAR; + + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online, 0); + if(!proto) return GAIR_NOAVATAR; + + //Meta_CopyContactNick(AI->hContact, most_online, proto); + + AI->hContact = most_online; + + //Meta_SetNick(proto); + + _snprintf(szServiceName, sizeof(szServiceName), "%s%s", proto, PS_GETAVATARINFO); + if (ServiceExists(szServiceName)) { + return (int)(CallService(szServiceName, wParam, lParam)); + } + } + return GAIR_NOAVATAR; // fail +} + +int Meta_GetInfo(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = (CCSDATA *) lParam; + char *proto = 0; + DWORD default_contact_number; + + if((default_contact_number = DBGetContactSettingDword(ccs->hContact,META_PROTO,"Default",(DWORD)-1)) == (DWORD)-1) + { + // This is a simple contact + // (this should normally not happen, since linked contacts do not appear on the list.) + return 0; + } + else + { + HANDLE most_online; + PROTO_AVATAR_INFORMATION AI; + char szServiceName[100]; + + most_online = Meta_GetMostOnlineSupporting(ccs->hContact, PFLAGNUM_4, PF4_AVATARS); + + if(!most_online) + return 0; + + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)most_online, 0); + if(!proto) return 0; + + AI.cbSize = sizeof(AI); + AI.hContact = ccs->hContact; + AI.format = PA_FORMAT_UNKNOWN; + strcpy(AI.filename, "X"); + if((int)CallProtoService(META_PROTO, PS_GETAVATARINFO, 0, (LPARAM)&AI) == GAIR_SUCCESS) + DBWriteContactSettingString(ccs->hContact, "ContactPhoto", "File",AI.filename); + + most_online = Meta_GetMostOnline(ccs->hContact); + Meta_CopyContactNick(ccs->hContact, most_online); + + if(!most_online) + return 0; + + //Meta_CopyContactNick(ccs->hContact, most_online, proto); + + ccs->hContact = most_online; + //Meta_SetNick(proto); + + _snprintf(szServiceName, sizeof(szServiceName), "%s%s", proto, PSS_GETINFO); + if (ServiceExists(szServiceName)) { + strncpy(szServiceName, PSS_GETINFO, sizeof(szServiceName)); + return (int)(CallContactService(ccs->hContact, szServiceName, ccs->wParam, ccs->lParam)); + } + } + return 0; // fail +} + +int Meta_OptInit(WPARAM wParam, LPARAM lParam) { + OPTIONSDIALOGPAGE odp; + ZeroMemory(&odp, sizeof(odp)); +#define OPTIONPAGE_OLD_SIZE2 60 + //odp.cbSize = sizeof(odp); + odp.cbSize = OPTIONPAGE_OLD_SIZE2; + odp.position = -790000000; + odp.hInstance = hInstance; + odp.pszTemplate = MAKEINTRESOURCE(IDD_OPTIONS); + odp.pszTitle = Translate("MetaContacts"); + odp.pszGroup = Translate("Contact List"); + odp.flags = ODPF_BOLDGROUPS; + odp.nIDBottomSimpleControl = 0; + odp.pfnDlgProc = DlgProcOpts; + CallService( MS_OPT_ADDPAGE, wParam,( LPARAM )&odp ); + + return 0; +} + +int Meta_CallMostOnline(WPARAM wParam, LPARAM lParam) { + HANDLE most_online_im = Meta_GetMostOnline((HANDLE)wParam); + + // fix nick + Meta_CopyContactNick((HANDLE)wParam, most_online_im); + + // fix status + Meta_FixStatus((HANDLE)wParam); + + // copy all other data + Meta_CopyData((HANDLE) wParam); + + return 0; +} + + +int Meta_OnOff(WPARAM wParam, LPARAM lParam) { + CLISTMENUITEM menu; + menu.cbSize = sizeof(CLISTMENUITEM); + // just write to db - the rest is handled in the Meta_SettingChanged function + if(DBGetContactSettingByte(0, META_PROTO, "Enabled", 1)) { + DBWriteContactSettingByte(0, META_PROTO, "Enabled", 0); + // modify main menu item + menu.flags = CMIM_NAME | CMIM_ICON; + menu.hIcon=hIconToggleOn; + menu.pszName = (char *)Translate("Toggle MetaContacts On"); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuOnOff, (LPARAM)&menu); + } else { + DBWriteContactSettingByte(0, META_PROTO, "Enabled", 1); + // modify main menu item + menu.flags = CMIM_NAME | CMIM_ICON; + menu.hIcon=hIconToggleOff; + menu.pszName = (char *)Translate("Toggle MetaContacts Off"); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuOnOff, (LPARAM)&menu); + } + + return 0; +} + + +int Meta_PreShutdown(WPARAM wParam, LPARAM lParam) { + //MessageBox(0, "Preshutdown called", "MC", MB_OK); + Meta_SetStatus((WPARAM)ID_STATUS_OFFLINE, 0); + Meta_UnhideLinkedContacts(); + Meta_SuppressStatus(FALSE); + //MessageBox(0, "Status is OFFLINE", "MC", MB_OK); + //MessageBox(0, "Preshutdown complete", "MC", MB_OK); + return 0; +} + +int Meta_OkToExit(WPARAM wParam, LPARAM lParam) { + Meta_SetStatus((WPARAM)ID_STATUS_OFFLINE, 0); + return 0; +} + +int Meta_OnIdleChanged(WPARAM wParam, LPARAM lParam) { + return 0; +} + +/** Initializes all services provided by the plugin +* +* Creates every function and hooks the event desired. +*/ +void Meta_InitServices() +{ + int i; + + // Get the memory management structure. + CallService(MS_SYSTEM_GET_MMI,0,(LPARAM)&mmi); + + previousMode = mcStatus = ID_STATUS_OFFLINE; + + // set hooks pointers and services pointers to zero - in case we do not initialize them all correctly + for(i=0;i