Fix #3161273: Lost Connection Gives No Error Message (part IV)
[siplcs.git] / src / miranda / sipe-miranda.c
blobd7663cb2298c3afbb4e606a9721ff48c47d4c23c
1 #define MIRANDA_VER 0x900
3 #include <windows.h>
4 #include <win2k.h>
5 #include <Richedit.h>
6 #include <process.h>
8 #include <glib.h>
10 #include "newpluginapi.h"
11 #include "m_protosvc.h"
12 #include "m_protoint.h"
13 #include "m_system.h"
14 #include "m_database.h"
15 #include "m_langpack.h"
16 #include "m_options.h"
17 #include "m_clist.h"
18 #include "m_chat.h"
19 #include "m_netlib.h"
20 #include "m_protomod.h"
22 #include "libsipe.h"
23 #include "sipe-core.h"
24 #include "sipe.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;
65 /* Misc functions */
66 TCHAR _tcharbuf[32768];
67 TCHAR* CHAR2TCHAR( const char *chr ) {
68 #ifdef UNICODE
69 if (!chr) return NULL;
70 mbstowcs( _tcharbuf, chr, strlen(chr)+1 );
71 return _tcharbuf;
72 #else
73 return chr;
74 #endif
77 char _charbuf[32768];
78 char* TCHAR2CHAR( const TCHAR *tchr ) {
79 #ifdef UNICODE
80 if (!tchr) return NULL;
81 wcstombs( _charbuf, tchr, wcslen(tchr)+1 );
82 return _charbuf;
83 #else
84 return tchr;
85 #endif
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);
111 g_free(pArguments);
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) {
134 switch (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;
146 case ID_STATUS_DND:
147 return SIPE_STATUS_ID_DND;
149 case ID_STATUS_NA:
150 return SIPE_STATUS_ID_AWAY;
152 case ID_STATUS_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;
164 default:
165 return SIPE_STATUS_ID_UNKNOWN;
170 int SendBroadcast(SIPPROTO *pr, HANDLE hContact,int type,int result,HANDLE hProcess,LPARAM lParam)
172 ACKDATA ack={0};
174 ack.cbSize = sizeof(ACKDATA);
175 ack.szModule = pr->proto.m_szModuleName;
176 ack.hContact = hContact;
177 ack.type = type;
178 ack.result = result;
179 ack.hProcess = hProcess;
180 ack.lParam = lParam;
181 return CallService(MS_PROTO_BROADCASTACK,0,(LPARAM)&ack);
185 /* Protocol interface support functions */
186 void
187 _debuglog(const char *filename, const char *funcname, const char *fmt,...)
189 va_list va;
190 char szText[32768];
191 const char *tmp;
192 FILE *fh;
194 for ( tmp=filename ; *tmp ; tmp++ )
196 if ((*tmp == '/') || (*tmp == '\\')) filename=tmp+1;
199 va_start(va,fmt);
200 vsnprintf(szText,sizeof(szText),fmt,va);
201 va_end(va);
203 if (!fopen_s(&fh,"c:/sipsimple.log","a")) {
204 fprintf(fh, "[%d] %22s %s: %s", _getpid(), filename, funcname, szText);
205 fclose(fh);
209 typedef struct _time_entry
211 guint interval;
212 GSourceFunc function;
213 gpointer data;
214 HANDLE sem;
215 } time_entry;
217 static unsigned __stdcall
218 inputloop(void* data)
220 int cnt;
221 struct sipe_miranda_sel_entry *entry;
222 INT_PTR lstRes;
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);
232 if (lstRes < 0)
234 SIPE_DEBUG_INFO_NOFORMAT("Connection failed while waiting.");
235 break;
237 else if (lstRes == 0)
239 SIPE_DEBUG_INFO_NOFORMAT("Select Timeout.");
240 lstRes = SOCKET_ERROR;
242 else
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]);
251 if (!entry)
253 SIPE_DEBUG_INFO_NOFORMAT("ERROR: no read handler found.");
254 continue;
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]);
266 if (!entry)
268 SIPE_DEBUG_INFO_NOFORMAT("ERROR: no write handler found.");
269 continue;
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));
281 return 0;
284 /* libsipe interface functions */
285 static char*
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;
290 HANDLE hContact;
292 if (!name)
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)
302 int rcnt = 0;
303 int wcnt = 0;
304 struct sipe_miranda_sel_entry *entry;
306 if (!m_readhash)
307 m_readhash = g_hash_table_new(NULL, NULL);
309 if (!m_writehash)
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);
315 return 0;
318 entry = g_new0(struct sipe_miranda_sel_entry,1);
319 entry->sig = ENTRY_SIG;
320 entry->func = func;
321 entry->user_data = user_data;
322 entry->fd = fd;
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 );
337 if (!(rcnt+wcnt))
338 CloseHandle((HANDLE) mir_forkthreadex( inputloop, NULL, 8192, NULL ));
340 SIPE_DEBUG_INFO_NOFORMAT("Added input handler.");
341 return entry;
344 gboolean
345 sipe_miranda_input_remove(struct sipe_miranda_sel_entry *entry)
347 int cnt;
349 if (!entry)
351 SIPE_DEBUG_INFO_NOFORMAT("Not a valid entry. NULL.");
352 return FALSE;
355 if (entry->sig != ENTRY_SIG)
357 SIPE_DEBUG_INFO("Not a valid entry. Sig is <%08x>.", entry->sig);
358 return FALSE;
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 );
380 return TRUE;
383 static void*
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;
389 CCSDATA ccs;
390 PROTORECVEVENT pre = {0};
391 HANDLE hContact;
392 char* szBlob;
393 char* pCurBlob;
395 hContact = sipe_backend_buddy_find( SIPE_CORE_PUBLIC, who, NULL );
396 if (!hContact)
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;
406 ccs.wParam = 0;
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 */
427 return NULL;
430 static void
431 miranda_sipe_connection_cleanup(struct sipe_core_private *sip)
433 SIPPROTO *pr = sip->public.backend_private;
434 _NIF();
437 static void
438 miranda_sipe_notify_user(SIP_HANDLE sip, const char *name, sipe_message_flags flags, const gchar *message)
440 _NIF();
443 static void
444 __debuglog(sipe_debug_level level, const char *fmt,...)
446 va_list va;
447 char szText[32768];
448 FILE *fh;
449 char *str = DBGetString( NULL, SIPSIMPLE_PROTOCOL_NAME, "debuglog");
451 va_start(va,fmt);
452 vsnprintf(szText,sizeof(szText),fmt,va);
453 va_end(va);
455 if (!str)
456 str = mir_strdup("c:/sipsimple.log");
458 if (!fopen_s(&fh, str, "a")) {
459 fprintf(fh, "<[%d]> %s", _getpid(), szText);
460 fclose(fh);
462 mir_free(str);
466 /****************************************************************************
467 * Struct that defines our interface with libsipe
468 ****************************************************************************/
469 /* Protocol interface functions */
470 int RecvContacts( SIPPROTO *pr, HANDLE hContact, PROTORECVEVENT* evt )
472 _NIF();
473 return 0;
476 int RecvFile( SIPPROTO *pr, HANDLE hContact, PROTOFILEEVENT* evt )
478 _NIF();
479 return 0;
482 int RecvUrl( SIPPROTO *pr, HANDLE hContact, PROTORECVEVENT* evt )
484 _NIF();
485 return 0;
488 int SendContacts( SIPPROTO *pr, HANDLE hContact, int flags, int nContacts, HANDLE* hContactsList )
490 _NIF();
491 SIPE_DEBUG_INFO("SendContacts: flags <%x> ncontacts <%x>", flags, nContacts);
492 return 0;
495 int SendUrl( SIPPROTO *pr, HANDLE hContact, int flags, const char* url )
497 _NIF();
498 SIPE_DEBUG_INFO("SendUrl: iflags <%x> url <%s>", flags, url);
499 return 0;
502 int SetApparentMode( SIPPROTO *pr, HANDLE hContact, int mode )
504 _NIF();
505 SIPE_DEBUG_INFO("SetApparentMode: mode <%x>", mode);
506 return 0;
509 int RecvAwayMsg( SIPPROTO *pr, HANDLE hContact, int mode, PROTORECVEVENT* evt )
511 _NIF();
512 SIPE_DEBUG_INFO("RecvAwayMsg: mode <%x>", mode);
513 return 0;
516 int SendAwayMsg( SIPPROTO *pr, HANDLE hContact, HANDLE hProcess, const char* msg )
518 _NIF();
519 SIPE_DEBUG_INFO("SendAwayMsg: msg <%s>", msg);
520 return 0;
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"
526 "\\b0\\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"
532 "\\pard\\par "
533 "Home: http://sipe.sourceforge.net\\par "
534 "Support: http://sourceforge.net/projects/sipe/forums/forum/68853\\par "
535 "License: GPLv2\\par "
536 "\\par"
537 "\\b Authors:\\b0\\par"
538 " - Anibal Avelar\\par"
539 " - Gabriel Burt\\par"
540 " - Stefan Becker\\par"
541 " - pier11\\par"
542 "}";
545 /* Dialogs */
546 /* Event handlers */