Sync up miranda files with current libsipe branch
[siplcs.git] / src / miranda / sipe-miranda.c
blobb3f32c67526f41e6ac6dd2d6ff6afb6cfc3d0f87
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 mbstowcs( _tcharbuf, chr, strlen(chr)+1 );
70 return _tcharbuf;
71 #else
72 return chr;
73 #endif
76 char _charbuf[32768];
77 char* TCHAR2CHAR( const TCHAR *tchr ) {
78 #ifdef UNICODE
79 wcstombs( _charbuf, tchr, wcslen(tchr)+1 );
80 return _charbuf;
81 #else
82 return tchr;
83 #endif
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);
109 g_free(pArguments);
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) {
132 switch (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;
144 case ID_STATUS_DND:
145 return SIPE_STATUS_ID_DND;
147 case ID_STATUS_NA:
148 return SIPE_STATUS_ID_AWAY;
150 case ID_STATUS_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;
162 default:
163 return SIPE_STATUS_ID_UNKNOWN;
168 int SendBroadcast(SIPPROTO *pr, HANDLE hContact,int type,int result,HANDLE hProcess,LPARAM lParam)
170 ACKDATA ack={0};
172 ack.cbSize = sizeof(ACKDATA);
173 ack.szModule = pr->proto.m_szModuleName;
174 ack.hContact = hContact;
175 ack.type = type;
176 ack.result = result;
177 ack.hProcess = hProcess;
178 ack.lParam = lParam;
179 return CallService(MS_PROTO_BROADCASTACK,0,(LPARAM)&ack);
183 /* Protocol interface support functions */
184 void
185 _debuglog(const char *filename, const char *funcname, const char *fmt,...)
187 va_list va;
188 char szText[32768];
189 const char *tmp;
190 FILE *fh;
192 for ( tmp=filename ; *tmp ; tmp++ )
194 if ((*tmp == '/') || (*tmp == '\\')) filename=tmp+1;
197 va_start(va,fmt);
198 vsnprintf(szText,sizeof(szText),fmt,va);
199 va_end(va);
201 if (!fopen_s(&fh,"c:/sipsimple.log","a")) {
202 fprintf(fh, "[%d] %22s %s: %s", _getpid(), filename, funcname, szText);
203 fclose(fh);
207 typedef struct _time_entry
209 guint interval;
210 GSourceFunc function;
211 gpointer data;
212 HANDLE sem;
213 } time_entry;
215 static unsigned __stdcall
216 inputloop(void* data)
218 int cnt;
219 struct sipe_miranda_sel_entry *entry;
220 INT_PTR lstRes;
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);
230 if (lstRes < 0)
232 SIPE_DEBUG_INFO_NOFORMAT("Connection failed while waiting.");
233 break;
235 else if (lstRes == 0)
237 SIPE_DEBUG_INFO_NOFORMAT("Select Timeout.");
238 lstRes = SOCKET_ERROR;
240 else
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]);
249 if (!entry)
251 SIPE_DEBUG_INFO_NOFORMAT("ERROR: no read handler found.");
252 continue;
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]);
264 if (!entry)
266 SIPE_DEBUG_INFO_NOFORMAT("ERROR: no write handler found.");
267 continue;
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));
279 return 0;
282 /* libsipe interface functions */
283 static char*
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;
288 HANDLE hContact;
290 if (!name)
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)
300 int rcnt = 0;
301 int wcnt = 0;
302 struct sipe_miranda_sel_entry *entry;
304 if (!m_readhash)
305 m_readhash = g_hash_table_new(NULL, NULL);
307 if (!m_writehash)
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);
313 return 0;
316 entry = g_new0(struct sipe_miranda_sel_entry,1);
317 entry->sig = ENTRY_SIG;
318 entry->func = func;
319 entry->user_data = user_data;
320 entry->fd = fd;
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 );
335 if (!(rcnt+wcnt))
336 CloseHandle((HANDLE) mir_forkthreadex( inputloop, NULL, 8192, NULL ));
338 SIPE_DEBUG_INFO_NOFORMAT("Added input handler.");
339 return entry;
342 gboolean
343 sipe_miranda_input_remove(struct sipe_miranda_sel_entry *entry)
345 int cnt;
347 if (!entry)
349 SIPE_DEBUG_INFO_NOFORMAT("Not a valid entry. NULL.");
350 return FALSE;
353 if (entry->sig != ENTRY_SIG)
355 SIPE_DEBUG_INFO("Not a valid entry. Sig is <%08x>.", entry->sig);
356 return FALSE;
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 );
378 return TRUE;
381 static void*
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;
387 CCSDATA ccs;
388 PROTORECVEVENT pre = {0};
389 HANDLE hContact;
390 char* szBlob;
391 char* pCurBlob;
393 hContact = sipe_backend_buddy_find( SIPE_CORE_PUBLIC, who, NULL );
394 if (!hContact)
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;
404 ccs.wParam = 0;
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 */
425 return NULL;
428 static void
429 miranda_sipe_connection_cleanup(struct sipe_core_private *sip)
431 SIPPROTO *pr = sip->public.backend_private;
432 _NIF();
435 static void
436 miranda_sipe_notify_user(SIP_HANDLE sip, const char *name, sipe_message_flags flags, const gchar *message)
438 _NIF();
441 static void
442 __debuglog(sipe_debug_level level, const char *fmt,...)
444 va_list va;
445 char szText[32768];
446 FILE *fh;
447 char *str = DBGetString( NULL, SIPSIMPLE_PROTOCOL_NAME, "debuglog");
449 va_start(va,fmt);
450 vsnprintf(szText,sizeof(szText),fmt,va);
451 va_end(va);
453 if (!str)
454 str = mir_strdup("c:/sipsimple.log");
456 if (!fopen_s(&fh, str, "a")) {
457 fprintf(fh, "<[%d]> %s", _getpid(), szText);
458 fclose(fh);
460 mir_free(str);
464 /****************************************************************************
465 * Struct that defines our interface with libsipe
466 ****************************************************************************/
467 /* Protocol interface functions */
468 int RecvContacts( SIPPROTO *pr, HANDLE hContact, PROTORECVEVENT* evt )
470 _NIF();
471 return 0;
474 int RecvFile( SIPPROTO *pr, HANDLE hContact, PROTOFILEEVENT* evt )
476 _NIF();
477 return 0;
480 int RecvUrl( SIPPROTO *pr, HANDLE hContact, PROTORECVEVENT* evt )
482 _NIF();
483 return 0;
486 int SendContacts( SIPPROTO *pr, HANDLE hContact, int flags, int nContacts, HANDLE* hContactsList )
488 _NIF();
489 SIPE_DEBUG_INFO("SendContacts: flags <%x> ncontacts <%x>", flags, nContacts);
490 return 0;
493 int SendUrl( SIPPROTO *pr, HANDLE hContact, int flags, const char* url )
495 _NIF();
496 SIPE_DEBUG_INFO("SendUrl: iflags <%x> url <%s>", flags, url);
497 return 0;
500 int SetApparentMode( SIPPROTO *pr, HANDLE hContact, int mode )
502 _NIF();
503 SIPE_DEBUG_INFO("SetApparentMode: mode <%x>", mode);
504 return 0;
507 int RecvAwayMsg( SIPPROTO *pr, HANDLE hContact, int mode, PROTORECVEVENT* evt )
509 _NIF();
510 SIPE_DEBUG_INFO("RecvAwayMsg: mode <%x>", mode);
511 return 0;
514 int SendAwayMsg( SIPPROTO *pr, HANDLE hContact, HANDLE hProcess, const char* msg )
516 _NIF();
517 SIPE_DEBUG_INFO("SendAwayMsg: msg <%s>", msg);
518 return 0;
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"
524 "\\b0\\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"
530 "\\pard\\par "
531 "Home: http://sipe.sourceforge.net\\par "
532 "Support: http://sourceforge.net/projects/sipe/forums/forum/68853\\par "
533 "License: GPLv2\\par "
534 "\\par"
535 "\\b Authors:\\b0\\par"
536 " - Anibal Avelar\\par"
537 " - Gabriel Burt\\par"
538 " - Stefan Becker\\par"
539 " - pier11\\par"
540 "}";
543 /* Dialogs */
544 /* Event handlers */