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 if (!chr
) return NULL
;
70 mbstowcs( _tcharbuf
, chr
, strlen(chr
)+1 );
78 char* TCHAR2CHAR( const TCHAR
*tchr
) {
80 if (!tchr
) return NULL
;
81 wcstombs( _charbuf
, tchr
, wcslen(tchr
)+1 );
88 void CreateProtoService(const SIPPROTO
*pr
, const char* szService
, SipSimpleServiceFunc serviceProc
)
90 char str
[ MAXMODULELABELLENGTH
];
92 mir_snprintf(str
, sizeof(str
), "%s%s", pr
->proto
.m_szModuleName
, szService
);
93 CreateServiceFunctionObj(str
, (MIRANDASERVICEOBJ
)*(void**)&serviceProc
, pr
);
96 HANDLE
HookProtoEvent(const SIPPROTO
*pr
, const char* szEvent
, SipSimpleEventFunc pFunc
)
98 return HookEventObj(szEvent
, (MIRANDAHOOKOBJ
)*(void**)&pFunc
, pr
);
101 void ProtocolAckThread(SIPPROTO
*pr
, struct miranda_sipe_ack_args
* pArguments
)
103 ProtoBroadcastAck(pr
->proto
.m_szModuleName
, pArguments
->hContact
, pArguments
->nAckType
, pArguments
->nAckResult
, pArguments
->hSequence
, pArguments
->pszMessage
);
105 if (pArguments
->nAckResult
== ACKRESULT_SUCCESS
)
106 SIPE_DEBUG_INFO_NOFORMAT("ProtocolAckThread: Sent ACK");
107 else if (pArguments
->nAckResult
== ACKRESULT_FAILED
)
108 SIPE_DEBUG_INFO_NOFORMAT("ProtocolAckThread: Sent NACK");
110 g_free((gpointer
)pArguments
->pszMessage
);
114 void ForkThread( SIPPROTO
*pr
, SipSimpleThreadFunc pFunc
, void* arg
)
116 CloseHandle(( HANDLE
)mir_forkthreadowner(( pThreadFuncOwner
)*( void** )&pFunc
, pr
, arg
, NULL
));
119 void SendProtoAck( SIPPROTO
*pr
, HANDLE hContact
, DWORD dwCookie
, int nAckResult
, int nAckType
, char* pszMessage
)
121 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
122 pArgs
->hContact
= hContact
;
123 pArgs
->hSequence
= (HANDLE
)dwCookie
;
124 pArgs
->nAckResult
= nAckResult
;
125 pArgs
->nAckType
= nAckType
;
126 pArgs
->pszMessage
= (LPARAM
)g_strdup(pszMessage
);
128 ForkThread( pr
, ( SipSimpleThreadFunc
)&ProtocolAckThread
, pArgs
);
132 const char *MirandaStatusToSipe(int status
) {
136 case ID_STATUS_OFFLINE
:
137 return SIPE_STATUS_ID_OFFLINE
;
139 case ID_STATUS_ONLINE
:
140 case ID_STATUS_FREECHAT
:
141 return SIPE_STATUS_ID_AVAILABLE
;
143 case ID_STATUS_ONTHEPHONE
:
144 return SIPE_STATUS_ID_ON_PHONE
;
147 return SIPE_STATUS_ID_DND
;
150 return SIPE_STATUS_ID_AWAY
;
153 return SIPE_STATUS_ID_BRB
;
155 case ID_STATUS_OUTTOLUNCH
:
156 return SIPE_STATUS_ID_LUNCH
;
158 case ID_STATUS_OCCUPIED
:
159 return SIPE_STATUS_ID_BUSY
;
161 case ID_STATUS_INVISIBLE
:
162 return SIPE_STATUS_ID_INVISIBLE
;
165 return SIPE_STATUS_ID_UNKNOWN
;
170 int SendBroadcast(SIPPROTO
*pr
, HANDLE hContact
,int type
,int result
,HANDLE hProcess
,LPARAM lParam
)
174 ack
.cbSize
= sizeof(ACKDATA
);
175 ack
.szModule
= pr
->proto
.m_szModuleName
;
176 ack
.hContact
= hContact
;
179 ack
.hProcess
= hProcess
;
181 return CallService(MS_PROTO_BROADCASTACK
,0,(LPARAM
)&ack
);
185 /* Protocol interface support functions */
187 _debuglog(const char *filename
, const char *funcname
, const char *fmt
,...)
194 for ( tmp
=filename
; *tmp
; tmp
++ )
196 if ((*tmp
== '/') || (*tmp
== '\\')) filename
=tmp
+1;
200 vsnprintf(szText
,sizeof(szText
),fmt
,va
);
203 if (!fopen_s(&fh
,"c:/sipsimple.log","a")) {
204 fprintf(fh
, "[%d] %22s %s: %s", _getpid(), filename
, funcname
, szText
);
209 typedef struct _time_entry
212 GSourceFunc function
;
217 static unsigned __stdcall
218 inputloop(void* data
)
221 struct sipe_miranda_sel_entry
*entry
;
224 m_select
.cbSize
= sizeof(m_select
);
225 m_select
.dwTimeout
= 6000;
227 while( m_select
.hReadConns
[0] || m_select
.hWriteConns
[0])
230 SIPE_DEBUG_INFO_NOFORMAT("About to run select");
231 lstRes
= CallService(MS_NETLIB_SELECTEX
, 0, (LPARAM
)&m_select
);
234 SIPE_DEBUG_INFO_NOFORMAT("Connection failed while waiting.");
237 else if (lstRes
== 0)
239 SIPE_DEBUG_INFO_NOFORMAT("Select Timeout.");
240 lstRes
= SOCKET_ERROR
;
244 SIPE_DEBUG_INFO_NOFORMAT("Back from select");
246 for ( cnt
=0 ; m_select
.hReadConns
[cnt
] ; cnt
++ )
248 if (!m_select
.hReadStatus
[cnt
]) continue;
249 SIPE_DEBUG_INFO("FD at position <%d> ready to read.", cnt
);
250 entry
= (struct sipe_miranda_sel_entry
*)g_hash_table_lookup(m_readhash
, (gconstpointer
)m_select
.hReadConns
[cnt
]);
253 SIPE_DEBUG_INFO_NOFORMAT("ERROR: no read handler found.");
256 SIPE_DEBUG_INFO_NOFORMAT("About to call read function.");
257 entry
->func( entry
->user_data
, (gint
)m_select
.hReadConns
[cnt
], SIPE_MIRANDA_INPUT_READ
);
258 SIPE_DEBUG_INFO_NOFORMAT("read function returned.");
261 for ( cnt
=0 ; m_select
.hWriteConns
[cnt
] ; cnt
++ )
263 if (!m_select
.hWriteStatus
[cnt
]) continue;
264 SIPE_DEBUG_INFO("FD at position <%d> ready to write.", cnt
);
265 entry
= (struct sipe_miranda_sel_entry
*)g_hash_table_lookup(m_writehash
, (gconstpointer
)m_select
.hWriteConns
[cnt
]);
268 SIPE_DEBUG_INFO_NOFORMAT("ERROR: no write handler found.");
271 SIPE_DEBUG_INFO_NOFORMAT("About to call write function.");
272 entry
->func( entry
->user_data
, (gint
)m_select
.hWriteConns
[cnt
], SIPE_MIRANDA_INPUT_WRITE
);
273 SIPE_DEBUG_INFO_NOFORMAT("write function returned.");
277 /* Free all removed entries */
278 while (m_entries
) g_list_delete_link(m_entries
, g_list_last(m_entries
));
284 /* libsipe interface functions */
286 miranda_sipe_get_current_status(struct sipe_core_private
*sipe_private
, const char* name
)
288 SIPPROTO
*pr
= sipe_private
->public.backend_private
;
289 char *module
= pr
->proto
.m_szModuleName
;
293 return g_strdup(MirandaStatusToSipe(pr
->proto
.m_iStatus
));
295 hContact
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, name
, NULL
);
296 return g_strdup(MirandaStatusToSipe(DBGetContactSettingWord( hContact
, module
, "Status", ID_STATUS_OFFLINE
)));
299 struct sipe_miranda_sel_entry
*
300 sipe_miranda_input_add(HANDLE fd
, sipe_miranda_input_condition cond
, sipe_miranda_input_function func
, gpointer user_data
)
304 struct sipe_miranda_sel_entry
*entry
;
307 m_readhash
= g_hash_table_new(NULL
, NULL
);
310 m_writehash
= g_hash_table_new(NULL
, NULL
);
312 if ((cond
!= SIPE_MIRANDA_INPUT_READ
) && (cond
!= SIPE_MIRANDA_INPUT_WRITE
))
314 SIPE_DEBUG_INFO("Invalid input condition <%d> cond.", cond
);
318 entry
= g_new0(struct sipe_miranda_sel_entry
,1);
319 entry
->sig
= ENTRY_SIG
;
321 entry
->user_data
= user_data
;
324 if (cond
== SIPE_MIRANDA_INPUT_READ
)
326 for ( rcnt
=0 ; m_select
.hReadConns
[rcnt
] && m_select
.hReadConns
[rcnt
]!=(HANDLE
)fd
; rcnt
++ );
327 m_select
.hReadConns
[rcnt
] = (HANDLE
)fd
;
328 g_hash_table_replace( m_readhash
, (gpointer
)fd
, entry
);
330 else if (cond
== SIPE_MIRANDA_INPUT_WRITE
)
332 for ( wcnt
=0 ; m_select
.hWriteConns
[wcnt
] && m_select
.hWriteConns
[wcnt
]!=(HANDLE
)fd
; wcnt
++ );
333 m_select
.hWriteConns
[rcnt
] = (HANDLE
)fd
;
334 g_hash_table_replace( m_writehash
, (gpointer
)fd
, entry
);
338 CloseHandle((HANDLE
) mir_forkthreadex( inputloop
, NULL
, 8192, NULL
));
340 SIPE_DEBUG_INFO_NOFORMAT("Added input handler.");
345 sipe_miranda_input_remove(struct sipe_miranda_sel_entry
*entry
)
351 SIPE_DEBUG_INFO_NOFORMAT("Not a valid entry. NULL.");
355 if (entry
->sig
!= ENTRY_SIG
)
357 SIPE_DEBUG_INFO("Not a valid entry. Sig is <%08x>.", entry
->sig
);
361 if (g_hash_table_lookup(m_readhash
, (gconstpointer
)entry
->fd
) == entry
)
363 for ( cnt
=0 ; m_select
.hReadConns
[cnt
] && m_select
.hReadConns
[cnt
]!=(HANDLE
)entry
->fd
; cnt
++ );
364 for ( ; m_select
.hReadConns
[cnt
] ; cnt
++ ) m_select
.hReadConns
[cnt
] = m_select
.hReadConns
[cnt
+1];
365 g_hash_table_remove(m_readhash
, (gconstpointer
)entry
->fd
);
368 if (g_hash_table_lookup(m_writehash
, (gconstpointer
)entry
->fd
) == entry
)
370 for ( cnt
=0 ; m_select
.hWriteConns
[cnt
] && m_select
.hWriteConns
[cnt
]!=(HANDLE
)entry
->fd
; cnt
++ );
371 for ( ; m_select
.hWriteConns
[cnt
] ; cnt
++ ) m_select
.hWriteConns
[cnt
] = m_select
.hWriteConns
[cnt
+1];
372 g_hash_table_remove(m_writehash
, (gconstpointer
)entry
->fd
);
375 /* Add it to the list of entries that can be freed after the next select
376 * loop in the thread that's handling the actual select
378 g_list_append( m_entries
, entry
);
384 miranda_sipe_request_authorization(struct sipe_core_private
*sipe_private
, const char *who
, const char *alias
,
385 sipe_backend_buddy_request_authorization_cb auth_cb
,
386 sipe_backend_buddy_request_authorization_cb deny_cb
, void *data
)
388 SIPPROTO
*pr
= sipe_private
->public.backend_private
;
390 PROTORECVEVENT pre
= {0};
395 hContact
= sipe_backend_buddy_find( SIPE_CORE_PUBLIC
, who
, NULL
);
398 hContact
= ( HANDLE
)CallService( MS_DB_CONTACT_ADD
, 0, 0 );
399 CallService( MS_PROTO_ADDTOCONTACT
, ( WPARAM
)hContact
,( LPARAM
)pr
->proto
.m_szModuleName
);
400 DBWriteContactSettingByte( hContact
, "CList", "NotOnList", 1 );
401 sipe_miranda_setContactString( pr
, hContact
, SIP_UNIQUEID
, who
); // name
402 sipe_miranda_setContactStringUtf( pr
, hContact
, "Nick", alias
); // server_alias
405 ccs
.szProtoService
= PSR_AUTH
;
407 ccs
.lParam
= (LPARAM
)&pre
;
408 ccs
.hContact
=hContact
= hContact
;
410 pre
.timestamp
= time(NULL
);
411 pre
.lParam
=sizeof(DWORD
)+sizeof(HANDLE
)+strlen(who
)+1+strlen(alias
)+1+1+5;
413 /*blob is: uin(DWORD), hcontact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ), reason(ASCIIZ)*/
414 pCurBlob
=szBlob
=(char *)_alloca(pre
.lParam
);
415 memset(pCurBlob
, 0, sizeof(DWORD
)); pCurBlob
+=sizeof(DWORD
);
416 memcpy(pCurBlob
,&hContact
,sizeof(HANDLE
)); pCurBlob
+=sizeof(HANDLE
);
417 strcpy((char *)pCurBlob
,who
); pCurBlob
+=strlen((char *)pCurBlob
)+1;
418 *pCurBlob
= '\0'; pCurBlob
++;
419 strcpy((char *)pCurBlob
,alias
); pCurBlob
+=strlen((char *)pCurBlob
)+1;
420 *pCurBlob
= '\0'; pCurBlob
++;
421 *pCurBlob
= '\0'; pCurBlob
++;
422 pre
.szMessage
=(char *)szBlob
;
424 CallService(MS_PROTO_CHAINRECV
,0,(LPARAM
)&ccs
);
426 /* TODO: Store callbacks somewhere since miranda has no way to pass them on */
431 miranda_sipe_connection_cleanup(struct sipe_core_private
*sip
)
433 SIPPROTO
*pr
= sip
->public.backend_private
;
438 miranda_sipe_notify_user(SIP_HANDLE sip
, const char *name
, sipe_message_flags flags
, const gchar
*message
)
444 __debuglog(sipe_debug_level level
, const char *fmt
,...)
449 char *str
= DBGetString( NULL
, SIPSIMPLE_PROTOCOL_NAME
, "debuglog");
452 vsnprintf(szText
,sizeof(szText
),fmt
,va
);
456 str
= mir_strdup("c:/sipsimple.log");
458 if (!fopen_s(&fh
, str
, "a")) {
459 fprintf(fh
, "<[%d]> %s", _getpid(), szText
);
466 /****************************************************************************
467 * Struct that defines our interface with libsipe
468 ****************************************************************************/
469 /* Protocol interface functions */
470 int RecvContacts( SIPPROTO
*pr
, HANDLE hContact
, PROTORECVEVENT
* evt
)
476 int RecvFile( SIPPROTO
*pr
, HANDLE hContact
, PROTOFILEEVENT
* evt
)
482 int RecvUrl( SIPPROTO
*pr
, HANDLE hContact
, PROTORECVEVENT
* evt
)
488 int SendContacts( SIPPROTO
*pr
, HANDLE hContact
, int flags
, int nContacts
, HANDLE
* hContactsList
)
491 SIPE_DEBUG_INFO("SendContacts: flags <%x> ncontacts <%x>", flags
, nContacts
);
495 int SendUrl( SIPPROTO
*pr
, HANDLE hContact
, int flags
, const char* url
)
498 SIPE_DEBUG_INFO("SendUrl: iflags <%x> url <%s>", flags
, url
);
502 int SetApparentMode( SIPPROTO
*pr
, HANDLE hContact
, int mode
)
505 SIPE_DEBUG_INFO("SetApparentMode: mode <%x>", mode
);
509 int RecvAwayMsg( SIPPROTO
*pr
, HANDLE hContact
, int mode
, PROTORECVEVENT
* evt
)
512 SIPE_DEBUG_INFO("RecvAwayMsg: mode <%x>", mode
);
516 int SendAwayMsg( SIPPROTO
*pr
, HANDLE hContact
, HANDLE hProcess
, const char* msg
)
519 SIPE_DEBUG_INFO("SendAwayMsg: msg <%s>", msg
);
523 static const char *about_txt
=
524 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fswiss\\fcharset0 Arial;}{\\f1\\fnil\fcharset2 Symbol;}}"
525 "{\\*\\generator Msftedit 5.41.15.1507;}\\viewkind4\\uc1\\pard\\b\\f0\\fs24 Sipe" SIPE_VERSION
"\\fs20\\par"
527 "A third-party plugin implementing extended version of SIP/SIMPLE used by various products:\\par"
528 "\\pard{\\pntext\\f1\\'B7\\tab}{\\*\\pn\\pnlvlblt\\pnf1\\pnindent0{\\pntxtb\'B7}}\\fi-720"
529 "\\li720 MS Office Communications Server 2007 (R2)\\par"
530 "{\\pntext\\f1\\'B7\\tab}MS Live Communications Server 2005/2003\\par"
531 "{\\pntext\\f1\\'B7\\tab}Reuters Messaging\\par"
533 "Home: http://sipe.sourceforge.net\\par "
534 "Support: http://sourceforge.net/projects/sipe/forums/forum/68853\\par "
535 "License: GPLv2\\par "
537 "\\b Authors:\\b0\\par"
538 " - Anibal Avelar\\par"
539 " - Gabriel Burt\\par"
540 " - Stefan Becker\\par"