1 #define MIRANDA_VER 0x900
10 #include "newpluginapi.h"
11 #include "m_protosvc.h"
12 #include "m_protoint.h"
14 #include "m_database.h"
15 #include "m_langpack.h"
16 #include "m_options.h"
20 #include "m_protomod.h"
23 #include "sipe-core.h"
25 #include "sipe-core-private.h"
26 #include "sipe-backend.h"
27 #include "sipe-utils.h"
28 #include "sipe-conf.h"
29 #include "sipe-chat.h"
30 #include "sipe-session.h"
31 #include "miranda-private.h"
32 #include "miranda-resource.h"
34 /* Status identifiers (see also: sipe_status_types()) */
35 #define SIPE_STATUS_ID_UNKNOWN "unset" /* Unset (primitive) */
36 #define SIPE_STATUS_ID_OFFLINE "offline" /* Offline (primitive) */
37 #define SIPE_STATUS_ID_AVAILABLE "available" /* Online */
38 /* PURPLE_STATUS_UNAVAILABLE: */
39 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
40 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
41 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
42 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
43 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
44 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
45 #define SIPE_STATUS_ID_INVISIBLE "invisible" /* Appear Offline */
46 /* PURPLE_STATUS_AWAY: */
47 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
48 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
49 #define SIPE_STATUS_ID_AWAY "away" /* Away (primitive) */
50 /** Reuters status (user settable) */
51 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
52 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
53 /* ??? PURPLE_STATUS_MOBILE */
54 /* ??? PURPLE_STATUS_TUNE */
56 /* Miranda interface globals */
57 #define ENTRY_SIG 0x88442211
59 static NETLIBSELECTEX m_select
= {0};
60 static GHashTable
*m_readhash
= NULL
;
61 static GHashTable
*m_writehash
= NULL
;
62 static GList
*m_entries
= NULL
;
63 static HANDLE wake_up_semaphore
= NULL
;
66 TCHAR _tcharbuf
[32768];
67 TCHAR
* CHAR2TCHAR( const char *chr
) {
69 mbstowcs( _tcharbuf
, chr
, strlen(chr
)+1 );
77 char* TCHAR2CHAR( const TCHAR
*tchr
) {
79 wcstombs( _charbuf
, tchr
, wcslen(tchr
)+1 );
86 void CreateProtoService(const SIPPROTO
*pr
, const char* szService
, SipSimpleServiceFunc serviceProc
)
88 char str
[ MAXMODULELABELLENGTH
];
90 mir_snprintf(str
, sizeof(str
), "%s%s", pr
->proto
.m_szModuleName
, szService
);
91 CreateServiceFunctionObj(str
, (MIRANDASERVICEOBJ
)*(void**)&serviceProc
, pr
);
94 HANDLE
HookProtoEvent(const SIPPROTO
*pr
, const char* szEvent
, SipSimpleEventFunc pFunc
)
96 return HookEventObj(szEvent
, (MIRANDAHOOKOBJ
)*(void**)&pFunc
, pr
);
99 void ProtocolAckThread(SIPPROTO
*pr
, struct miranda_sipe_ack_args
* pArguments
)
101 ProtoBroadcastAck(pr
->proto
.m_szModuleName
, pArguments
->hContact
, pArguments
->nAckType
, pArguments
->nAckResult
, pArguments
->hSequence
, pArguments
->pszMessage
);
103 if (pArguments
->nAckResult
== ACKRESULT_SUCCESS
)
104 SIPE_DEBUG_INFO_NOFORMAT("ProtocolAckThread: Sent ACK");
105 else if (pArguments
->nAckResult
== ACKRESULT_FAILED
)
106 SIPE_DEBUG_INFO_NOFORMAT("ProtocolAckThread: Sent NACK");
108 g_free((gpointer
)pArguments
->pszMessage
);
112 void ForkThread( SIPPROTO
*pr
, SipSimpleThreadFunc pFunc
, void* arg
)
114 CloseHandle(( HANDLE
)mir_forkthreadowner(( pThreadFuncOwner
)*( void** )&pFunc
, pr
, arg
, NULL
));
117 void SendProtoAck( SIPPROTO
*pr
, HANDLE hContact
, DWORD dwCookie
, int nAckResult
, int nAckType
, char* pszMessage
)
119 struct miranda_sipe_ack_args
* pArgs
= (struct miranda_sipe_ack_args
*)g_malloc(sizeof(struct miranda_sipe_ack_args
)); // This will be freed in the new thread
120 pArgs
->hContact
= hContact
;
121 pArgs
->hSequence
= (HANDLE
)dwCookie
;
122 pArgs
->nAckResult
= nAckResult
;
123 pArgs
->nAckType
= nAckType
;
124 pArgs
->pszMessage
= (LPARAM
)g_strdup(pszMessage
);
126 ForkThread( pr
, ( SipSimpleThreadFunc
)&ProtocolAckThread
, pArgs
);
130 const char *MirandaStatusToSipe(int status
) {
134 case ID_STATUS_OFFLINE
:
135 return SIPE_STATUS_ID_OFFLINE
;
137 case ID_STATUS_ONLINE
:
138 case ID_STATUS_FREECHAT
:
139 return SIPE_STATUS_ID_AVAILABLE
;
141 case ID_STATUS_ONTHEPHONE
:
142 return SIPE_STATUS_ID_ON_PHONE
;
145 return SIPE_STATUS_ID_DND
;
148 return SIPE_STATUS_ID_AWAY
;
151 return SIPE_STATUS_ID_BRB
;
153 case ID_STATUS_OUTTOLUNCH
:
154 return SIPE_STATUS_ID_LUNCH
;
156 case ID_STATUS_OCCUPIED
:
157 return SIPE_STATUS_ID_BUSY
;
159 case ID_STATUS_INVISIBLE
:
160 return SIPE_STATUS_ID_INVISIBLE
;
163 return SIPE_STATUS_ID_UNKNOWN
;
168 int SendBroadcast(SIPPROTO
*pr
, HANDLE hContact
,int type
,int result
,HANDLE hProcess
,LPARAM lParam
)
172 ack
.cbSize
= sizeof(ACKDATA
);
173 ack
.szModule
= pr
->proto
.m_szModuleName
;
174 ack
.hContact
= hContact
;
177 ack
.hProcess
= hProcess
;
179 return CallService(MS_PROTO_BROADCASTACK
,0,(LPARAM
)&ack
);
183 /* Protocol interface support functions */
185 _debuglog(const char *filename
, const char *funcname
, const char *fmt
,...)
192 for ( tmp
=filename
; *tmp
; tmp
++ )
194 if ((*tmp
== '/') || (*tmp
== '\\')) filename
=tmp
+1;
198 vsnprintf(szText
,sizeof(szText
),fmt
,va
);
201 if (!fopen_s(&fh
,"c:/sipsimple.log","a")) {
202 fprintf(fh
, "[%d] %22s %s: %s", _getpid(), filename
, funcname
, szText
);
207 typedef struct _time_entry
210 GSourceFunc function
;
215 static unsigned __stdcall
216 inputloop(void* data
)
219 struct sipe_miranda_sel_entry
*entry
;
222 m_select
.cbSize
= sizeof(m_select
);
223 m_select
.dwTimeout
= 6000;
225 while( m_select
.hReadConns
[0] || m_select
.hWriteConns
[0])
228 SIPE_DEBUG_INFO_NOFORMAT("About to run select");
229 lstRes
= CallService(MS_NETLIB_SELECTEX
, 0, (LPARAM
)&m_select
);
232 SIPE_DEBUG_INFO_NOFORMAT("Connection failed while waiting.");
235 else if (lstRes
== 0)
237 SIPE_DEBUG_INFO_NOFORMAT("Select Timeout.");
238 lstRes
= SOCKET_ERROR
;
242 SIPE_DEBUG_INFO_NOFORMAT("Back from select");
244 for ( cnt
=0 ; m_select
.hReadConns
[cnt
] ; cnt
++ )
246 if (!m_select
.hReadStatus
[cnt
]) continue;
247 SIPE_DEBUG_INFO("FD at position <%d> ready to read.", cnt
);
248 entry
= (struct sipe_miranda_sel_entry
*)g_hash_table_lookup(m_readhash
, (gconstpointer
)m_select
.hReadConns
[cnt
]);
251 SIPE_DEBUG_INFO_NOFORMAT("ERROR: no read handler found.");
254 SIPE_DEBUG_INFO_NOFORMAT("About to call read function.");
255 entry
->func( entry
->user_data
, (gint
)m_select
.hReadConns
[cnt
], SIPE_MIRANDA_INPUT_READ
);
256 SIPE_DEBUG_INFO_NOFORMAT("read function returned.");
259 for ( cnt
=0 ; m_select
.hWriteConns
[cnt
] ; cnt
++ )
261 if (!m_select
.hWriteStatus
[cnt
]) continue;
262 SIPE_DEBUG_INFO("FD at position <%d> ready to write.", cnt
);
263 entry
= (struct sipe_miranda_sel_entry
*)g_hash_table_lookup(m_writehash
, (gconstpointer
)m_select
.hWriteConns
[cnt
]);
266 SIPE_DEBUG_INFO_NOFORMAT("ERROR: no write handler found.");
269 SIPE_DEBUG_INFO_NOFORMAT("About to call write function.");
270 entry
->func( entry
->user_data
, (gint
)m_select
.hWriteConns
[cnt
], SIPE_MIRANDA_INPUT_WRITE
);
271 SIPE_DEBUG_INFO_NOFORMAT("write function returned.");
275 /* Free all removed entries */
276 while (m_entries
) g_list_delete_link(m_entries
, g_list_last(m_entries
));
282 /* libsipe interface functions */
284 miranda_sipe_get_current_status(struct sipe_core_private
*sipe_private
, const char* name
)
286 SIPPROTO
*pr
= sipe_private
->public.backend_private
;
287 char *module
= pr
->proto
.m_szModuleName
;
291 return g_strdup(MirandaStatusToSipe(pr
->proto
.m_iStatus
));
293 hContact
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, name
, NULL
);
294 return g_strdup(MirandaStatusToSipe(DBGetContactSettingWord( hContact
, module
, "Status", ID_STATUS_OFFLINE
)));
297 struct sipe_miranda_sel_entry
*
298 sipe_miranda_input_add(HANDLE fd
, sipe_miranda_input_condition cond
, sipe_miranda_input_function func
, gpointer user_data
)
302 struct sipe_miranda_sel_entry
*entry
;
305 m_readhash
= g_hash_table_new(NULL
, NULL
);
308 m_writehash
= g_hash_table_new(NULL
, NULL
);
310 if ((cond
!= SIPE_MIRANDA_INPUT_READ
) && (cond
!= SIPE_MIRANDA_INPUT_WRITE
))
312 SIPE_DEBUG_INFO("Invalid input condition <%d> cond.", cond
);
316 entry
= g_new0(struct sipe_miranda_sel_entry
,1);
317 entry
->sig
= ENTRY_SIG
;
319 entry
->user_data
= user_data
;
322 if (cond
== SIPE_MIRANDA_INPUT_READ
)
324 for ( rcnt
=0 ; m_select
.hReadConns
[rcnt
] && m_select
.hReadConns
[rcnt
]!=(HANDLE
)fd
; rcnt
++ );
325 m_select
.hReadConns
[rcnt
] = (HANDLE
)fd
;
326 g_hash_table_replace( m_readhash
, (gpointer
)fd
, entry
);
328 else if (cond
== SIPE_MIRANDA_INPUT_WRITE
)
330 for ( wcnt
=0 ; m_select
.hWriteConns
[wcnt
] && m_select
.hWriteConns
[wcnt
]!=(HANDLE
)fd
; wcnt
++ );
331 m_select
.hWriteConns
[rcnt
] = (HANDLE
)fd
;
332 g_hash_table_replace( m_writehash
, (gpointer
)fd
, entry
);
336 CloseHandle((HANDLE
) mir_forkthreadex( inputloop
, NULL
, 8192, NULL
));
338 SIPE_DEBUG_INFO_NOFORMAT("Added input handler.");
343 sipe_miranda_input_remove(struct sipe_miranda_sel_entry
*entry
)
349 SIPE_DEBUG_INFO_NOFORMAT("Not a valid entry. NULL.");
353 if (entry
->sig
!= ENTRY_SIG
)
355 SIPE_DEBUG_INFO("Not a valid entry. Sig is <%08x>.", entry
->sig
);
359 if (g_hash_table_lookup(m_readhash
, (gconstpointer
)entry
->fd
) == entry
)
361 for ( cnt
=0 ; m_select
.hReadConns
[cnt
] && m_select
.hReadConns
[cnt
]!=(HANDLE
)entry
->fd
; cnt
++ );
362 for ( ; m_select
.hReadConns
[cnt
] ; cnt
++ ) m_select
.hReadConns
[cnt
] = m_select
.hReadConns
[cnt
+1];
363 g_hash_table_remove(m_readhash
, (gconstpointer
)entry
->fd
);
366 if (g_hash_table_lookup(m_writehash
, (gconstpointer
)entry
->fd
) == entry
)
368 for ( cnt
=0 ; m_select
.hWriteConns
[cnt
] && m_select
.hWriteConns
[cnt
]!=(HANDLE
)entry
->fd
; cnt
++ );
369 for ( ; m_select
.hWriteConns
[cnt
] ; cnt
++ ) m_select
.hWriteConns
[cnt
] = m_select
.hWriteConns
[cnt
+1];
370 g_hash_table_remove(m_writehash
, (gconstpointer
)entry
->fd
);
373 /* Add it to the list of entries that can be freed after the next select
374 * loop in the thread that's handling the actual select
376 g_list_append( m_entries
, entry
);
382 miranda_sipe_request_authorization(struct sipe_core_private
*sipe_private
, const char *who
, const char *alias
,
383 sipe_backend_buddy_request_authorization_cb auth_cb
,
384 sipe_backend_buddy_request_authorization_cb deny_cb
, void *data
)
386 SIPPROTO
*pr
= sipe_private
->public.backend_private
;
388 PROTORECVEVENT pre
= {0};
393 hContact
= sipe_backend_buddy_find( SIPE_CORE_PUBLIC
, who
, NULL
);
396 hContact
= ( HANDLE
)CallService( MS_DB_CONTACT_ADD
, 0, 0 );
397 CallService( MS_PROTO_ADDTOCONTACT
, ( WPARAM
)hContact
,( LPARAM
)pr
->proto
.m_szModuleName
);
398 DBWriteContactSettingByte( hContact
, "CList", "NotOnList", 1 );
399 sipe_miranda_setContactString( pr
, hContact
, SIP_UNIQUEID
, who
); // name
400 sipe_miranda_setContactStringUtf( pr
, hContact
, "Nick", alias
); // server_alias
403 ccs
.szProtoService
= PSR_AUTH
;
405 ccs
.lParam
= (LPARAM
)&pre
;
406 ccs
.hContact
=hContact
= hContact
;
408 pre
.timestamp
= time(NULL
);
409 pre
.lParam
=sizeof(DWORD
)+sizeof(HANDLE
)+strlen(who
)+1+strlen(alias
)+1+1+5;
411 /*blob is: uin(DWORD), hcontact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ), reason(ASCIIZ)*/
412 pCurBlob
=szBlob
=(char *)_alloca(pre
.lParam
);
413 memset(pCurBlob
, 0, sizeof(DWORD
)); pCurBlob
+=sizeof(DWORD
);
414 memcpy(pCurBlob
,&hContact
,sizeof(HANDLE
)); pCurBlob
+=sizeof(HANDLE
);
415 strcpy((char *)pCurBlob
,who
); pCurBlob
+=strlen((char *)pCurBlob
)+1;
416 *pCurBlob
= '\0'; pCurBlob
++;
417 strcpy((char *)pCurBlob
,alias
); pCurBlob
+=strlen((char *)pCurBlob
)+1;
418 *pCurBlob
= '\0'; pCurBlob
++;
419 *pCurBlob
= '\0'; pCurBlob
++;
420 pre
.szMessage
=(char *)szBlob
;
422 CallService(MS_PROTO_CHAINRECV
,0,(LPARAM
)&ccs
);
424 /* TODO: Store callbacks somewhere since miranda has no way to pass them on */
429 miranda_sipe_connection_cleanup(struct sipe_core_private
*sip
)
431 SIPPROTO
*pr
= sip
->public.backend_private
;
436 miranda_sipe_notify_user(SIP_HANDLE sip
, const char *name
, sipe_message_flags flags
, const gchar
*message
)
442 __debuglog(sipe_debug_level level
, const char *fmt
,...)
447 char *str
= DBGetString( NULL
, SIPSIMPLE_PROTOCOL_NAME
, "debuglog");
450 vsnprintf(szText
,sizeof(szText
),fmt
,va
);
454 str
= mir_strdup("c:/sipsimple.log");
456 if (!fopen_s(&fh
, str
, "a")) {
457 fprintf(fh
, "<[%d]> %s", _getpid(), szText
);
464 /****************************************************************************
465 * Struct that defines our interface with libsipe
466 ****************************************************************************/
467 /* Protocol interface functions */
468 int RecvContacts( SIPPROTO
*pr
, HANDLE hContact
, PROTORECVEVENT
* evt
)
474 int RecvFile( SIPPROTO
*pr
, HANDLE hContact
, PROTOFILEEVENT
* evt
)
480 int RecvUrl( SIPPROTO
*pr
, HANDLE hContact
, PROTORECVEVENT
* evt
)
486 int SendContacts( SIPPROTO
*pr
, HANDLE hContact
, int flags
, int nContacts
, HANDLE
* hContactsList
)
489 SIPE_DEBUG_INFO("SendContacts: flags <%x> ncontacts <%x>", flags
, nContacts
);
493 int SendUrl( SIPPROTO
*pr
, HANDLE hContact
, int flags
, const char* url
)
496 SIPE_DEBUG_INFO("SendUrl: iflags <%x> url <%s>", flags
, url
);
500 int SetApparentMode( SIPPROTO
*pr
, HANDLE hContact
, int mode
)
503 SIPE_DEBUG_INFO("SetApparentMode: mode <%x>", mode
);
507 int RecvAwayMsg( SIPPROTO
*pr
, HANDLE hContact
, int mode
, PROTORECVEVENT
* evt
)
510 SIPE_DEBUG_INFO("RecvAwayMsg: mode <%x>", mode
);
514 int SendAwayMsg( SIPPROTO
*pr
, HANDLE hContact
, HANDLE hProcess
, const char* msg
)
517 SIPE_DEBUG_INFO("SendAwayMsg: msg <%s>", msg
);
521 static const char *about_txt
=
522 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fswiss\\fcharset0 Arial;}{\\f1\\fnil\fcharset2 Symbol;}}"
523 "{\\*\\generator Msftedit 5.41.15.1507;}\\viewkind4\\uc1\\pard\\b\\f0\\fs24 Sipe" SIPE_VERSION
"\\fs20\\par"
525 "A third-party plugin implementing extended version of SIP/SIMPLE used by various products:\\par"
526 "\\pard{\\pntext\\f1\\'B7\\tab}{\\*\\pn\\pnlvlblt\\pnf1\\pnindent0{\\pntxtb\'B7}}\\fi-720"
527 "\\li720 MS Office Communications Server 2007 (R2)\\par"
528 "{\\pntext\\f1\\'B7\\tab}MS Live Communications Server 2005/2003\\par"
529 "{\\pntext\\f1\\'B7\\tab}Reuters Messaging\\par"
531 "Home: http://sipe.sourceforge.net\\par "
532 "Support: http://sourceforge.net/projects/sipe/forums/forum/68853\\par "
533 "License: GPLv2\\par "
535 "\\b Authors:\\b0\\par"
536 " - Anibal Avelar\\par"
537 " - Gabriel Burt\\par"
538 " - Stefan Becker\\par"