2 * @file miranda-utils.c
6 * Copyright (C) 2010-11 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "miranda-version.h"
26 #include "newpluginapi.h"
27 #include "m_protosvc.h"
28 #include "m_protoint.h"
29 #include "m_database.h"
31 #include "m_langpack.h"
32 #include "m_protomod.h"
35 #include "sipe-backend.h"
36 #include "sipe-core.h"
37 #include "miranda-private.h"
40 * Table to hold HTML entities we want to convert
42 static GHashTable
*entities
= NULL
;
46 * Various shortcut functions to get database values
49 sipe_miranda_getGlobalString(const gchar
* name
)
51 return DBGetString( NULL
, SIPSIMPLE_PROTOCOL_NAME
, name
);
55 sipe_miranda_getContactString(const SIPPROTO
*pr
, HANDLE hContact
, const gchar
* name
)
57 return DBGetString( hContact
, pr
->proto
.m_szModuleName
, name
);
61 sipe_miranda_getString(const SIPPROTO
*pr
, const gchar
* name
)
63 return sipe_miranda_getContactString( pr
, NULL
, name
);
67 sipe_miranda_getDword(const SIPPROTO
*pr
, HANDLE hContact
, const gchar
* name
, DWORD
* rv
)
70 DBCONTACTGETSETTING cgs
;
72 cgs
.szModule
= pr
->proto
.m_szModuleName
;
75 if(CallService(MS_DB_CONTACT_GETSETTING
,(WPARAM
)hContact
,(LPARAM
)&cgs
))
88 sipe_miranda_getGlobalWord(const gchar
* name
, WORD
* rv
)
91 DBCONTACTGETSETTING cgs
;
93 cgs
.szModule
= SIPSIMPLE_PROTOCOL_NAME
;
96 if(CallService(MS_DB_CONTACT_GETSETTING
, (WPARAM
)NULL
,(LPARAM
)&cgs
))
109 sipe_miranda_getWord(const SIPPROTO
*pr
, HANDLE hContact
, const gchar
* name
, WORD
* rv
)
112 DBCONTACTGETSETTING cgs
;
114 cgs
.szModule
= pr
->proto
.m_szModuleName
;
115 cgs
.szSetting
= name
;
117 if(CallService(MS_DB_CONTACT_GETSETTING
,(WPARAM
)hContact
,(LPARAM
)&cgs
))
130 sipe_miranda_getBool(const SIPPROTO
*pr
, const gchar
*name
, gboolean defval
)
134 if (sipe_miranda_getWord( pr
, NULL
, name
, &ret
))
135 return ret
?TRUE
:FALSE
;
141 sipe_miranda_getStaticString(const SIPPROTO
*pr
, HANDLE hContact
, const gchar
* valueName
, gchar
* dest
, unsigned dest_len
)
144 DBCONTACTGETSETTING sVal
;
147 dbv
.cchVal
= (WORD
)dest_len
;
148 dbv
.type
= DBVT_ASCIIZ
;
151 sVal
.szModule
= pr
->proto
.m_szModuleName
;
152 sVal
.szSetting
= valueName
;
153 if (CallService(MS_DB_CONTACT_GETSETTINGSTATIC
, (WPARAM
)hContact
, (LPARAM
)&sVal
) != 0)
156 return (dbv
.type
!= DBVT_ASCIIZ
);
160 * Various shortcut functions to set database values
163 sipe_miranda_setGlobalString(const gchar
* name
, const gchar
* value
)
165 DBWriteContactSettingString(NULL
, SIPSIMPLE_PROTOCOL_NAME
, name
, value
);
169 sipe_miranda_setGlobalStringUtf(const gchar
* valueName
, const gchar
* parValue
)
171 DBWriteContactSettingStringUtf( NULL
, SIPSIMPLE_PROTOCOL_NAME
, valueName
, parValue
);
175 sipe_miranda_setContactString(const SIPPROTO
*pr
, HANDLE hContact
, const gchar
* name
, const gchar
* value
)
177 DBWriteContactSettingString(hContact
, pr
->proto
.m_szModuleName
, name
, value
);
181 sipe_miranda_setContactStringUtf(const SIPPROTO
*pr
, HANDLE hContact
, const gchar
* valueName
, const gchar
* parValue
)
183 DBWriteContactSettingStringUtf( hContact
, pr
->proto
.m_szModuleName
, valueName
, parValue
);
187 sipe_miranda_setString(const SIPPROTO
*pr
, const gchar
* name
, const gchar
* value
)
189 sipe_miranda_setContactString( pr
, NULL
, name
, value
);
193 sipe_miranda_setStringUtf(const SIPPROTO
*pr
, const gchar
* name
, const gchar
* value
)
195 sipe_miranda_setContactStringUtf( pr
, NULL
, name
, value
);
199 sipe_miranda_setGlobalWord(const gchar
* szSetting
, WORD wValue
)
201 return DBWriteContactSettingWord(NULL
, SIPSIMPLE_PROTOCOL_NAME
, szSetting
, wValue
);
205 sipe_miranda_setWord(const SIPPROTO
*pr
, HANDLE hContact
, const gchar
* szSetting
, WORD wValue
)
207 return DBWriteContactSettingWord(hContact
, pr
->proto
.m_szModuleName
, szSetting
, wValue
);
211 sipe_miranda_setBool(const SIPPROTO
*pr
, const gchar
*name
, gboolean value
)
213 return DBWriteContactSettingWord(NULL
, pr
->proto
.m_szModuleName
, name
, value
?1:0);
217 * Initialize our table of HTML entities
219 #define ADDENT(a,b) g_hash_table_insert(entities, a, b)
223 entities
= g_hash_table_new(g_str_hash
, g_str_equal
);
233 * WARNING: Returns miranda-allocated string, not glib one
236 sipe_miranda_eliminate_html(const gchar
*string
, int len
)
238 gchar
*tmp
= (char*)mir_alloc(len
+ 1);
246 for (i
=0,j
=0;i
<len
;i
++)
248 if (!tag
&& string
[i
] == '<')
250 if ((i
+ 4 <= len
) && (!_strnicmp(string
+ i
, "<br>", 4) || !_strnicmp(string
+ i
, "<br/>", 5)))
259 else if (tag
&& string
[i
] == '>')
267 if ((string
[i
] == '&') && (tkend
= strstr((char*)&string
[i
], ";")))
273 rep
= (char*)g_hash_table_lookup(entities
, &string
[i
+1]);
277 strcpy(&tmp
[j
], rep
);
279 i
+= strlen(&string
[i
]);
304 sipe_miranda_network_get_port_from_fd( HANDLE fd
)
306 SOCKET sock
= CallService(MS_NETLIB_GETSOCKET
, (WPARAM
)fd
, (LPARAM
)0);
308 struct sockaddr_in sockbuf
;
309 int namelen
= sizeof(sockbuf
);
310 getsockname(sock
, (struct sockaddr
*)&sockbuf
, &namelen
);
311 SIPE_DEBUG_INFO("<%x> <%x><%x><%s>", namelen
, sockbuf
.sin_family
, sockbuf
.sin_port
, inet_ntoa(sockbuf
.sin_addr
) );
313 return sockbuf
.sin_port
;
317 TCHAR _tcharbuf
[32768];
318 TCHAR
* CHAR2TCHAR( const char *chr
) {
320 if (!chr
) return NULL
;
321 mbstowcs( _tcharbuf
, chr
, strlen(chr
)+1 );
328 char _charbuf
[32768];
329 char* TCHAR2CHAR( const TCHAR
*tchr
) {
331 if (!tchr
) return NULL
;
332 wcstombs( _charbuf
, tchr
, wcslen(tchr
)+1 );
340 sipe_miranda_AddEvent(const SIPPROTO
*pr
, HANDLE hContact
, WORD wType
, DWORD dwTime
, DWORD flags
, DWORD cbBlob
, PBYTE pBlob
)
342 DBEVENTINFO dbei
= {0};
344 dbei
.cbSize
= sizeof(dbei
);
345 dbei
.szModule
= pr
->proto
.m_szModuleName
;
346 dbei
.timestamp
= dwTime
;
348 dbei
.eventType
= wType
;
349 dbei
.cbBlob
= cbBlob
;
352 return (HANDLE
)CallService(MS_DB_EVENT_ADD
, (WPARAM
)hContact
, (LPARAM
)&dbei
);
360 static unsigned __stdcall
361 msgboxThread(void* arg
)
363 struct msgboxinfo
*err
= (struct msgboxinfo
*)arg
;
367 MessageBox(NULL
, err
->msg
, err
->caption
, MB_OK
);
369 mir_free(err
->caption
);
375 sipe_miranda_msgbox(const char *msg
, const char *caption
)
377 struct msgboxinfo
*info
= g_new(struct msgboxinfo
,1);
379 info
->msg
= mir_a2t(msg
);
380 info
->caption
= mir_a2t(caption
);
382 CloseHandle((HANDLE
) mir_forkthreadex( msgboxThread
, info
, 8192, NULL
));
385 char* sipe_miranda_acktype_strings
[] = {
386 "ACKTYPE_MESSAGE", "ACKTYPE_URL", "ACKTYPE_FILE",
387 "ACKTYPE_CHAT", "ACKTYPE_AWAYMSG", "ACKTYPE_AUTHREQ",
388 "ACKTYPE_ADDED", "ACKTYPE_GETINFO", "ACKTYPE_SETINFO",
389 "ACKTYPE_LOGIN", "ACKTYPE_SEARCH", "ACKTYPE_NEWUSER",
390 "ACKTYPE_STATUS", "ACKTYPE_CONTACTS", "ACKTYPE_AVATAR",
393 char* sipe_miranda_ackresult_strings
[] = {
394 "ACKRESULT_SUCCESS", "ACKRESULT_FAILED", "ACKRESULT_CONNECTING",
395 "ACKRESULT_CONNECTED", "ACKRESULT_INITIALISING", "ACKRESULT_SENTREQUEST",
396 "ACKRESULT_DATA", "ACKRESULT_NEXTFILE", "ACKRESULT_FILERESUME",
397 "ACKRESULT_DENIED", "ACKRESULT_STATUS", "ACKRESULT_LISTENING",
398 "ACKRESULT_CONNECTPROXY", "ACKRESULT_SEARCHRESULT" };
401 sipe_miranda_SendBroadcast(SIPPROTO
*pr
, HANDLE hContact
,int type
,int result
,HANDLE hProcess
,LPARAM lParam
)
405 ack
.cbSize
= sizeof(ACKDATA
);
406 ack
.szModule
= pr
->proto
.m_szModuleName
;
407 ack
.hContact
= hContact
;
410 ack
.hProcess
= hProcess
;
413 SIPE_DEBUG_INFO("broadcasting contact <%08x> type <%d:%s> result <%d:%s> par1 <%08x> par2 <%08x>",
415 type
, sipe_miranda_acktype_strings
[type
],
416 result
, sipe_miranda_ackresult_strings
[result
>99 ? result
-98 : result
],
419 return CallServiceSync(MS_PROTO_BROADCASTACK
,0,(LPARAM
)&ack
);
422 struct sipe_miranda_connection_info
{
428 void (*callback
)(HANDLE fd
, void *data
, const gchar
*reason
);
431 /* Private. For locking only */
437 static void __stdcall
438 connection_cb_async(void *data
)
440 struct sipe_miranda_connection_info
*entry
= (struct sipe_miranda_connection_info
*)data
;
441 SIPE_DEBUG_INFO("[C:%08x] Calling real connected function", entry
);
442 entry
->callback(entry
->fd
, entry
->data
, entry
->reason
);
443 SetEvent(entry
->hDoneEvent
);
446 static unsigned __stdcall
447 sipe_miranda_connected_callback(void* data
)
449 struct sipe_miranda_connection_info
*info
= (struct sipe_miranda_connection_info
*)data
;
450 SIPPROTO
*pr
= info
->pr
;
451 NETLIBOPENCONNECTION ncon
= {0};
453 ncon
.cbSize
= sizeof(ncon
);
454 ncon
.flags
= NLOCF_V2
;
455 ncon
.szHost
= info
->server_name
;
456 ncon
.wPort
= info
->server_port
;
457 ncon
.timeout
= info
->timeout
;
459 info
->fd
= (HANDLE
)CallService(MS_NETLIB_OPENCONNECTION
, (WPARAM
)pr
->m_hServerNetlibUser
, (LPARAM
)&ncon
);
460 if (info
->fd
== NULL
) {
461 SIPE_DEBUG_INFO("[C:%08x] Connection to <%s:%d> failed", info
, info
->server_name
, info
->server_port
);
462 info
->reason
= "Connection failed";
465 SIPE_DEBUG_INFO("[C:%08x] connected <%d>", info
, (int)info
->fd
);
469 if (!CallService(MS_NETLIB_STARTSSL
, (WPARAM
)info
->fd
, 0))
471 Netlib_CloseHandle(info
->fd
);
473 info
->reason
= "Failed to enabled SSL";
475 SIPE_DEBUG_INFO("[C:%08x] SSL enabled", info
);
482 info
->hDoneEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
483 CallFunctionAsync(connection_cb_async
, info
);
484 WaitForSingleObject(info
->hDoneEvent
, INFINITE
);
485 CloseHandle(info
->hDoneEvent
);
487 g_free(info
->server_name
);
492 struct sipe_miranda_connection_info
*
493 sipe_miranda_connect(SIPPROTO
*pr
,
498 void (*callback
)(HANDLE fd
, void *data
, const gchar
*reason
),
501 struct sipe_miranda_connection_info
*info
= g_new0(struct sipe_miranda_connection_info
, 1);
502 SIPE_DEBUG_INFO("[C:%08x] Connecting to <%s:%d> tls <%d> timeout <%d>", info
, host
, port
, tls
, timeout
);
505 info
->server_name
= g_strdup(host
);
506 info
->server_port
= port
;
507 info
->timeout
= timeout
;
509 info
->callback
= callback
;
512 CloseHandle((HANDLE
) mir_forkthreadex( sipe_miranda_connected_callback
, info
, 65536, NULL
));
517 struct sipe_miranda_ack_args
524 const gchar
*modname
;
527 static unsigned __stdcall
528 ProtocolAckThread(struct sipe_miranda_ack_args
* args
)
530 ProtoBroadcastAck(args
->modname
, args
->hContact
, args
->nAckType
, args
->nAckResult
, args
->hSequence
, (LPARAM
)args
->pszMessage
);
532 if (args
->nAckResult
== ACKRESULT_SUCCESS
)
533 SIPE_DEBUG_INFO_NOFORMAT("ProtocolAckThread: Sent ACK");
534 else if (args
->nAckResult
== ACKRESULT_FAILED
)
535 SIPE_DEBUG_INFO_NOFORMAT("ProtocolAckThread: Sent NACK");
537 g_free(args
->pszMessage
);
543 sipe_miranda_SendProtoAck( SIPPROTO
*pr
, HANDLE hContact
, DWORD dwCookie
, int nAckResult
, int nAckType
, const char* pszMessage
)
545 struct sipe_miranda_ack_args
* pArgs
= g_new0(struct sipe_miranda_ack_args
, 1);
547 pArgs
->hContact
= hContact
;
548 pArgs
->hSequence
= (HANDLE
)dwCookie
;
549 pArgs
->nAckResult
= nAckResult
;
550 pArgs
->nAckType
= nAckType
;
551 pArgs
->pszMessage
= g_strdup(pszMessage
);
552 pArgs
->modname
= pr
->proto
.m_szModuleName
;
554 CloseHandle((HANDLE
) mir_forkthreadex(ProtocolAckThread
, pArgs
, 65536, NULL
));
558 sipe_miranda_cmd(gchar
*cmd
, gchar
*buf
, DWORD
*maxlen
)
560 STARTUPINFOA si
= {0};
561 PROCESS_INFORMATION pi
= {0};
562 SECURITY_ATTRIBUTES sa
= {0};
565 sa
.nLength
= sizeof(sa
);
566 sa
.bInheritHandle
= TRUE
;
568 if (!CreatePipe(&rd
, &wr
, &sa
, 0))
570 SIPE_DEBUG_INFO_NOFORMAT("Could not create pipe");
574 SetHandleInformation(rd
, HANDLE_FLAG_INHERIT
, 0);
577 si
.dwFlags
= STARTF_USESTDHANDLES
;
580 si
.hStdInput
= GetStdHandle(STD_INPUT_HANDLE
);
582 if (!CreateProcessA(NULL
, cmd
, NULL
, NULL
, TRUE
, CREATE_NO_WINDOW
, NULL
, NULL
, &si
, &pi
))
584 SIPE_DEBUG_INFO("Could not run child program <%s> (%d)", cmd
, GetLastError());
588 CloseHandle(pi
.hThread
);
589 CloseHandle(pi
.hProcess
);
591 if (!ReadFile(rd
, buf
, *maxlen
, maxlen
, NULL
))
593 SIPE_DEBUG_INFO("Could not read from child program <%s>", cmd
);
601 sipe_miranda_html2rtf(const gchar
*text
)
603 const gchar
*intro
= "{\\rtf1\\ansi";
604 const gchar
*link1
= "{\\field{\\*\\fldinst{HYPERLINK \"";
605 const gchar
*link2
= "}}{\\fldrslt {\\ul\\cf2 ";
606 gchar
*tmp
= g_malloc(strlen(text
)+1);
607 int maxlen
= strlen(text
);
608 const gchar
*i
= text
;
610 gboolean skiptag
= FALSE
;
611 gboolean escape
= FALSE
;
612 gboolean copystring
= FALSE
;
613 gboolean link_stage2
= FALSE
;
615 strncpy(tmp
+j
, intro
, maxlen
-j
);
620 if (j
+100>=maxlen
) /* 100 is max substitution size */
623 tmp
= g_realloc(tmp
, maxlen
);
625 if (skiptag
&& !escape
&& *i
!= '>') {
627 } else if (skiptag
&& !escape
) {
630 } else if (copystring
) {
631 if (!escape
&& *i
== '"') copystring
= FALSE
;
632 if (escape
) escape
= FALSE
;
633 else if (*i
== '\\') escape
= TRUE
;
637 } else if (link_stage2
) {
638 strcpy(tmp
+j
, link2
);
642 } else if (g_str_has_prefix(i
,"<br/>")) {
643 strcpy(tmp
+j
, "\\par\n");
646 } else if (g_str_has_prefix(i
,"<b>")) {
647 strcpy(tmp
+j
, "\\b");
650 } else if (g_str_has_prefix(i
,"</b>")) {
651 strcpy(tmp
+j
, "\\b0");
654 } else if (g_str_has_prefix(i
,"<font size=\"")) {
655 strcpy(tmp
+j
, "\\fs36");
659 } else if (g_str_has_prefix(i
,"</font>")) {
660 strcpy(tmp
+j
, "\\fs20");
663 } else if (g_str_has_prefix(i
,"<a href=\"")) {
664 strcpy(tmp
+j
, link1
);
669 } else if (g_str_has_prefix(i
,"</a>")) {
670 strcpy(tmp
+j
, "}}}\\cf0 ");
673 } else if (*i
== '<') {
678 } else if (*i
== '\\') {
691 tmp
= g_realloc(tmp
, j
);
695 int SipeStatusToMiranda(guint activity
) {
699 case SIPE_ACTIVITY_OFFLINE
:
700 return ID_STATUS_OFFLINE
;
701 case SIPE_ACTIVITY_AVAILABLE
:
702 return ID_STATUS_ONLINE
;
703 case SIPE_ACTIVITY_ON_PHONE
:
704 return ID_STATUS_ONTHEPHONE
;
705 case SIPE_ACTIVITY_DND
:
706 case SIPE_ACTIVITY_URGENT_ONLY
:
707 return ID_STATUS_DND
;
708 case SIPE_ACTIVITY_AWAY
:
709 case SIPE_ACTIVITY_OOF
:
711 case SIPE_ACTIVITY_LUNCH
:
712 return ID_STATUS_OUTTOLUNCH
;
713 case SIPE_ACTIVITY_BUSY
:
714 case SIPE_ACTIVITY_IN_MEETING
:
715 case SIPE_ACTIVITY_IN_CONF
:
716 return ID_STATUS_OCCUPIED
;
717 case SIPE_ACTIVITY_INVISIBLE
:
718 return ID_STATUS_INVISIBLE
;
719 case SIPE_ACTIVITY_BRB
:
720 return ID_STATUS_AWAY
;
721 case SIPE_ACTIVITY_UNSET
:
722 return ID_STATUS_OFFLINE
;
723 case SIPE_ACTIVITY_INACTIVE
:
724 case SIPE_ACTIVITY_ONLINE
:
725 case SIPE_ACTIVITY_BUSYIDLE
:
726 return ID_STATUS_ONLINE
;
728 /* None of those? We'll have to guess. Online seems ok. */
729 return ID_STATUS_ONLINE
;
732 /* Don't have SIPE equivalent of these:
738 guint
MirandaStatusToSipe(int status
) {
742 case ID_STATUS_OFFLINE
:
743 return SIPE_ACTIVITY_OFFLINE
;
745 case ID_STATUS_ONLINE
:
746 case ID_STATUS_FREECHAT
:
747 return SIPE_ACTIVITY_AVAILABLE
;
749 case ID_STATUS_ONTHEPHONE
:
750 return SIPE_ACTIVITY_ON_PHONE
;
753 return SIPE_ACTIVITY_DND
;
756 return SIPE_ACTIVITY_AWAY
;
759 return SIPE_ACTIVITY_BRB
;
761 case ID_STATUS_OUTTOLUNCH
:
762 return SIPE_ACTIVITY_LUNCH
;
764 case ID_STATUS_OCCUPIED
:
765 return SIPE_ACTIVITY_BUSY
;
767 case ID_STATUS_INVISIBLE
:
768 return SIPE_ACTIVITY_INVISIBLE
;
771 return SIPE_ACTIVITY_UNSET
;
776 gchar
*sipe_miranda_uri_self(SIPPROTO
*pr
) {
777 gchar
*username
= sipe_miranda_getString(pr
, "username");
778 gchar
*uri
= g_strdup_printf("sip:%s", username
);