6 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
7 * Copyright (C) 2007 Anibal Avelar <avelar@gmail.com>
8 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
11 * Thanks to Google's Summer of Code Program and the helpful mentors
14 * Session-based SIP MESSAGE documentation:
15 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #include <sys/socket.h>
34 #include <sys/ioctl.h>
35 #include <sys/types.h>
36 #include <netinet/in.h>
40 # define _(String) ((const char *) gettext (String))
42 # define _(String) ((const char *) (String))
43 #endif /* ENABLE_NLS */
48 #define _LIBC_INTERNAL_
61 #include "accountopt.h"
63 #include "conversation.h"
80 #endif /*USE_KERBEROS*/
83 #include "sipe-sign.h"
87 /* Keep in sync with sipe_transport_type! */
88 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
89 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
93 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
96 static gchar
*get_epid()
98 return sipe_uuid_get_macaddr();
101 static char *genbranch()
103 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
104 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
105 rand() & 0xFFFF, rand() & 0xFFFF);
108 static char *gencallid()
110 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
111 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
112 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
113 rand() & 0xFFFF, rand() & 0xFFFF);
116 static gchar
*find_tag(const gchar
*hdr
)
118 gchar
* tag
= sipmsg_find_part_of_header (hdr
, "tag=", ";", NULL
);
120 // In case it's at the end and there's no trailing ;
121 tag
= sipmsg_find_part_of_header (hdr
, "tag=", NULL
, NULL
);
127 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
132 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
134 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
136 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
137 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
140 static void sipe_close(PurpleConnection
*gc
);
142 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, const char * buddy_name
);
143 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
);
144 static void send_presence_info(struct sipe_account_data
*sip
);
146 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
148 static void sipe_keep_alive_timeout(struct sipe_account_data
*sip
, const gchar
*hdr
)
150 gchar
*timeout
= sipmsg_find_part_of_header(hdr
, "timeout=", ";", NULL
);
151 if (timeout
!= NULL
) {
152 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
153 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
154 sip
->keepalive_timeout
);
159 static void sipe_keep_alive(PurpleConnection
*gc
)
161 struct sipe_account_data
*sip
= gc
->proto_data
;
162 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
163 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
164 gchar buf
[2] = {0, 0};
165 purple_debug_info("sipe", "sending keep alive\n");
166 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
168 time_t now
= time(NULL
);
169 if ((sip
->keepalive_timeout
> 0) &&
170 ((now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
171 #if PURPLE_VERSION_CHECK(2,4,0)
172 && ((now
- gc
->last_received
) >= sip
->keepalive_timeout
)
175 purple_debug_info("sipe", "sending keep alive\n");
176 sendout_pkt(gc
, "\r\n\r\n");
177 sip
->last_keepalive
= now
;
182 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
184 struct sip_connection
*ret
= NULL
;
185 GSList
*entry
= sip
->openconns
;
188 if (ret
->fd
== fd
) return ret
;
194 static void sipe_auth_free(struct sip_auth
*auth
)
198 g_free(auth
->opaque
);
202 g_free(auth
->target
);
204 g_free(auth
->digest_session_key
);
205 auth
->digest_session_key
= NULL
;
206 g_free(auth
->ntlm_key
);
207 auth
->ntlm_key
= NULL
;
208 auth
->type
= AUTH_TYPE_UNSET
;
213 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
215 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
217 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
221 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
223 struct sip_connection
*conn
= connection_find(sip
, fd
);
225 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
226 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
232 static void connection_free_all(struct sipe_account_data
*sip
)
234 struct sip_connection
*ret
= NULL
;
235 GSList
*entry
= sip
->openconns
;
238 connection_remove(sip
, ret
->fd
);
239 entry
= sip
->openconns
;
243 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
245 const gchar
*method
= msg
->method
;
246 const gchar
*target
= msg
->target
;
251 const char *authdomain
= sip
->authdomain
;
252 const char *authuser
= sip
->authuser
;
253 //const char *krb5_realm;
255 //gchar *krb5_token = NULL;
257 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
258 // and do error checking
260 // KRB realm should always be uppercase
261 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
263 if (sip
->realhostname
) {
264 host
= sip
->realhostname
;
265 } else if (purple_account_get_bool(sip
->account
, "useproxy", TRUE
)) {
266 host
= purple_account_get_string(sip
->account
, "proxy", "");
268 host
= sip
->sipdomain
;
271 /*gboolean new_auth = krb5_auth.gss_context == NULL;
273 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
276 if (new_auth || force_reauth) {
277 krb5_token = krb5_auth.base64_token;
280 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
281 krb5_token = krb5_auth.base64_token;*/
287 if (!authuser
|| strlen(authuser
) < 1) {
288 authuser
= sip
->username
;
291 if (auth
->type
== AUTH_TYPE_DIGEST
) { /* Digest */
292 sprintf(noncecount
, "%08d", auth
->nc
++);
293 response
= purple_cipher_http_digest_calculate_response(
294 "md5", method
, target
, NULL
, NULL
,
295 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
296 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
298 ret
= g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"", authuser
, auth
->realm
, auth
->nonce
, target
, noncecount
, response
);
301 } else if (auth
->type
== AUTH_TYPE_NTLM
) { /* NTLM */
302 // If we have a signature for the message, include that
303 if (msg
->signature
) {
304 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", crand=\"%s\", cnum=\"%s\", response=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, msg
->rand
, msg
->num
, msg
->signature
);
308 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
309 const gchar
*ntlm_key
;
311 #if GLIB_CHECK_VERSION(2,8,0)
312 const gchar
* hostname
= g_get_host_name();
314 static char hostname
[256];
315 int ret
= gethostname(hostname
, sizeof(hostname
));
316 hostname
[sizeof(hostname
) - 1] = '\0';
317 if (ret
== -1 || hostname
[0] == '\0') {
318 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Error when getting host name. Using \"localhost.\"\n");
320 strcpy(hostname
, "localhost");
323 /*const gchar * hostname = purple_get_host_name();*/
325 gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, hostname
, authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
326 auth
->ntlm_key
= (gchar
*)ntlm_key
;
327 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
332 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
334 } else if (auth
->type
== AUTH_TYPE_KERBEROS
) {
337 /*if (new_auth || force_reauth) {
338 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
340 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, krb5_token);
342 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
345 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
346 gchar * mic = "MICTODO";
347 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
348 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
349 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
350 //auth->opaque ? auth->opaque : "", auth->target);
351 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
356 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
359 sprintf(noncecount
, "%08d", auth
->nc
++);
360 response
= purple_cipher_http_digest_calculate_response(
361 "md5", method
, target
, NULL
, NULL
,
362 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
363 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
365 ret
= g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"", authuser
, auth
->realm
, auth
->nonce
, target
, noncecount
, response
);
370 static char *parse_attribute(const char *attrname
, const char *source
)
372 const char *tmp
, *tmp2
;
374 int len
= strlen(attrname
);
376 if (!strncmp(source
, attrname
, len
)) {
378 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
380 retval
= g_strndup(tmp
, tmp2
- tmp
);
382 retval
= g_strdup(tmp
);
388 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
391 const char *authuser
;
394 //const char *krb5_realm;
397 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
398 // and do error checking
400 // KRB realm should always be uppercase
401 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
403 if (sip->realhostname) {
404 host = sip->realhostname;
405 } else if (purple_account_get_bool(sip->account, "useproxy", TRUE)) {
406 host = purple_account_get_string(sip->account, "proxy", "");
408 host = sip->sipdomain;
411 authuser
= sip
->authuser
;
413 if (!authuser
|| strlen(authuser
) < 1) {
414 authuser
= sip
->username
;
418 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
422 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
423 auth
->type
= AUTH_TYPE_NTLM
;
424 parts
= g_strsplit(hdr
+5, "\", ", 0);
427 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
428 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
430 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
433 if ((tmp
= parse_attribute("targetname=\"",
435 g_free(auth
->target
);
438 else if ((tmp
= parse_attribute("realm=\"",
443 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
444 g_free(auth
->opaque
);
451 if (!strstr(hdr
, "gssapi-data")) {
459 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
460 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
461 auth
->type
= AUTH_TYPE_KERBEROS
;
462 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
463 parts
= g_strsplit(hdr
+9, "\", ", 0);
466 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
467 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
468 /*if (krb5_auth.gss_context == NULL) {
469 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
471 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
474 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
475 g_free(auth
->target
);
477 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
480 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
481 g_free(auth
->opaque
);
491 auth
->type
= AUTH_TYPE_DIGEST
;
492 parts
= g_strsplit(hdr
, " ", 0);
494 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
498 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
506 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
508 g_free(auth
->digest_session_key
);
509 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
510 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
516 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
518 PurpleConnection
*gc
= data
;
519 struct sipe_account_data
*sip
= gc
->proto_data
;
523 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
525 if (max_write
== 0) {
526 if (sip
->tx_handler
!= 0){
527 purple_input_remove(sip
->tx_handler
);
533 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
535 if (written
< 0 && errno
== EAGAIN
)
537 else if (written
<= 0) {
538 /*TODO: do we really want to disconnect on a failure to write?*/
539 purple_connection_error(gc
, _("Could not write"));
543 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
546 static void sipe_canwrite_cb_ssl(gpointer data
, gint src
, PurpleInputCondition cond
)
548 PurpleConnection
*gc
= data
;
549 struct sipe_account_data
*sip
= gc
->proto_data
;
553 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
555 if (max_write
== 0) {
556 if (sip
->tx_handler
!= 0) {
557 purple_input_remove(sip
->tx_handler
);
563 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
565 if (written
< 0 && errno
== EAGAIN
)
567 else if (written
<= 0) {
568 /*TODO: do we really want to disconnect on a failure to write?*/
569 purple_connection_error(gc
, _("Could not write"));
573 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
576 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
578 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
580 PurpleConnection
*gc
= data
;
581 struct sipe_account_data
*sip
;
582 struct sip_connection
*conn
;
584 if (!PURPLE_CONNECTION_IS_VALID(gc
))
592 purple_connection_error(gc
, _("Could not connect"));
596 sip
= gc
->proto_data
;
598 sip
->connecting
= FALSE
;
599 sip
->last_keepalive
= time(NULL
);
601 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
603 /* If there is more to write now, we need to register a handler */
604 if (sip
->txbuf
->bufused
> 0)
605 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
607 conn
= connection_create(sip
, source
);
608 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
611 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
613 struct sipe_account_data
*sip
;
614 struct sip_connection
*conn
;
616 if (!PURPLE_CONNECTION_IS_VALID(gc
))
618 if (gsc
) purple_ssl_close(gsc
);
622 sip
= gc
->proto_data
;
625 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
626 sip
->connecting
= FALSE
;
627 sip
->last_keepalive
= time(NULL
);
629 conn
= connection_create(sip
, gsc
->fd
);
631 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
636 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
638 PurpleConnection
*gc
= data
;
639 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
640 if (sip
== NULL
) return;
642 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
644 /* If there is more to write now */
645 if (sip
->txbuf
->bufused
> 0) {
646 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
651 static void sendlater(PurpleConnection
*gc
, const char *buf
)
653 struct sipe_account_data
*sip
= gc
->proto_data
;
655 if (!sip
->connecting
) {
656 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
657 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
658 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
660 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
661 purple_connection_error(gc
, _("Couldn't create socket"));
664 sip
->connecting
= TRUE
;
667 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
668 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
670 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
673 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
675 struct sipe_account_data
*sip
= gc
->proto_data
;
676 time_t currtime
= time(NULL
);
677 int writelen
= strlen(buf
);
679 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
680 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
681 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
682 purple_debug_info("sipe", "could not send packet\n");
691 if (sip
->tx_handler
) {
696 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
698 ret
= write(sip
->fd
, buf
, writelen
);
702 if (ret
< 0 && errno
== EAGAIN
)
704 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
709 if (ret
< writelen
) {
710 if (!sip
->tx_handler
){
712 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
715 sip
->tx_handler
= purple_input_add(sip
->fd
,
716 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
721 /* XXX: is it OK to do this? You might get part of a request sent
722 with part of another. */
723 if (sip
->txbuf
->bufused
> 0)
724 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
726 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
732 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
734 sendout_pkt(gc
, buf
);
738 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
740 GSList
*tmp
= msg
->headers
;
743 GString
*outstr
= g_string_new("");
744 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
746 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
747 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
748 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
749 tmp
= g_slist_next(tmp
);
751 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
752 sendout_pkt(sip
->gc
, outstr
->str
);
753 g_string_free(outstr
, TRUE
);
756 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
759 if (sip
->registrar
.ntlm_key
) {
760 struct sipmsg_breakdown msgbd
;
761 gchar
*signature_input_str
;
763 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
764 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
765 sip
->registrar
.ntlm_num
++;
766 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
767 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
768 if (signature_input_str
!= NULL
) {
769 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
770 msg
->rand
= g_strdup(msgbd
.rand
);
771 msg
->num
= g_strdup(msgbd
.num
);
772 g_free(signature_input_str
);
774 sipmsg_breakdown_free(&msgbd
);
777 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
778 buf
= auth_header(sip
, &sip
->registrar
, msg
);
779 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
780 sipmsg_add_header(msg
, "Authorization", buf
);
782 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
785 } else if (!strcmp(method
,"SUBSCRIBE") || !strcmp(method
,"SERVICE") || !strcmp(method
,"MESSAGE") || !strcmp(method
,"INVITE") || !strcmp(method
, "ACK") || !strcmp(method
, "NOTIFY") || !strcmp(method
, "BYE") || !strcmp(method
, "INFO") || !strcmp(method
, "OPTIONS")) {
786 sip
->registrar
.nc
= 3;
787 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
789 buf
= auth_header(sip
, &sip
->registrar
, msg
);
790 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
793 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
797 static char *get_contact(struct sipe_account_data
*sip
)
799 return g_strdup(sip
->contact
);
804 static char *get_contact_service(struct sipe_account_data *sip)
806 return g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip->listenport, TRANSPORT_DESCRIPTOR, generateUUIDfromEPID(get_epid()));
807 //return g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace", sip->username, sip->listenport, purple_network_get_my_ip(-1), TRANSPORT_DESCRIPTOR);
811 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
812 const char *text
, const char *body
)
816 GString
*outstr
= g_string_new("");
817 struct sipe_account_data
*sip
= gc
->proto_data
;
821 sipmsg_remove_header(msg
, "ms-user-data");
823 contact
= get_contact(sip
);
824 sipmsg_remove_header(msg
, "Contact");
825 sipmsg_add_header(msg
, "Contact", contact
);
828 /* When sending the acknowlegements and errors, the content length from the original
829 message is still here, but there is no body; we need to make sure we're sending the
830 correct content length */
831 sipmsg_remove_header(msg
, "Content-Length");
834 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
835 sipmsg_add_header(msg
, "Content-Length", len
);
837 sipmsg_remove_header(msg
, "Content-Type");
838 sipmsg_add_header(msg
, "Content-Length", "0");
841 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
842 //gchar * mic = "MICTODO";
843 msg
->response
= code
;
845 sipmsg_remove_header(msg
, "Authentication-Info");
846 sign_outgoing_message(msg
, sip
, msg
->method
);
848 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
851 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
852 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
854 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
855 tmp
= g_slist_next(tmp
);
857 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
858 sendout_pkt(gc
, outstr
->str
);
859 g_string_free(outstr
, TRUE
);
862 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
864 if (trans
->msg
) sipmsg_free(trans
->msg
);
865 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
869 static struct transaction
*
870 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
872 struct transaction
*trans
= g_new0(struct transaction
, 1);
873 trans
->time
= time(NULL
);
874 trans
->msg
= (struct sipmsg
*)msg
;
875 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
876 trans
->callback
= callback
;
877 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
881 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
883 struct transaction
*trans
;
884 GSList
*transactions
= sip
->transactions
;
885 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
887 while (transactions
) {
888 trans
= transactions
->data
;
889 if (!strcmp(trans
->cseq
, cseq
)) {
892 transactions
= transactions
->next
;
898 static struct transaction
*
899 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
900 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
901 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
903 struct sipe_account_data
*sip
= gc
->proto_data
;
904 const char *addh
= "";
907 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
908 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
909 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
910 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
911 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
912 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
913 gchar
*route
= strdup("");
914 gchar
*epid
= get_epid(); // TODO generate one per account/login
915 struct transaction
*trans
;
917 if (dialog
&& dialog
->routes
)
919 GSList
*iter
= dialog
->routes
;
924 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
926 iter
= g_slist_next(iter
);
930 if (!ourtag
&& !dialog
) {
934 if (!strcmp(method
, "REGISTER")) {
935 if (sip
->regcallid
) {
937 callid
= g_strdup(sip
->regcallid
);
939 sip
->regcallid
= g_strdup(callid
);
943 if (addheaders
) addh
= addheaders
;
945 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
946 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
947 "From: <sip:%s>%s%s;epid=%s\r\n"
948 "To: <%s>%s%s%s%s\r\n"
949 "Max-Forwards: 70\r\n"
954 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
956 dialog
&& dialog
->request
? dialog
->request
: url
,
957 TRANSPORT_DESCRIPTOR
,
958 purple_network_get_my_ip(-1),
960 branch
? ";branch=" : "",
961 branch
? branch
: "",
963 ourtag
? ";tag=" : "",
964 ourtag
? ourtag
: "",
967 theirtag
? ";tag=" : "",
968 theirtag
? theirtag
: "",
969 theirepid
? ";epid=" : "",
970 theirepid
? theirepid
: "",
971 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
977 body
? strlen(body
) : 0,
981 //printf ("parsing msg buf:\n%s\n\n", buf);
982 msg
= sipmsg_parse_msg(buf
);
993 sign_outgoing_message (msg
, sip
, method
);
995 buf
= sipmsg_to_string (msg
);
997 /* add to ongoing transactions */
998 trans
= transactions_add_buf(sip
, msg
, tc
);
999 sendout_pkt(gc
, buf
);
1005 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
1007 gchar
*from
= g_strdup_printf("sip:%s", sip
->username
);
1008 gchar
*contact
= get_contact(sip
);
1009 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
1010 "Content-Type: application/SOAP+xml\r\n",contact
);
1012 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1013 tr
->payload
= payload
;
1020 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1022 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
1025 static char *get_contact_register(struct sipe_account_data
*sip
)
1027 char *epid
= get_epid();
1028 char *uuid
= generateUUIDfromEPID(epid
);
1029 char *buf
= g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, BENOTIFY\";proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip
->listenport
, TRANSPORT_DESCRIPTOR
, uuid
);
1035 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1037 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
1038 char *to
= g_strdup_printf("sip:%s", sip
->username
);
1039 char *contact
= get_contact_register(sip
);
1040 char *hdr
= g_strdup_printf("Contact: %s\r\n"
1041 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1042 "Event: registration\r\n"
1043 "Allow-Events: presence\r\n"
1044 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1045 "Expires: %d\r\n", contact
,expire
);
1048 sip
->registerstatus
= 1;
1050 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1051 process_register_response
);
1058 static void do_register_cb(struct sipe_account_data
*sip
)
1060 do_register_exp(sip
, sip
->registerexpire
);
1061 sip
->reregister_set
= FALSE
;
1064 static void do_register(struct sipe_account_data
*sip
)
1066 do_register_exp(sip
, sip
->registerexpire
);
1070 * Returns URI from provided To or From header.
1072 * Needs to g_free() after use.
1074 * @return URI with sip: prefix
1076 static gchar
*parse_from(const gchar
*hdr
)
1079 const gchar
*tmp
, *tmp2
= hdr
;
1081 if (!hdr
) return NULL
;
1082 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1083 tmp
= strchr(hdr
, '<');
1085 /* i hate the different SIP UA behaviours... */
1086 if (tmp
) { /* sip address in <...> */
1088 tmp
= strchr(tmp2
, '>');
1090 from
= g_strndup(tmp2
, tmp
- tmp2
);
1092 purple_debug_info("sipe", "found < without > in From\n");
1096 tmp
= strchr(tmp2
, ';');
1098 from
= g_strndup(tmp2
, tmp
- tmp2
);
1100 from
= g_strdup(tmp2
);
1103 purple_debug_info("sipe", "got %s\n", from
);
1107 static xmlnode
* xmlnode_get_descendant(xmlnode
* parent
, ...)
1110 xmlnode
* node
= NULL
;
1113 va_start(args
, parent
);
1114 while ((name
= va_arg(args
, const char *)) != NULL
) {
1115 node
= xmlnode_get_child(parent
, name
);
1116 if (node
== NULL
) return NULL
;
1126 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1128 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1129 send_soap_request(sip
, body
);
1134 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1137 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1139 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1142 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1146 void sipe_auth_user_cb(void * data
)
1148 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1151 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1156 void sipe_deny_user_cb(void * data
)
1158 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1161 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1166 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1168 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1169 sipe_contact_allow_deny(sip
, name
, TRUE
);
1173 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1175 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1176 sipe_contact_allow_deny(sip
, name
, FALSE
);
1180 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1182 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1183 sipe_contact_set_acl(sip, name, "");
1187 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1191 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1192 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1194 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1196 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1197 if (!watchers
) return;
1199 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1200 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1201 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1202 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1204 // TODO pull out optional displayName to pass as alias
1206 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1207 job
->who
= remote_user
;
1209 purple_account_request_authorization(
1223 xmlnode_free(watchers
);
1228 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1230 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1231 if (!purple_group
) {
1232 purple_group
= purple_group_new(group
->name
);
1233 purple_blist_add_group(purple_group
, NULL
);
1237 group
->purple_group
= purple_group
;
1238 sip
->groups
= g_slist_append(sip
->groups
, group
);
1239 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1241 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1245 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1247 struct sipe_group
*group
;
1253 entry
= sip
->groups
;
1255 group
= entry
->data
;
1256 if (group
->id
== id
) {
1259 entry
= entry
->next
;
1264 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, gchar
* name
)
1266 struct sipe_group
*group
;
1272 entry
= sip
->groups
;
1274 group
= entry
->data
;
1275 if (!strcmp(group
->name
, name
)) {
1278 entry
= entry
->next
;
1284 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1287 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1288 body
= g_strdup_printf(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1289 send_soap_request(sip
, body
);
1291 g_free(group
->name
);
1292 group
->name
= g_strdup(name
);
1296 * Only appends if no such value already stored.
1299 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1300 GSList
* res
= list
;
1301 if (!g_slist_find_custom(list
, data
, func
)) {
1302 res
= g_slist_insert_sorted(list
, data
, func
);
1308 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1309 return group1
->id
- group2
->id
;
1313 * Returns string like "2 4 7 8" - group ids buddy belong to.
1316 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1319 //creating array from GList, converting int to gchar*
1320 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1321 GSList
*entry
= buddy
->groups
;
1323 struct sipe_group
* group
= entry
->data
;
1324 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1325 entry
= entry
->next
;
1329 res
= g_strjoinv(" ", ids_arr
);
1330 g_strfreev(ids_arr
);
1335 * Sends buddy update to server
1338 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1340 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1341 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1343 if (buddy
&& purple_buddy
) {
1344 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1346 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1347 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1349 body
= g_strdup_printf(SIPE_SOAP_SET_CONTACT
,
1350 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1352 send_soap_request(sip
, body
);
1358 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1360 if (msg
->response
== 200) {
1361 struct sipe_group
*group
;
1362 struct group_user_context
*ctx
= (struct group_user_context
*)tc
->payload
;
1366 struct sipe_buddy
*buddy
;
1367 group
->name
= ctx
->group_name
;
1369 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1375 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1382 group_id
= xmlnode_get_data(node
);
1389 group
= g_new0(struct sipe_group
, 1);
1390 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1393 sipe_group_add(sip
, group
);
1395 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1397 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1400 sipe_group_set_user(sip
, ctx
->user_name
);
1409 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1411 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1413 ctx
->group_name
= g_strdup(name
);
1414 ctx
->user_name
= g_strdup(who
);
1416 body
= g_strdup_printf(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1417 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1423 * Should return FALSE if repetitive action is not needed
1425 gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1428 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1429 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1430 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1431 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1432 ret
= sched_action
->repetitive
;
1433 g_free(sched_action
->payload
);
1434 g_free(sched_action
->name
);
1435 g_free(sched_action
);
1440 * Do schedule action for execution in the future.
1441 * Non repetitive execution.
1443 * @param name of action (will be copied)
1444 * @param timeout in seconds
1445 * @action callback function
1446 * @payload callback data (can be NULL, otherwise caller must allocate memory)
1448 void sipe_schedule_action(gchar
*name
, int timeout
, Action action
, struct sipe_account_data
*sip
, void * payload
)
1450 struct scheduled_action
*sched_action
;
1452 purple_debug_info("sipe","scheduling action %s timeout:%d\n", name
, timeout
);
1453 sched_action
= g_new0(struct scheduled_action
, 1);
1454 sched_action
->repetitive
= FALSE
;
1455 sched_action
->name
= g_strdup(name
);
1456 sched_action
->action
= action
;
1457 sched_action
->sip
= sip
;
1458 sched_action
->payload
= payload
;
1459 sched_action
->timeout_handler
= purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1460 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1461 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1465 * Kills action timer effectively cancelling
1468 * @param name of action
1470 void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, gchar
*name
)
1474 if (!sip
->timeouts
|| !name
) return;
1476 entry
= sip
->timeouts
;
1478 struct scheduled_action
*sched_action
= entry
->data
;
1479 if(!strcmp(sched_action
->name
, name
)) {
1480 GSList
*to_delete
= entry
;
1481 entry
= entry
->next
;
1482 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1483 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1484 purple_timeout_remove(sched_action
->timeout_handler
);
1485 g_free(sched_action
->payload
);
1486 g_free(sched_action
->name
);
1487 g_free(sched_action
);
1489 entry
= entry
->next
;
1494 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1496 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1498 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1500 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1505 static void sipe_subscribe_resource_uri(const char *name
, gpointer value
, gchar
**resources_uri
)
1507 gchar
*tmp
= *resources_uri
;
1508 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1513 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1514 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1515 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1516 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1517 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1520 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
){
1521 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1522 gchar
*contact
= get_contact(sip
);
1525 gchar
*resources_uri
= g_strdup("");
1526 gchar
*require
= "";
1528 gchar
*content_type
;
1530 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1532 if (sip
->msrtc_event_categories
) {
1533 require
= ", categoryList";
1534 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1535 content_type
= "application/msrtc-adrl-categorylist+xml";
1536 content
= g_strdup_printf(
1537 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1538 "<action name=\"subscribe\" id=\"63792024\">\n"
1539 "<adhocList>\n%s</adhocList>\n"
1540 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1541 "<category name=\"note\"/>\n"
1542 "<category name=\"state\"/>\n"
1545 "</batchSub>", sip
->username
, resources_uri
);
1547 content_type
= "application/adrl+xml";
1548 content
= g_strdup_printf(
1549 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1550 "<create xmlns=\"\">\n%s</create>\n"
1551 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1553 g_free(resources_uri
);
1555 request
= g_strdup_printf(
1556 "Require: adhoclist%s\r\n"
1557 "Supported: eventlist\r\n"
1558 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1559 "Supported: ms-piggyback-first-notify\r\n"
1560 "Supported: com.microsoft.autoextend\r\n"
1561 "Supported: ms-benotify\r\n"
1562 "Proxy-Require: ms-benotify\r\n"
1563 "Event: presence\r\n"
1564 "Content-Type: %s\r\n"
1565 "Contact: %s\r\n", require
, accept
, content_type
, contact
);
1568 /* subscribe to buddy presence */
1569 //send_sip_request(sip->gc, "SUBSCRIBE", resource_uri, resource_uri, request, content, NULL, process_subscribe_response);
1570 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1578 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1579 * The user sends a single SUBSCRIBE request to the subscribed contact.
1580 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1584 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, const char * buddy_name
)
1586 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1587 gchar
*tmp
= get_contact(sip
);
1590 request
= g_strdup_printf(
1591 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1592 "Supported: ms-piggyback-first-notify\r\n"
1593 "Supported: com.microsoft.autoextend\r\n"
1594 "Supported: ms-benotify\r\n"
1595 "Proxy-Require: ms-benotify\r\n"
1596 "Event: presence\r\n"
1597 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1598 "Contact: %s\r\n", tmp
);
1600 content
= g_strdup_printf(
1601 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1602 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1603 "<resource uri=\"%s\"/>\n"
1605 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1606 "<category name=\"note\"/>\n"
1607 "<category name=\"state\"/>\n"
1610 "</batchSub>", sip
->username
, to
1615 /* subscribe to buddy presence */
1616 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1623 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1625 if (!purple_status_is_active(status
))
1629 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
1632 g_free(sip
->status
);
1633 sip
->status
= g_strdup(purple_status_get_id(status
));
1634 send_presence_info(sip
);
1640 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
)
1642 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1643 sipe_group_set_user(sip
, name
);
1647 sipe_group_buddy(PurpleConnection
*gc
,
1649 const char *old_group_name
,
1650 const char *new_group_name
)
1652 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1653 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1654 struct sipe_group
* old_group
= NULL
;
1655 struct sipe_group
* new_group
;
1657 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1658 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1660 if(!buddy
) { // buddy not in roaming list
1664 if (old_group_name
) {
1665 old_group
= sipe_group_find_by_name(sip
, g_strdup(old_group_name
));
1667 new_group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1670 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1671 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1675 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1677 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1678 sipe_group_set_user(sip
, who
);
1682 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1684 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1685 struct sipe_buddy
*b
;
1687 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1689 // Prepend sip: if needed
1690 if (strncmp("sip:", buddy
->name
, 4)) {
1691 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
1692 purple_blist_rename_buddy(buddy
, buf
);
1696 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1697 b
= g_new0(struct sipe_buddy
, 1);
1698 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1699 b
->name
= g_strdup(buddy
->name
);
1700 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1701 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1702 sipe_subscribe_presence_single(sip
, b
->name
); //@TODO should go to callback
1704 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1708 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1710 g_free(buddy
->name
);
1711 g_free(buddy
->annotation
);
1712 g_free(buddy
->device_name
);
1713 g_slist_free(buddy
->groups
);
1718 * Unassociates buddy from group first.
1719 * Then see if no groups left, removes buddy completely.
1720 * Otherwise updates buddy groups on server.
1722 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1724 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1725 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1726 struct sipe_group
*g
= NULL
;
1728 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1733 g
= sipe_group_find_by_name(sip
, group
->name
);
1737 b
->groups
= g_slist_remove(b
->groups
, g
);
1738 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
1741 if (g_slist_length(b
->groups
) < 1) {
1742 gchar
*action_name
= g_strdup_printf("<%s><%s>", "presence", buddy
->name
);
1743 sipe_cancel_scheduled_action(sip
, action_name
);
1744 g_free(action_name
);
1746 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1749 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1750 send_soap_request(sip
, body
);
1756 //updates groups on server
1757 sipe_group_set_user(sip
, b
->name
);
1763 sipe_rename_group(PurpleConnection
*gc
,
1764 const char *old_name
,
1766 GList
*moved_buddies
)
1768 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1769 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1771 sipe_group_rename(sip
, s_group
, group
->name
);
1773 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1778 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1780 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1781 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1784 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1785 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1786 send_soap_request(sip
, body
);
1789 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1790 g_free(s_group
->name
);
1792 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1796 static GList
*sipe_status_types(PurpleAccount
*acc
)
1798 PurpleStatusType
*type
;
1799 GList
*types
= NULL
;
1802 type
= purple_status_type_new_with_attrs(
1803 PURPLE_STATUS_AVAILABLE
, NULL
, "Online", TRUE
, TRUE
, FALSE
,
1804 // Translators: noun
1805 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1807 types
= g_list_append(types
, type
);
1810 type
= purple_status_type_new_with_attrs(
1811 PURPLE_STATUS_UNAVAILABLE
, "busy", _("Busy"), TRUE
, TRUE
, FALSE
,
1812 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1814 types
= g_list_append(types
, type
);
1816 // Do Not Disturb (Not let user set it)
1817 type
= purple_status_type_new_with_attrs(
1818 PURPLE_STATUS_UNAVAILABLE
, "do-not-disturb", "Do Not Disturb", TRUE
, FALSE
, FALSE
,
1819 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1821 types
= g_list_append(types
, type
);
1824 type
= purple_status_type_new_with_attrs(
1825 PURPLE_STATUS_AWAY
, "be-right-back", _("Be Right Back"), TRUE
, TRUE
, FALSE
,
1826 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1828 types
= g_list_append(types
, type
);
1831 type
= purple_status_type_new_with_attrs(
1832 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1833 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1835 types
= g_list_append(types
, type
);
1838 type
= purple_status_type_new_with_attrs(
1839 PURPLE_STATUS_UNAVAILABLE
, "on-the-phone", _("On The Phone"), TRUE
, TRUE
, FALSE
,
1840 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1842 types
= g_list_append(types
, type
);
1845 type
= purple_status_type_new_with_attrs(
1846 PURPLE_STATUS_AWAY
, "out-to-lunch", "Out To Lunch", TRUE
, TRUE
, FALSE
,
1847 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1849 types
= g_list_append(types
, type
);
1852 type
= purple_status_type_new_full(
1853 PURPLE_STATUS_INVISIBLE
, NULL
, "Appear Offline", TRUE
, TRUE
, FALSE
);
1854 types
= g_list_append(types
, type
);
1857 type
= purple_status_type_new_full(
1858 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1859 types
= g_list_append(types
, type
);
1865 * A callback for g_hash_table_foreach
1867 static void sipe_buddy_subscribe_cb(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1869 sipe_subscribe_presence_single(sip
, buddy
->name
);
1873 * Removes entries from purple buddy list
1874 * that does not correspond ones in the roaming contact list.
1876 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
1877 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1878 GSList
*entry
= buddies
;
1879 struct sipe_buddy
*buddy
;
1883 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
1884 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
1887 g
= purple_buddy_get_group(b
);
1888 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
1890 gboolean in_sipe_groups
= FALSE
;
1891 GSList
*entry2
= buddy
->groups
;
1893 struct sipe_group
*group
= entry2
->data
;
1894 if (!strcmp(group
->name
, g
->name
)) {
1895 in_sipe_groups
= TRUE
;
1898 entry2
= entry2
->next
;
1900 if(!in_sipe_groups
) {
1901 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
1902 purple_blist_remove_buddy(b
);
1905 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
1906 purple_blist_remove_buddy(b
);
1908 entry
= entry
->next
;
1912 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1914 int len
= msg
->bodylen
;
1916 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1919 const gchar
*contacts_delta
;
1920 xmlnode
*group_node
;
1921 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1925 /* Convert the contact from XML to Purple Buddies */
1926 isc
= xmlnode_from_str(msg
->body
, len
);
1931 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
1932 if (contacts_delta
) {
1933 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1937 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1938 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1939 const char *name
= xmlnode_get_attrib(group_node
, "name");
1941 if (!strncmp(name
, "~", 1)) {
1943 name
= "Other Contacts";
1945 group
->name
= g_strdup(name
);
1946 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1948 sipe_group_add(sip
, group
);
1951 // Make sure we have at least one group
1952 if (g_slist_length(sip
->groups
) == 0) {
1953 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1954 PurpleGroup
*purple_group
;
1956 group
->name
= g_strdup("Other Contacts");
1958 purple_group
= purple_group_new(group
->name
);
1959 purple_blist_add_group(purple_group
, NULL
);
1960 sip
->groups
= g_slist_append(sip
->groups
, group
);
1963 /* Parse contacts */
1964 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1965 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
1966 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
1967 gchar
* groups
= g_strdup(xmlnode_get_attrib(item
, "groups"));
1968 gchar
* buddy_name
= g_strdup_printf("sip:%s", uri
);
1969 gchar
**item_groups
;
1970 struct sipe_group
*group
= NULL
;
1971 struct sipe_buddy
*buddy
= NULL
;
1974 // assign to group Other Contacts if nothing else received
1975 if(!groups
|| !strcmp("", groups
) ) {
1976 group
= sipe_group_find_by_name(sip
, "Other Contacts");
1977 groups
= group
? g_strdup_printf("%d", group
->id
) : "1";
1980 item_groups
= g_strsplit(groups
, " ", 0);
1982 while (item_groups
[i
]) {
1983 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
1985 // If couldn't find the right group for this contact, just put them in the first group we have
1986 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1987 group
= sip
->groups
->data
;
1990 if (group
!= NULL
) {
1991 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1993 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1994 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1997 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
1998 if (name
!= NULL
&& strlen(name
) != 0) {
1999 purple_blist_alias_buddy(b
, name
);
2004 buddy
= g_new0(struct sipe_buddy
, 1);
2005 buddy
->name
= g_strdup(b
->name
);
2006 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2009 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2011 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2013 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2018 } // while, contact groups
2019 g_strfreev(item_groups
);
2029 sipe_cleanup_local_blist(sip
);
2031 //subscribe to buddies
2032 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2033 //if(sip->msrtc_event_categories){
2034 sipe_subscribe_presence_batched(sip
);
2036 //g_hash_table_foreach(sip->buddies, (GHFunc)sipe_buddy_subscribe_cb, (gpointer)sip);
2038 sip
->subscribed_buddies
= TRUE
;
2045 * Subscribe roaming contacts
2047 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2049 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2050 gchar
*tmp
= get_contact(sip
);
2051 gchar
*hdr
= g_strdup_printf(
2052 "Event: vnd-microsoft-roaming-contacts\r\n"
2053 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2054 "Supported: com.microsoft.autoextend\r\n"
2055 "Supported: ms-benotify\r\n"
2056 "Proxy-Require: ms-benotify\r\n"
2057 "Supported: ms-piggyback-first-notify\r\n"
2058 "Contact: %s\r\n", tmp
);
2061 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2066 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2068 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2069 gchar
*tmp
= get_contact(sip
);
2070 gchar
*hdr
= g_strdup_printf(
2071 "Event: presence.wpending\r\n"
2072 "Accept: text/xml+msrtc.wpending\r\n"
2073 "Supported: com.microsoft.autoextend\r\n"
2074 "Supported: ms-benotify\r\n"
2075 "Proxy-Require: ms-benotify\r\n"
2076 "Supported: ms-piggyback-first-notify\r\n"
2077 "Contact: %s\r\n", tmp
);
2080 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2085 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2087 const gchar
*contacts_delta
;
2090 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2096 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2099 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2106 * When we receive some self (BE) NOTIFY with a new subscriber
2107 * we sends a setSubscribers request to him [SIP-PRES]
2111 static void sipe_process_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2118 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2120 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2123 contact
= get_contact(sip
);
2124 to
= g_strdup_printf("sip:%s", sip
->username
);
2126 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2131 user
= xmlnode_get_attrib(node
, "user");
2132 if (!user
) continue;
2134 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
2136 hdr
= g_strdup_printf(
2138 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2140 body
= g_strdup_printf(
2141 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2142 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2143 "</setSubscribers>", user
);
2145 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
2155 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2157 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2158 gchar
*tmp
= get_contact(sip
);
2159 gchar
*hdr
= g_strdup_printf(
2160 "Event: vnd-microsoft-roaming-ACL\r\n"
2161 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
2162 "Supported: com.microsoft.autoextend\r\n"
2163 "Supported: ms-benotify\r\n"
2164 "Proxy-Require: ms-benotify\r\n"
2165 "Supported: ms-piggyback-first-notify\r\n"
2166 "Contact: %s\r\n", tmp
);
2169 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2175 * To request for presence information about the user, access level settings that have already been configured by the user
2176 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
2177 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
2180 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2182 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2183 gchar
*tmp
= get_contact(sip
);
2184 gchar
*hdr
= g_strdup_printf(
2185 "Event: vnd-microsoft-roaming-self\r\n"
2186 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
2187 "Supported: com.microsoft.autoextend\r\n"
2188 "Supported: ms-benotify\r\n"
2189 "Proxy-Require: ms-benotify\r\n"
2190 "Supported: ms-piggyback-first-notify\r\n"
2192 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
2194 gchar
*body
=g_strdup(
2195 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
2196 "<roaming type=\"categories\"/>"
2197 "<roaming type=\"containers\"/>"
2198 "<roaming type=\"subscribers\"/></roamingList>");
2201 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2207 /** Subscription for provisioning information to help with initial
2208 * configuration. This subscription is a one-time query (denoted by the Expires header,
2209 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
2210 * configuration, meeting policies, and policy settings that Communicator must enforce.
2211 * TODO: for what we need this information.
2214 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2216 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2217 gchar
*tmp
= get_contact(sip
);
2218 gchar
*hdr
= g_strdup_printf(
2219 "Event: vnd-microsoft-provisioning-v2\r\n"
2220 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
2221 "Supported: com.microsoft.autoextend\r\n"
2222 "Supported: ms-benotify\r\n"
2223 "Proxy-Require: ms-benotify\r\n"
2224 "Supported: ms-piggyback-first-notify\r\n"
2227 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
2228 gchar
*body
= g_strdup(
2229 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
2230 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
2231 "<provisioningGroup name=\"ucPolicy\"/>"
2232 "</provisioningGroupList>");
2235 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2241 /* IM Session (INVITE and MESSAGE methods) */
2243 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
2245 struct sip_im_session
*session
;
2247 if (sip
== NULL
|| who
== NULL
) {
2251 entry
= sip
->im_sessions
;
2253 session
= entry
->data
;
2254 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
2257 entry
= entry
->next
;
2262 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
2264 struct sip_im_session
*session
= find_im_session(sip
, who
);
2266 session
= g_new0(struct sip_im_session
, 1);
2267 session
->with
= g_strdup(who
);
2268 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
2273 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2275 struct sip_dialog
*dialog
= session
->dialog
;
2278 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
2281 entry
= dialog
->routes
;
2283 g_free(entry
->data
);
2284 entry
= g_slist_remove(entry
, entry
->data
);
2286 entry
= dialog
->supported
;
2288 g_free(entry
->data
);
2289 entry
= g_slist_remove(entry
, entry
->data
);
2291 g_free(dialog
->callid
);
2292 g_free(dialog
->ourtag
);
2293 g_free(dialog
->theirtag
);
2294 g_free(dialog
->theirepid
);
2295 g_free(dialog
->request
);
2297 g_free(session
->dialog
);
2299 entry
= session
->outgoing_message_queue
;
2301 g_free(entry
->data
);
2302 entry
= g_slist_remove(entry
, entry
->data
);
2305 g_free(session
->with
);
2310 process_options_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2312 gboolean ret
= TRUE
;
2314 if (msg
->response
!= 200) {
2315 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
2319 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
2325 * Asks UA/proxy about its capabilities.
2327 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
2329 gchar
*to
= strstr(who
, "sip:") ? g_strdup(who
) : g_strdup_printf("sip:%s", who
);
2330 gchar
*contact
= get_contact(sip
);
2332 request
= g_strdup_printf(
2333 "Accept: application/sdp\r\n"
2334 "Contact: %s\r\n", contact
);
2338 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
2344 static void sipe_present_message_undelivered_err(gchar
*with
, struct sipe_account_data
*sip
, gchar
*message
)
2346 char *msg
, *msg_tmp
;
2347 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
2348 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2350 msg_tmp
= g_strdup_printf( _("The following message could not be delivered to all recipients, "\
2351 "possibly because one or more persons are offline:\n%s") ,
2353 purple_conv_present_error(with
, sip
->account
, msg_tmp
);
2358 static void sipe_im_remove_first_from_queue (struct sip_im_session
* session
);
2359 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
);
2362 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2364 gboolean ret
= TRUE
;
2365 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
2366 struct sip_im_session
* session
= find_im_session(sip
, with
);
2367 struct sip_dialog
*dialog
;
2370 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2375 if (msg
->response
!= 200) {
2376 gchar
*queued_msg
= NULL
;
2377 purple_debug_info("sipe", "process_message_response: MESSAGE response not 200\n");
2379 if (session
->outgoing_message_queue
) {
2380 queued_msg
= session
->outgoing_message_queue
->data
;
2382 sipe_present_message_undelivered_err(with
, sip
, queued_msg
);
2383 im_session_destroy(sip
, session
);
2388 dialog
= session
->dialog
;
2390 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2394 sipe_im_remove_first_from_queue(session
);
2395 sipe_im_process_queue(sip
, session
);
2400 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
2410 if (strncmp("sip:", session
->with
, 4)) {
2411 fullto
= g_strdup_printf("sip:%s", session
->with
);
2413 fullto
= g_strdup(session
->with
);
2416 sipe_parse_html(msg
, &msgformat
, &msgtext
);
2417 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
2419 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2422 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2425 msgr
= g_strdup("");
2428 tmp
= get_contact(sip
);
2429 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
2430 //hdr = g_strdup("Content-Type: text/rtf\r\n");
2431 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
2432 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n",
2437 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msgtext
, session
->dialog
, process_message_response
);
2445 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
2447 GSList
*entry
= session
->outgoing_message_queue
;
2449 char *queued_msg
= entry
->data
;
2450 sipe_send_message(sip
, session
, queued_msg
);
2455 sipe_im_remove_first_from_queue (struct sip_im_session
* session
)
2457 if (session
&& session
->outgoing_message_queue
) {
2458 char *queued_msg
= session
->outgoing_message_queue
->data
;
2459 // Remove from the queue and free the string
2460 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2466 sipe_get_route_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2468 GSList
*hdr
= msg
->headers
;
2469 struct siphdrelement
*elem
;
2475 if(!g_ascii_strcasecmp(elem
->name
, "Record-Route"))
2477 gchar
*route
= sipmsg_find_part_of_header(elem
->value
, "<", ">", NULL
);
2478 dialog
->routes
= g_slist_append(dialog
->routes
, route
);
2480 hdr
= g_slist_next(hdr
);
2485 dialog
->routes
= g_slist_reverse(dialog
->routes
);
2490 dialog
->request
= dialog
->routes
->data
;
2491 dialog
->routes
= g_slist_remove(dialog
->routes
, dialog
->routes
->data
);
2494 contact
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Contact"), "<", ">", NULL
);
2495 dialog
->routes
= g_slist_append(dialog
->routes
, contact
);
2499 sipe_get_supported_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2501 GSList
*hdr
= msg
->headers
;
2502 struct siphdrelement
*elem
;
2506 if(!g_ascii_strcasecmp(elem
->name
, "Supported")
2507 && !g_slist_find_custom(dialog
->supported
, elem
->value
, (GCompareFunc
)strcmp
))
2509 dialog
->supported
= g_slist_append(dialog
->supported
, g_strdup(elem
->value
));
2512 hdr
= g_slist_next(hdr
);
2517 sipe_parse_dialog(struct sipmsg
* msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2519 gchar
*us
= outgoing
? "From" : "To";
2520 gchar
*them
= outgoing
? "To" : "From";
2522 dialog
->callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
2523 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, us
));
2524 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, them
));
2525 if (!dialog
->theirepid
) {
2526 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", ";", NULL
);
2528 if (!dialog
->theirepid
) {
2529 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", NULL
, NULL
);
2532 sipe_get_route_header(msg
, dialog
, outgoing
);
2533 sipe_get_supported_header(msg
, dialog
, outgoing
);
2538 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2540 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
2541 struct sip_im_session
* session
= find_im_session(sip
, with
);
2542 struct sip_dialog
*dialog
;
2545 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2550 if (msg
->response
!= 200) {
2551 gchar
*queued_msg
= NULL
;
2552 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2554 if (session
->outgoing_message_queue
) {
2555 queued_msg
= session
->outgoing_message_queue
->data
;
2557 sipe_present_message_undelivered_err(with
, sip
, queued_msg
);
2559 im_session_destroy(sip
, session
);
2564 dialog
= session
->dialog
;
2566 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2571 sipe_parse_dialog(msg
, dialog
, TRUE
);
2574 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
2575 session
->outgoing_invite
= NULL
;
2576 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)strcmp
)) {
2577 sipe_im_remove_first_from_queue(session
);
2579 sipe_im_process_queue(sip
, session
);
2587 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
* session
, gchar
* msg_body
)
2596 char *ms_text_format
;
2600 if (session
->dialog
) {
2601 purple_debug_info("sipe", "session with %s already has a dialog open\n", session
->with
);
2605 session
->dialog
= g_new0(struct sip_dialog
, 1);
2607 if (strstr(session
->with
, "sip:")) {
2608 to
= g_strdup(session
->with
);
2610 to
= g_strdup_printf("sip:%s", session
->with
);
2613 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
2614 purple_debug_info("sipe", "sipe_invite: msgformat=%s", msgformat
);
2616 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2620 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2624 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
2625 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
2630 contact
= get_contact(sip
);
2631 hdr
= g_strdup_printf(
2633 "Content-Type: application/sdp\r\n",
2634 contact
, ms_text_format
);
2635 g_free(ms_text_format
);
2637 body
= g_strdup_printf(
2639 "o=- 0 0 IN IP4 %s\r\n"
2643 "m=message %d sip null\r\n"
2644 "a=accept-types:text/plain text/html image/gif "
2645 "multipart/alternative application/im-iscomposing+xml\r\n",
2646 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
2648 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
2649 to
, to
, hdr
, body
, session
->dialog
, process_invite_response
);
2658 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2661 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->dialog
, NULL
);
2662 im_session_destroy(sip
, session
);
2667 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
2669 struct sipe_account_data
*sip
= gc
->proto_data
;
2671 purple_debug_info("sipe", "conversation with %s closed\n", who
);
2672 im_session_close(sip
, find_im_session(sip
, who
));
2676 im_session_close_all (struct sipe_account_data
*sip
)
2678 GSList
*entry
= sip
->im_sessions
;
2680 im_session_close (sip
, entry
->data
);
2681 entry
= sip
->im_sessions
;
2685 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
2687 struct sipe_account_data
*sip
;
2690 struct sip_im_session
*session
;
2692 sip
= gc
->proto_data
;
2694 text
= g_strdup(what
);
2696 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
2698 session
= find_or_create_im_session(sip
, who
);
2700 // Queue the message
2701 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, text
);
2703 if (session
->dialog
&& session
->dialog
->callid
) {
2704 sipe_im_process_queue(sip
, session
);
2705 } else if (!session
->outgoing_invite
) {
2706 // Need to send the INVITE to get the outgoing dialog setup
2707 sipe_invite(sip
, session
, text
);
2716 /* End IM Session (INVITE and MESSAGE methods) */
2719 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
2721 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2722 struct sip_im_session
*session
;
2724 if (state
== PURPLE_NOT_TYPING
)
2727 session
= find_im_session(sip
, who
);
2729 if (session
&& session
->dialog
) {
2730 send_sip_request(gc
, "INFO", who
, who
,
2731 "Content-Type: application/xml\r\n",
2732 SIPE_SEND_TYPING
, session
->dialog
, NULL
);
2735 return SIPE_TYPING_SEND_TIMEOUT
;
2738 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
2740 GSList
*tmp
= sip
->transactions
;
2741 time_t currtime
= time(NULL
);
2743 struct transaction
*trans
= tmp
->data
;
2745 purple_debug_info("sipe", "have open transaction age: %ld\n", currtime
-trans
->time
);
2746 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
2749 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
2751 sendout_sipmsg(sip
, trans
->msg
);
2758 static void do_reauthenticate_cb(struct sipe_account_data
*sip
)
2760 /* register again when security token expires */
2761 /* we have to start a new authentication as the security token
2762 * is almost expired by sending a not signed REGISTER message */
2763 purple_debug_info("sipe", "do a full reauthentication\n");
2764 sipe_auth_free(&sip
->registrar
);
2765 sip
->registerstatus
= 0;
2767 sip
->reauthenticate_set
= FALSE
;
2770 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2774 gboolean found
= FALSE
;
2776 from
= parse_from(sipmsg_find_header(msg
, "From"));
2780 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
2782 contenttype
= sipmsg_find_header(msg
, "Content-Type");
2783 if (!contenttype
|| !strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
2784 gchar
*msgr
= sipmsg_find_part_of_header(contenttype
, "msgr=", NULL
, NULL
);
2785 gchar
*x_mms_im_format
= sipmsg_get_x_mms_im_format(msgr
);
2787 gchar
*body_esc
= g_markup_escape_text(msg
->body
, -1);
2788 gchar
*body_html
= sipmsg_apply_x_mms_im_format(x_mms_im_format
, body_esc
);
2791 g_free(x_mms_im_format
);
2793 serv_got_im(sip
->gc
, from
, body_html
, 0, time(NULL
));
2795 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2797 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
2798 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2803 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
2807 state
= xmlnode_get_child(isc
, "state");
2810 purple_debug_info("sipe", "process_incoming_message: no state found\n");
2815 statedata
= xmlnode_get_data(state
);
2817 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
2818 else serv_got_typing_stopped(sip
->gc
, from
);
2823 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2827 purple_debug_info("sipe", "got unknown mime-type");
2828 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
2833 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2835 gchar
*ms_text_format
;
2838 struct sip_im_session
*session
;
2840 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? msg
->body
: "");
2842 // Only accept text invitations
2843 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
2844 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
2848 from
= parse_from(sipmsg_find_header(msg
, "From"));
2849 session
= find_or_create_im_session (sip
, from
);
2851 if (session
->dialog
) {
2852 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
2854 session
->dialog
= g_new0(struct sip_dialog
, 1);
2856 sipe_parse_dialog(msg
, session
->dialog
, FALSE
);
2858 session
->dialog
->callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
2859 session
->dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
2860 session
->dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
2861 session
->dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
2864 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
2867 //ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk=
2868 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
2869 if (ms_text_format
&& !strncmp(ms_text_format
, "text/plain", 10)) {
2870 gchar
*msgr
= sipmsg_find_part_of_header(ms_text_format
, "msgr=", ";", NULL
);
2871 gchar
*x_mms_im_format
= sipmsg_get_x_mms_im_format(msgr
);
2873 gchar
*ms_body
= sipmsg_find_part_of_header(ms_text_format
, "ms-body=", NULL
, NULL
);
2876 gchar
*body
= purple_base64_decode(ms_body
, NULL
);
2877 gchar
*body_esc
= g_markup_escape_text(body
, -1);
2878 gchar
*body_html
= sipmsg_apply_x_mms_im_format(x_mms_im_format
, body_esc
);
2882 serv_got_im(sip
->gc
, from
, body_html
, 0, time(NULL
));
2884 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message reciept
2886 g_free(x_mms_im_format
);
2890 sipmsg_remove_header(msg
, "Ms-Conversation-ID");
2891 sipmsg_remove_header(msg
, "Ms-Text-Format");
2892 sipmsg_remove_header(msg
, "EndPoints");
2893 sipmsg_remove_header(msg
, "User-Agent");
2894 sipmsg_remove_header(msg
, "Roster-Manager");
2896 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
2897 //sipmsg_add_header(msg, "Supported", "ms-renders-gif");
2899 body
= g_strdup_printf(
2901 "o=- 0 0 IN IP4 %s\r\n"
2905 "m=message %d sip sip:%s\r\n"
2906 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
2907 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
2908 sip
->realport
, sip
->username
);
2909 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
2913 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2917 struct sip_im_session
*session
;
2920 from
= parse_from(sipmsg_find_header(msg
, "From"));
2921 session
= find_or_create_im_session (sip
, from
);
2923 if (session
->dialog
) {
2924 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
2926 session
->dialog
= g_new0(struct sip_dialog
, 1);
2928 sipe_parse_dialog(msg
, session
->dialog
, FALSE
);
2930 session
->dialog
->callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
2931 session
->dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
2932 session
->dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
2933 session
->dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
2936 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
2941 sipmsg_remove_header(msg
, "Ms-Conversation-ID");
2942 sipmsg_remove_header(msg
, "EndPoints");
2943 sipmsg_remove_header(msg
, "User-Agent");
2945 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, BENOTIFY");
2946 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
2948 body
= g_strdup_printf(
2950 "o=- 0 0 IN IP4 0.0.0.0\r\n"
2952 "c=IN IP4 0.0.0.0\r\n"
2954 "m=message %d sip sip:%s\r\n"
2955 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
2956 sip
->realport
, sip
->username
);
2957 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
2961 static void sipe_connection_cleanup(struct sipe_account_data
*);
2962 static void create_connection(struct sipe_account_data
*, gchar
*, int);
2964 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2968 const gchar
*expires_header
;
2970 GSList
*hdr
= msg
->headers
;
2971 struct siphdrelement
*elem
;
2973 expires_header
= sipmsg_find_header(msg
, "Expires");
2974 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
2975 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
2977 switch (msg
->response
) {
2980 sip
->registerstatus
= 0;
2983 gchar
*contact_hdr
= NULL
;
2988 sip
->registerexpire
= expires
;
2990 if (!sip
->reregister_set
) {
2991 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
2992 sipe_schedule_action(action_name
, expires
, (Action
) do_register_cb
, sip
, NULL
);
2993 g_free(action_name
);
2994 sip
->reregister_set
= TRUE
;
2997 sip
->registerstatus
= 3;
2999 if (!sip
->reauthenticate_set
) {
3000 /* we have to reauthenticate as our security token expires
3001 after eight hours (be five minutes early) */
3002 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
3003 guint reauth_timeout
= (8 * 3600) - 360;
3004 sipe_schedule_action(action_name
, reauth_timeout
, (Action
) do_reauthenticate_cb
, sip
, NULL
);
3005 g_free(action_name
);
3006 sip
->reauthenticate_set
= TRUE
;
3009 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
3012 uuid
= generateUUIDfromEPID(epid
);
3015 // There can be multiple Contact headers (one per location where the user is logged in) so
3016 // make sure to only get the one for this uuid
3017 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
3018 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
3019 if (valid_contact
) {
3020 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
3021 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
3022 g_free(valid_contact
);
3025 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
3030 g_free(sip
->contact
);
3032 sip
->contact
= g_strdup_printf("<%s>", gruu
);
3035 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
3036 sip
->contact
= g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace", sip
->username
, sip
->listenport
, purple_network_get_my_ip(-1), TRANSPORT_DESCRIPTOR
);
3038 sip
->msrtc_event_categories
= FALSE
;
3043 if(!g_ascii_strcasecmp(elem
->name
, "Supported"))
3045 if (strstr(elem
->value
, "msrtc-event-categories")){
3046 sip
->msrtc_event_categories
= TRUE
;
3048 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s, %d\n", elem
->value
, sip
->msrtc_event_categories
);
3050 hdr
= g_slist_next(hdr
);
3053 if (!sip
->subscribed
) { //do it just once, not every re-register
3054 tmp
= sipmsg_find_header(msg
, "Allow-Events");
3055 sipe_options_request(sip
, sip
->sipdomain
);
3056 if (tmp
&& strstr(tmp
, "vnd-microsoft-provisioning")){
3057 sipe_subscribe_roaming_contacts(sip
, msg
);
3059 sipe_subscribe_roaming_acl(sip
, msg
);
3060 sipe_subscribe_roaming_self(sip
, msg
);
3061 sipe_subscribe_roaming_provisioning(sip
, msg
);
3062 sipe_subscribe_presence_wpending(sip
, msg
);
3063 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
3064 sip
->subscribed
= TRUE
;
3067 if (purple_account_get_bool(sip
->account
, "clientkeepalive", FALSE
)) {
3068 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Setting user defined keepalive\n");
3069 sip
->keepalive_timeout
= purple_account_get_int(sip
->account
, "keepalive", 0);
3071 tmp
= sipmsg_find_header(msg
, "ms-keep-alive");
3073 sipe_keep_alive_timeout(sip
, tmp
);
3077 // Should we remove the transaction here?
3078 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
3079 transactions_remove(sip
, tc
);
3084 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
3086 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
3087 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
3091 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
3094 tmp
= g_strsplit(parts
[0], ":", 0);
3095 hostname
= g_strdup(tmp
[0]);
3096 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
3100 tmp
= g_strsplit(parts
[i
], "=", 0);
3102 if (g_strcasecmp("transport", tmp
[0]) == 0) {
3103 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
3104 transport
= SIPE_TRANSPORT_TCP
;
3105 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
3106 transport
= SIPE_TRANSPORT_UDP
;
3115 /* Close old connection */
3116 sipe_connection_cleanup(sip
);
3118 /* Create new connection */
3119 sip
->transport
= transport
;
3120 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
3121 hostname
, port
, TRANSPORT_DESCRIPTOR
);
3122 create_connection(sip
, hostname
, port
);
3128 if (sip
->registerstatus
!= 2) {
3129 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
3130 if (sip
->registrar
.retries
> 3) {
3131 sip
->gc
->wants_to_die
= TRUE
;
3132 purple_connection_error(sip
->gc
, _("Wrong Password"));
3135 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3136 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3138 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3140 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3141 fill_auth(sip
, tmp
, &sip
->registrar
);
3142 sip
->registerstatus
= 2;
3143 if (sip
->account
->disconnecting
) {
3144 do_register_exp(sip
, 0);
3152 const gchar
*warning
= sipmsg_find_header(msg
, "Warning");
3153 if (warning
!= NULL
) {
3155 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
3157 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
3158 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
3161 warning
= _("You have been rejected by the server");
3164 sip
->gc
->wants_to_die
= TRUE
;
3165 purple_connection_error(sip
->gc
, warning
);
3171 const gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3172 if (warning
!= NULL
) {
3173 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3174 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
3177 warning
= _("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator");
3180 sip
->gc
->wants_to_die
= TRUE
;
3181 purple_connection_error(sip
->gc
, warning
);
3187 const gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3188 if (warning
!= NULL
) {
3189 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3190 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
3193 warning
= _("Service unavailable: no reason given");
3196 sip
->gc
->wants_to_die
= TRUE
;
3197 purple_connection_error(sip
->gc
, warning
);
3205 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3208 xmlnode
*xn_categories
;
3209 xmlnode
*xn_category
;
3212 const char *activity
= NULL
;
3214 xn_categories
= xmlnode_from_str(data
, len
);
3215 uri
= xmlnode_get_attrib(xn_categories
, "uri");
3217 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
3219 xn_category
= xmlnode_get_next_twin(xn_category
) )
3221 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
3223 if (!strcmp(attrVar
, "note"))
3226 struct sipe_buddy
*sbuddy
;
3227 xn_node
= xmlnode_get_child(xn_category
, "note");
3228 if (!xn_node
) continue;
3229 xn_node
= xmlnode_get_child(xn_node
, "body");
3230 if (!xn_node
) continue;
3232 note
= xmlnode_get_data(xn_node
);
3235 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3239 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3240 sbuddy
->annotation
= g_strdup(note
);
3246 else if(!strcmp(attrVar
, "state"))
3250 xn_node
= xmlnode_get_child(xn_category
, "state");
3251 if (!xn_node
) continue;
3252 xn_node
= xmlnode_get_child(xn_node
, "availability");
3253 if (!xn_node
) continue;
3255 data
= xmlnode_get_data(xn_node
);
3260 activity
= "unknown";
3261 else if (avail
< 4500)
3262 activity
= "available";
3263 else if (avail
< 6000)
3265 else if (avail
< 7500)
3267 else if (avail
< 9000)
3269 else if (avail
< 12000)
3271 else if (avail
< 18000)
3274 activity
= "offline";
3282 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n",activity
);
3283 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
3287 xmlnode_free(xn_categories
);
3290 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3292 const char *uri
,*state
;
3294 xmlnode
*xn_resource
;
3295 xmlnode
*xn_instance
;
3297 xn_list
= xmlnode_from_str(data
, len
);
3299 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
3301 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
3303 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
3304 if (!xn_instance
) return;
3306 state
= xmlnode_get_attrib(xn_instance
, "state");
3307 uri
= xmlnode_get_attrib(xn_instance
, "cid");
3308 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n",uri
,state
);
3309 if(strstr(state
,"resubscribe")){
3310 sipe_subscribe_presence_single(sip
, uri
);
3315 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3319 gchar
*activity
= NULL
;
3321 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
3322 gboolean isonline
= FALSE
;
3323 xmlnode
*display_name_node
;
3325 pidf
= xmlnode_from_str(data
, len
);
3327 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data
);
3331 uri
= xmlnode_get_attrib(pidf
, "entity");
3333 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
3335 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3336 basicstatus
= xmlnode_get_child(status
, "basic");
3341 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
3346 getbasic
= xmlnode_get_data(basicstatus
);
3348 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
3353 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
3354 if (strstr(getbasic
, "open")) {
3359 display_name_node
= xmlnode_get_child(pidf
, "display-name");
3360 // updating display name if alias was just URI
3361 if (display_name_node
) {
3362 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
3363 GSList
*entry
= buddies
;
3364 PurpleBuddy
*p_buddy
;
3365 char * display_name
= xmlnode_get_data(display_name_node
);
3368 const char *server_alias
;
3371 p_buddy
= entry
->data
;
3373 alias
= (char *)purple_buddy_get_alias(p_buddy
);
3374 alias
= alias
? g_strdup_printf("sip:%s", alias
) : NULL
;
3375 if (!alias
|| !g_ascii_strcasecmp(uri
, alias
)) {
3376 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
3377 purple_blist_alias_buddy(p_buddy
, display_name
);
3381 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3383 ( (server_alias
&& strcmp(display_name
, server_alias
))
3384 || !server_alias
|| strlen(server_alias
) == 0 )
3386 purple_blist_server_alias_buddy(p_buddy
, display_name
);
3389 entry
= entry
->next
;
3391 g_free(display_name
);
3394 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
3395 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3396 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
3397 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
3398 activity
= xmlnode_get_data(basicstatus
);
3399 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
3406 gchar
* status_id
= NULL
;
3408 if (strstr(activity
, "busy")) {
3410 } else if (strstr(activity
, "away")) {
3416 status_id
= "available";
3419 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
3420 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
3422 purple_prpl_got_user_status(sip
->account
, uri
, "offline", NULL
);
3429 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3431 const char *availability
;
3432 const char *activity
;
3433 const char *display_name
= NULL
;
3434 const char *activity_name
;
3439 struct sipe_buddy
*sbuddy
;
3441 xmlnode
*xn_presentity
= xmlnode_from_str(data
, len
);
3443 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
3444 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
3445 xmlnode
*xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
3446 xmlnode
*xn_email
= xmlnode_get_child(xn_presentity
, "email");
3447 const char *email
= xn_email
? xmlnode_get_attrib(xn_email
, "email") : NULL
;
3448 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
3449 xmlnode
*xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
3450 char *note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
3451 xmlnode
*xn_devices
= xmlnode_get_child(xn_presentity
, "devices");
3452 xmlnode
*xn_device_presence
= xn_devices
? xmlnode_get_child(xn_devices
, "devicePresence") : NULL
;
3453 xmlnode
*xn_device_name
= xn_device_presence
? xmlnode_get_child(xn_device_presence
, "deviceName") : NULL
;
3454 const char *device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
3456 name
= xmlnode_get_attrib(xn_presentity
, "uri");
3457 uri
= g_strdup_printf("sip:%s", name
);
3458 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
3459 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
3461 // updating display name if alias was just URI
3462 if (xn_display_name
) {
3463 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
3464 GSList
*entry
= buddies
;
3465 PurpleBuddy
*p_buddy
;
3466 display_name
= xmlnode_get_attrib(xn_display_name
, "displayName");
3469 const char *email_str
, *server_alias
;
3471 p_buddy
= entry
->data
;
3473 if (!g_ascii_strcasecmp(name
, purple_buddy_get_alias(p_buddy
))) {
3474 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
3475 purple_blist_alias_buddy(p_buddy
, display_name
);
3478 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3480 ( (server_alias
&& strcmp(display_name
, server_alias
))
3481 || !server_alias
|| strlen(server_alias
) == 0 )
3483 purple_blist_server_alias_buddy(p_buddy
, display_name
);
3487 email_str
= purple_blist_node_get_string((PurpleBlistNode
*)p_buddy
, "email");
3488 if (!email_str
|| g_ascii_strcasecmp(email_str
, email
)) {
3489 purple_blist_node_set_string((PurpleBlistNode
*)p_buddy
, "email", email
);
3493 entry
= entry
->next
;
3497 avl
= atoi(availability
);
3498 act
= atoi(activity
);
3501 activity_name
= "away";
3502 else if (act
<= 150)
3503 activity_name
= "out-to-lunch";
3504 else if (act
<= 300)
3505 activity_name
= "be-right-back";
3506 else if (act
<= 400)
3507 activity_name
= "available";
3508 else if (act
<= 500)
3509 activity_name
= "on-the-phone";
3510 else if (act
<= 600)
3511 activity_name
= "busy";
3513 activity_name
= "available";
3516 activity_name
= "offline";
3518 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3521 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3522 sbuddy
->annotation
= NULL
;
3523 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
3525 if (sbuddy
->device_name
) { g_free(sbuddy
->device_name
); }
3526 sbuddy
->device_name
= NULL
;
3527 if (device_name
) { sbuddy
->device_name
= g_strdup(device_name
); }
3530 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
3531 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
3533 xmlnode_free(xn_presentity
);
3537 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3539 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
3541 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
3543 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
3544 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
3546 const char *content
= msg
->body
;
3547 unsigned length
= msg
->bodylen
;
3548 PurpleMimeDocument
*mime
= NULL
;
3550 if (strstr(ctype
, "multipart"))
3552 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
3553 const char *content_type
;
3555 mime
= purple_mime_document_parse(doc
);
3556 parts
= purple_mime_document_get_parts(mime
);
3558 content
= purple_mime_part_get_data(parts
->data
);
3559 length
= purple_mime_part_get_length(parts
->data
);
3560 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
3561 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
3563 process_incoming_notify_rlmi_resub(sip
, content
, length
);
3565 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
3567 process_incoming_notify_msrtc(sip
, content
, length
);
3571 process_incoming_notify_rlmi(sip
, content
, length
);
3573 parts
= parts
->next
;
3579 purple_mime_document_free(mime
);
3582 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
3584 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
3586 else if(strstr(ctype
, "application/rlmi+xml"))
3588 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
3591 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
3593 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
3597 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
3602 * Dispatcher for all incoming subscription information
3603 * whether it comes from NOTIFY, BENOTIFY requests or
3604 * piggy-backed to subscription's OK responce.
3606 * @param request whether initiated from BE/NOTIFY request or OK-response message.
3607 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
3609 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
3611 gchar
*event
= sipmsg_find_header(msg
, "Event");
3612 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
3613 const char *uri
,*state
;
3615 xmlnode
*xn_resource
;
3616 xmlnode
*xn_instance
;
3620 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event
? event
: "", msg
->body
);
3621 purple_debug_info("sipe", "process_incoming_notify: subscription_state:%s\n\n", subscription_state
);
3625 const gchar
*expires_header
;
3626 expires_header
= sipmsg_find_header(msg
, "Expires");
3627 expires
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
3628 purple_debug_info("sipe", "process_incoming_notify: expires:%d\n\n", expires
);
3631 if (!subscription_state
|| strstr(subscription_state
, "active"))
3633 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
3635 sipe_process_presence(sip
, msg
);
3637 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
3639 sipe_process_roaming_contacts(sip
, msg
, NULL
);
3641 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self"))
3643 sipe_process_roaming_self(sip
, msg
);
3645 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning-v2"))
3648 purple_debug_info("sipe", "vnd-microsoft-provisioning-v2 data is not supported yet.");
3650 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
3652 sipe_process_roaming_acl(sip
, msg
);
3654 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
3656 sipe_process_presence_wpending(sip
, msg
);
3660 purple_debug_info("sipe", "Unable to process (BE)NOTIFY. Event is not supported:%s\n", event
? event
: "");
3664 //The server sends a (BE)NOTIFY with the status 'terminated'
3665 if(request
&& subscription_state
&& strstr(subscription_state
, "terminated") )
3667 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3668 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from
);
3672 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
3673 if (request
&& !benotify
)
3675 sipmsg_remove_header(msg
, "Expires");
3676 sipmsg_remove_header(msg
, "subscription-state");
3677 sipmsg_remove_header(msg
, "Event");
3678 sipmsg_remove_header(msg
, "Require");
3679 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3686 static gchar* gen_xpidf(struct sipe_account_data *sip)
3688 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3690 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
3691 "<display name=\"sip:%s\"/>\r\n"
3692 "<atom id=\"1234\">\r\n"
3693 "<address uri=\"sip:%s\">\r\n"
3694 "<status status=\"%s\"/>\r\n"
3707 static gchar* gen_pidf(struct sipe_account_data *sip)
3709 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3710 "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" xmlns:ep=\"urn:ietf:params:xml:ns:pidf:status:rpid-status\" xmlns:ci=\"urn:ietf:params:xml:ns:pidf:cipid\" entity=\"sip:%s\">\r\n"
3711 "<tuple id=\"0\">\r\n"
3713 "<basic>open</basic>\r\n"
3714 "<ep:activities>\r\n"
3715 " <ep:activity>%s</ep:activity>\r\n"
3719 "<ci:display-name>%s</ci:display-name>\r\n"
3729 process_send_presence_info_v0_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3731 if (msg
->response
== 488) {
3732 sip
->presence_method_version
= 1;
3733 send_presence_info(sip
);
3738 static void send_presence_info_v0(struct sipe_account_data
*sip
, const char * note
)
3740 int availability
= 300; // online
3741 int activity
= 400; // Available
3744 if (!strcmp(sip
->status
, "away")) {
3746 } else if (!strcmp(sip
->status
, "out-to-lunch")) {
3748 } else if (!strcmp(sip
->status
, "be-right-back")) {
3750 } else if (!strcmp(sip
->status
, "on-the-phone")) {
3752 } else if (!strcmp(sip
->status
, "do-not-disturb")) {
3754 } else if (!strcmp(sip
->status
, "busy")) {
3756 } else if (!strcmp(sip
->status
, "invisible")) {
3757 availability
= 0; // offline
3761 name
= g_strdup_printf("sip: sip:%s", sip
->username
);
3762 //@TODO: send user data - state; add hostname in upper case
3763 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
3764 send_soap_request_with_cb(sip
, body
, process_send_presence_info_v0_response
, NULL
);
3770 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3772 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
3773 if (msg
->response
== 200) {
3774 sip
->status_version
= 0;
3775 send_presence_info(sip
);
3781 process_send_presence_info_v1_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3783 if (msg
->response
== 409) {
3784 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
3785 // TODO need to parse the version #'s?
3786 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
3787 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
3791 purple_debug_info("sipe", "process_send_presence_info_v1_response = %s\n", msg
->body
);
3793 tmp
= get_contact(sip
);
3794 hdr
= g_strdup_printf("Contact: %s\r\n"
3795 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
3797 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
3807 static void send_presence_info_v1(struct sipe_account_data
*sip
, const char * note
)
3814 if (!strcmp(sip
->status
, "away")) {
3816 } else if (!strcmp(sip
->status
, "busy")) {
3823 uri
= g_strdup_printf("sip:%s", sip
->username
);
3824 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
3825 sip
->status_version
, code
,
3826 sip
->status_version
, code
,
3827 sip
->status_version
, note
? note
: "",
3828 sip
->status_version
, note
? note
: "",
3829 sip
->status_version
, note
? note
: ""
3831 sip
->status_version
++;
3833 tmp
= get_contact(sip
);
3834 hdr
= g_strdup_printf("Contact: %s\r\n"
3835 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
3837 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_info_v1_response
);
3845 static void send_presence_info(struct sipe_account_data
*sip
)
3847 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
3849 if (!status
) return;
3851 note
= purple_status_get_attr_string(status
, "message");
3853 purple_debug_info("sipe", "sending presence info, version = %d\n", sip
->presence_method_version
);
3854 if (sip
->presence_method_version
!= 1) {
3855 send_presence_info_v0(sip
, note
);
3857 send_presence_info_v1(sip
, note
);
3861 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
3863 gboolean found
= FALSE
;
3864 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
3865 if (msg
->response
== 0) { /* request */
3866 if (!strcmp(msg
->method
, "MESSAGE")) {
3867 process_incoming_message(sip
, msg
);
3869 } else if (!strcmp(msg
->method
, "NOTIFY")) {
3870 purple_debug_info("sipe","send->process_incoming_notify\n");
3871 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
3873 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
3874 purple_debug_info("sipe","send->process_incoming_benotify\n");
3875 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
3877 } else if (!strcmp(msg
->method
, "INVITE")) {
3878 process_incoming_invite(sip
, msg
);
3880 } else if (!strcmp(msg
->method
, "OPTIONS")) {
3881 process_incoming_options(sip
, msg
);
3883 } else if (!strcmp(msg
->method
, "INFO")) {
3885 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3887 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
3890 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3892 } else if (!strcmp(msg
->method
, "ACK")) {
3893 // ACK's don't need any response
3895 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
3896 // LCS 2005 sends us these - just respond 200 OK
3898 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3899 } else if (!strcmp(msg
->method
, "BYE")) {
3900 struct sip_im_session
*session
;
3902 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3904 from
= parse_from(sipmsg_find_header(msg
, "From"));
3905 session
= find_im_session (sip
, from
);
3909 // TODO Let the user know the other user left the conversation?
3910 im_session_destroy(sip
, session
);
3915 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3917 } else { /* response */
3918 struct transaction
*trans
= transactions_find(sip
, msg
);
3920 if (msg
->response
== 407) {
3921 gchar
*resend
, *auth
, *ptmp
;
3923 if (sip
->proxy
.retries
> 30) return;
3924 sip
->proxy
.retries
++;
3925 /* do proxy authentication */
3927 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
3929 fill_auth(sip
, ptmp
, &sip
->proxy
);
3930 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
3931 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
3932 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
3934 resend
= sipmsg_to_string(trans
->msg
);
3935 /* resend request */
3936 sendout_pkt(sip
->gc
, resend
);
3939 if (msg
->response
== 100 || msg
->response
== 180) {
3940 /* ignore provisional response */
3941 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
3943 sip
->proxy
.retries
= 0;
3944 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
3945 if (msg
->response
== 401)
3947 sip
->registrar
.retries
++;
3948 sip
->registrar
.expires
= 0;
3952 sip
->registrar
.retries
= 0;
3954 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
3956 if (msg
->response
== 401) {
3957 gchar
*resend
, *auth
, *ptmp
;
3959 if (sip
->registrar
.retries
> 4) return;
3960 sip
->registrar
.retries
++;
3962 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3963 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
3965 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3968 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
3970 fill_auth(sip
, ptmp
, &sip
->registrar
);
3971 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
3972 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
3973 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
3975 //sipmsg_remove_header(trans->msg, "Authorization");
3976 //sipmsg_add_header(trans->msg, "Authorization", auth);
3978 resend
= sipmsg_to_string(trans
->msg
);
3979 /* resend request */
3980 sendout_pkt(sip
->gc
, resend
);
3985 if (trans
->callback
) {
3986 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
3987 /* call the callback to process response*/
3988 (trans
->callback
)(sip
, msg
, trans
);
3990 /* Not sure if this is needed or what needs to be done
3991 but transactions seem to be removed prematurely so
3992 this only removes them if the response is 200 OK */
3993 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
3994 /*Has a bug and it's unneccesary*/
3995 /*transactions_remove(sip, trans);*/
4001 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction");
4005 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
4009 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
4017 /* according to the RFC remove CRLF at the beginning */
4018 while (*cur
== '\r' || *cur
== '\n') {
4021 if (cur
!= conn
->inbuf
) {
4022 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
4023 conn
->inbufused
= strlen(conn
->inbuf
);
4026 /* Received a full Header? */
4027 sip
->processing_input
= TRUE
;
4028 while (sip
->processing_input
&&
4029 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
4030 time_t currtime
= time(NULL
);
4033 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
4034 msg
= sipmsg_parse_header(conn
->inbuf
);
4037 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
4038 if (restlen
>= msg
->bodylen
) {
4039 dummy
= g_malloc(msg
->bodylen
+ 1);
4040 memcpy(dummy
, cur
, msg
->bodylen
);
4041 dummy
[msg
->bodylen
] = '\0';
4043 cur
+= msg
->bodylen
;
4044 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
4045 conn
->inbufused
= strlen(conn
->inbuf
);
4047 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
4048 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
4054 purple_debug_info("sipe", "body:\n%s", msg->body);
4057 // Verify the signature before processing it
4058 if (sip
->registrar
.ntlm_key
) {
4059 struct sipmsg_breakdown msgbd
;
4060 gchar
*signature_input_str
;
4061 gchar
*signature
= NULL
;
4064 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
4065 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
4066 if (signature_input_str
!= NULL
) {
4067 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
4069 g_free(signature_input_str
);
4071 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
4073 if (signature
!= NULL
) {
4074 if (rspauth
!= NULL
) {
4075 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
4076 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
4077 process_input_message(sip
, msg
);
4079 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
4080 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
4081 sip
->gc
->wants_to_die
= TRUE
;
4083 } else if (msg
->response
== 401) {
4084 purple_connection_error(sip
->gc
, _("Wrong Password"));
4085 sip
->gc
->wants_to_die
= TRUE
;
4091 sipmsg_breakdown_free(&msgbd
);
4093 process_input_message(sip
, msg
);
4100 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
4102 PurpleConnection
*gc
= data
;
4103 struct sipe_account_data
*sip
= gc
->proto_data
;
4108 static char buffer
[65536];
4109 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
4111 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
4112 msg
= sipmsg_parse_msg(buffer
);
4113 if (msg
) process_input_message(sip
, msg
);
4117 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
4119 struct sipe_account_data
*sip
= gc
->proto_data
;
4120 PurpleSslConnection
*gsc
= sip
->gsc
;
4122 purple_debug_error("sipe", "%s",debug
);
4123 purple_connection_error(gc
, msg
);
4125 /* Invalidate this connection. Next send will open a new one */
4127 connection_remove(sip
, gsc
->fd
);
4128 purple_ssl_close(gsc
);
4134 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
4136 PurpleConnection
*gc
= data
;
4137 struct sipe_account_data
*sip
;
4138 struct sip_connection
*conn
;
4140 gboolean firstread
= TRUE
;
4142 /* NOTE: This check *IS* necessary */
4143 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
4144 purple_ssl_close(gsc
);
4148 sip
= gc
->proto_data
;
4149 conn
= connection_find(sip
, gsc
->fd
);
4151 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
4152 gc
->wants_to_die
= TRUE
;
4153 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
4157 /* Read all available data from the SSL connection */
4159 /* Increase input buffer size as needed */
4160 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
4161 conn
->inbuflen
+= SIMPLE_BUF_INC
;
4162 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
4163 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
4166 /* Try to read as much as there is space left in the buffer */
4167 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
4168 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
4170 if (len
< 0 && errno
== EAGAIN
) {
4171 /* Try again later */
4173 } else if (len
< 0) {
4174 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
4176 } else if (firstread
&& (len
== 0)) {
4177 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
4181 conn
->inbufused
+= len
;
4184 /* Equivalence indicates that there is possibly more data to read */
4185 } while (len
== readlen
);
4187 conn
->inbuf
[conn
->inbufused
] = '\0';
4188 process_input(sip
, conn
);
4192 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
4194 PurpleConnection
*gc
= data
;
4195 struct sipe_account_data
*sip
= gc
->proto_data
;
4197 struct sip_connection
*conn
= connection_find(sip
, source
);
4199 purple_debug_error("sipe", "Connection not found!\n");
4203 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
4204 conn
->inbuflen
+= SIMPLE_BUF_INC
;
4205 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
4208 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
4210 if (len
< 0 && errno
== EAGAIN
)
4212 else if (len
<= 0) {
4213 purple_debug_info("sipe", "sipe_input_cb: read error\n");
4214 connection_remove(sip
, source
);
4215 if (sip
->fd
== source
) sip
->fd
= -1;
4219 conn
->inbufused
+= len
;
4220 conn
->inbuf
[conn
->inbufused
] = '\0';
4222 process_input(sip
, conn
);
4225 /* Callback for new connections on incoming TCP port */
4226 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
4228 PurpleConnection
*gc
= data
;
4229 struct sipe_account_data
*sip
= gc
->proto_data
;
4230 struct sip_connection
*conn
;
4232 int newfd
= accept(source
, NULL
, NULL
);
4234 conn
= connection_create(sip
, newfd
);
4236 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4239 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
4241 PurpleConnection
*gc
= data
;
4242 struct sipe_account_data
*sip
;
4243 struct sip_connection
*conn
;
4245 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4253 purple_connection_error(gc
, _("Could not connect"));
4257 sip
= gc
->proto_data
;
4259 sip
->last_keepalive
= time(NULL
);
4261 conn
= connection_create(sip
, source
);
4265 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4268 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
4270 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
4271 if (sip
== NULL
) return;
4276 static guint
sipe_ht_hash_nick(const char *nick
)
4278 char *lc
= g_utf8_strdown(nick
, -1);
4279 guint bucket
= g_str_hash(lc
);
4285 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
4287 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
4290 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
4292 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4294 sip
->listen_data
= NULL
;
4296 if (listenfd
== -1) {
4297 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4303 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
4304 sip
->listenfd
= sip
->fd
;
4306 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
4308 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
4312 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
4314 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4317 sip
->query_data
= NULL
;
4319 if (!hosts
|| !hosts
->data
) {
4320 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
4324 addr_size
= GPOINTER_TO_INT(hosts
->data
);
4325 hosts
= g_slist_remove(hosts
, hosts
->data
);
4326 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
4327 g_free(hosts
->data
);
4328 hosts
= g_slist_remove(hosts
, hosts
->data
);
4330 hosts
= g_slist_remove(hosts
, hosts
->data
);
4331 g_free(hosts
->data
);
4332 hosts
= g_slist_remove(hosts
, hosts
->data
);
4335 /* create socket for incoming connections */
4336 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
4337 sipe_udp_host_resolved_listen_cb
, sip
);
4338 if (sip
->listen_data
== NULL
) {
4339 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4344 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
4347 PurpleConnection
*gc
= data
;
4348 struct sipe_account_data
*sip
;
4350 /* If the connection is already disconnected, we don't need to do anything else */
4351 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4354 sip
= gc
->proto_data
;
4359 case PURPLE_SSL_CONNECT_FAILED
:
4360 purple_connection_error(gc
, _("Connection Failed"));
4362 case PURPLE_SSL_HANDSHAKE_FAILED
:
4363 purple_connection_error(gc
, _("SSL Handshake Failed"));
4365 case PURPLE_SSL_CERTIFICATE_INVALID
:
4366 purple_connection_error(gc
, _("SSL Certificate Invalid"));
4372 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
4374 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4375 PurpleProxyConnectData
*connect_data
;
4377 sip
->listen_data
= NULL
;
4379 sip
->listenfd
= listenfd
;
4380 if (sip
->listenfd
== -1) {
4381 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4385 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
4386 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
4387 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
4388 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
4389 sipe_newconn_cb
, sip
->gc
);
4390 purple_debug_info("sipe", "connecting to %s port %d\n",
4391 sip
->realhostname
, sip
->realport
);
4392 /* open tcp connection to the server */
4393 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
4394 sip
->realport
, login_cb
, sip
->gc
);
4396 if (connect_data
== NULL
) {
4397 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
4402 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
4404 PurpleAccount
*account
= sip
->account
;
4405 PurpleConnection
*gc
= sip
->gc
;
4407 if (purple_account_get_bool(account
, "useport", FALSE
)) {
4408 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
4409 port
= purple_account_get_int(account
, "port", 0);
4411 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
4414 sip
->realhostname
= hostname
;
4415 sip
->realport
= port
;
4417 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
4420 /* TODO: is there a good default grow size? */
4421 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
4422 sip
->txbuf
= purple_circ_buffer_new(0);
4424 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
4426 if (!purple_ssl_is_supported()) {
4427 gc
->wants_to_die
= TRUE
;
4428 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
4432 purple_debug_info("sipe", "using SSL\n");
4434 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
4435 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
4436 if (sip
->gsc
== NULL
) {
4437 purple_connection_error(gc
, _("Could not create SSL context"));
4440 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
4442 purple_debug_info("sipe", "using UDP\n");
4444 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
4445 if (sip
->query_data
== NULL
) {
4446 purple_connection_error(gc
, _("Could not resolve hostname"));
4450 purple_debug_info("sipe", "using TCP\n");
4451 /* create socket for incoming connections */
4452 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
4453 sipe_tcp_connect_listen_cb
, sip
);
4454 if (sip
->listen_data
== NULL
) {
4455 purple_connection_error(gc
, _("Could not create listen socket"));
4461 /* Service list for autodection */
4462 static const struct sipe_service_data service_autodetect
[] = {
4463 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4464 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4465 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4466 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4470 /* Service list for SSL/TLS */
4471 static const struct sipe_service_data service_tls
[] = {
4472 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4473 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4477 /* Service list for TCP */
4478 static const struct sipe_service_data service_tcp
[] = {
4479 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4480 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4484 /* Service list for UDP */
4485 static const struct sipe_service_data service_udp
[] = {
4486 { "sip", "udp", SIPE_TRANSPORT_UDP
},
4490 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
4491 static void resolve_next_service(struct sipe_account_data
*sip
,
4492 const struct sipe_service_data
*start
)
4495 sip
->service_data
= start
;
4497 sip
->service_data
++;
4498 if (sip
->service_data
->service
== NULL
) {
4500 /* Try connecting to the SIP hostname directly */
4501 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
4502 if (sip
->auto_transport
) {
4503 // If SSL is supported, default to using it; OCS servers aren't configured
4504 // by default to accept TCP
4505 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
4506 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
4507 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
4510 hostname
= g_strdup(sip
->sipdomain
);
4511 create_connection(sip
, hostname
, 0);
4516 /* Try to resolve next service */
4517 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
4518 sip
->service_data
->transport
,
4523 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
4525 struct sipe_account_data
*sip
= data
;
4527 sip
->srv_query_data
= NULL
;
4529 /* find the host to connect to */
4531 gchar
*hostname
= g_strdup(resp
->hostname
);
4532 int port
= resp
->port
;
4533 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
4537 sip
->transport
= sip
->service_data
->type
;
4539 create_connection(sip
, hostname
, port
);
4541 resolve_next_service(sip
, NULL
);
4545 static void sipe_login(PurpleAccount
*account
)
4547 PurpleConnection
*gc
;
4548 struct sipe_account_data
*sip
;
4549 gchar
**signinname_login
, **userserver
, **domain_user
;
4550 const char *transport
;
4552 const char *username
= purple_account_get_username(account
);
4553 gc
= purple_account_get_connection(account
);
4555 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
4556 gc
->wants_to_die
= TRUE
;
4557 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
4561 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
4562 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
4563 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
4565 sip
->account
= account
;
4566 sip
->registerexpire
= 900;
4567 sip
->reregister_set
= FALSE
;
4568 sip
->reauthenticate_set
= FALSE
;
4569 sip
->subscribed
= FALSE
;
4570 sip
->subscribed_buddies
= FALSE
;
4572 signinname_login
= g_strsplit(username
, ",", 2);
4574 userserver
= g_strsplit(signinname_login
[0], "@", 2);
4575 purple_connection_set_display_name(gc
, userserver
[0]);
4576 sip
->username
= g_strjoin("@", userserver
[0], userserver
[1], NULL
);
4577 sip
->sipdomain
= g_strdup(userserver
[1]);
4579 domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
4580 sip
->authdomain
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[0]) : NULL
;
4581 sip
->authuser
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[1]) : (signinname_login
? g_strdup(signinname_login
[1]) : NULL
);
4583 sip
->password
= g_strdup(purple_connection_get_password(gc
));
4585 g_strfreev(userserver
);
4586 g_strfreev(domain_user
);
4587 g_strfreev(signinname_login
);
4589 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
4591 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
4593 /* TODO: Set the status correctly. */
4594 sip
->status
= g_strdup("available");
4596 transport
= purple_account_get_string(account
, "transport", "auto");
4597 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
4598 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
4601 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
4602 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
4603 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
4604 } else if (strcmp(transport
, "auto") == 0) {
4605 sip
->auto_transport
= TRUE
;
4606 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
4607 } else if (strcmp(transport
, "tls") == 0) {
4608 resolve_next_service(sip
, service_tls
);
4609 } else if (strcmp(transport
, "tcp") == 0) {
4610 resolve_next_service(sip
, service_tcp
);
4612 resolve_next_service(sip
, service_udp
);
4616 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
4618 connection_free_all(sip
);
4620 if (sip
->query_data
!= NULL
)
4621 purple_dnsquery_destroy(sip
->query_data
);
4622 sip
->query_data
= NULL
;
4624 if (sip
->srv_query_data
!= NULL
)
4625 purple_srv_cancel(sip
->srv_query_data
);
4626 sip
->srv_query_data
= NULL
;
4628 if (sip
->listen_data
!= NULL
)
4629 purple_network_listen_cancel(sip
->listen_data
);
4630 sip
->listen_data
= NULL
;
4632 if (sip
->gsc
!= NULL
)
4633 purple_ssl_close(sip
->gsc
);
4636 sipe_auth_free(&sip
->registrar
);
4637 sipe_auth_free(&sip
->proxy
);
4640 purple_circ_buffer_destroy(sip
->txbuf
);
4643 g_free(sip
->realhostname
);
4644 sip
->realhostname
= NULL
;
4647 purple_input_remove(sip
->listenpa
);
4649 if (sip
->tx_handler
)
4650 purple_input_remove(sip
->tx_handler
);
4651 sip
->tx_handler
= 0;
4652 if (sip
->resendtimeout
)
4653 purple_timeout_remove(sip
->resendtimeout
);
4654 sip
->resendtimeout
= 0;
4655 if (sip
->timeouts
) {
4656 GSList
*entry
= sip
->timeouts
;
4658 struct scheduled_action
*sched_action
= entry
->data
;
4659 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
4660 purple_timeout_remove(sched_action
->timeout_handler
);
4661 g_free(sched_action
->payload
);
4662 g_free(sched_action
->name
);
4663 g_free(sched_action
);
4664 entry
= entry
->next
;
4667 g_slist_free(sip
->timeouts
);
4669 g_slist_free(sip
->allow_events
);
4672 g_free(sip
->contact
);
4673 sip
->contact
= NULL
;
4675 g_free(sip
->regcallid
);
4676 sip
->regcallid
= NULL
;
4679 sip
->processing_input
= FALSE
;
4683 * A callback for g_hash_table_foreach_remove
4685 static gboolean
sipe_buddy_remove(gpointer key
, struct sipe_buddy
*buddy
, gpointer user_data
)
4687 sipe_free_buddy(buddy
);
4690 static void sipe_close(PurpleConnection
*gc
)
4692 struct sipe_account_data
*sip
= gc
->proto_data
;
4695 /* leave all conversations */
4696 im_session_close_all(sip
);
4699 do_register_exp(sip
, 0);
4701 sipe_connection_cleanup(sip
);
4702 g_free(sip
->sipdomain
);
4703 g_free(sip
->username
);
4704 g_free(sip
->password
);
4705 g_free(sip
->authdomain
);
4706 g_free(sip
->authuser
);
4707 g_free(sip
->status
);
4709 g_hash_table_foreach_remove(sip
->buddies
, (GHRFunc
) sipe_buddy_remove
, NULL
);
4710 g_hash_table_destroy(sip
->buddies
);
4712 g_free(gc
->proto_data
);
4713 gc
->proto_data
= NULL
;
4716 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
4718 PurpleAccount
*acct
= purple_connection_get_account(gc
);
4719 char *id
= g_strdup_printf("sip:%s", (char *)g_list_nth_data(row
, 0));
4720 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
4722 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
4723 purple_conversation_present(conv
);
4727 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
4730 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
4731 g_list_nth_data(row
, 0), NULL
, g_list_nth_data(row
, 1));
4734 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,struct transaction
*tc
)
4736 PurpleNotifySearchResults
*results
;
4737 PurpleNotifySearchColumn
*column
;
4738 xmlnode
*searchResults
;
4740 int match_count
= 0;
4741 gboolean more
= FALSE
;
4744 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
4746 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4747 if (!searchResults
) {
4748 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
4752 results
= purple_notify_searchresults_new();
4754 if (results
== NULL
) {
4755 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
4756 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
4758 xmlnode_free(searchResults
);
4762 column
= purple_notify_searchresults_column_new(_("User Name"));
4763 purple_notify_searchresults_column_add(results
, column
);
4765 column
= purple_notify_searchresults_column_new(_("Name"));
4766 purple_notify_searchresults_column_add(results
, column
);
4768 column
= purple_notify_searchresults_column_new(_("Company"));
4769 purple_notify_searchresults_column_add(results
, column
);
4771 column
= purple_notify_searchresults_column_new(_("Country"));
4772 purple_notify_searchresults_column_add(results
, column
);
4774 column
= purple_notify_searchresults_column_new(_("Email"));
4775 purple_notify_searchresults_column_add(results
, column
);
4777 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
4780 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
4781 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
4782 g_strfreev(uri_parts
);
4784 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
4785 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
4786 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
4787 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
4789 purple_notify_searchresults_row_add(results
, row
);
4793 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
4794 char *data
= xmlnode_get_data_unescaped(mrow
);
4795 more
= (g_strcasecmp(data
, "true") == 0);
4799 secondary
= g_strdup_printf(
4800 dngettext(GETTEXT_PACKAGE
,
4801 "Found %d contact%s:",
4802 "Found %d contacts%s:", match_count
),
4803 match_count
, more
? _(" (more matched your query)") : "");
4805 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
4806 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
4807 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
4810 xmlnode_free(searchResults
);
4814 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
4816 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
4817 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
4821 PurpleRequestField
*field
= entries
->data
;
4822 const char *id
= purple_request_field_get_id(field
);
4823 const char *value
= purple_request_field_string_get_value(field
);
4825 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
4827 if (value
!= NULL
) attrs
[i
++] = g_strdup_printf(SIPE_SOAP_SEARCH_ROW
, id
, value
);
4828 } while ((entries
= g_list_next(entries
)) != NULL
);
4832 gchar
*query
= g_strjoinv(NULL
, attrs
);
4833 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
4834 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
4835 send_soap_request_with_cb(gc
->proto_data
, body
,
4836 (TransCallback
) process_search_contact_response
, NULL
);
4844 static void sipe_show_find_contact(PurplePluginAction
*action
)
4846 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
4847 PurpleRequestFields
*fields
;
4848 PurpleRequestFieldGroup
*group
;
4849 PurpleRequestField
*field
;
4851 fields
= purple_request_fields_new();
4852 group
= purple_request_field_group_new(NULL
);
4853 purple_request_fields_add_group(fields
, group
);
4855 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
4856 purple_request_field_group_add_field(group
, field
);
4857 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
4858 purple_request_field_group_add_field(group
, field
);
4859 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
4860 purple_request_field_group_add_field(group
, field
);
4861 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
4862 purple_request_field_group_add_field(group
, field
);
4864 purple_request_fields(gc
,
4866 _("Search for a Contact"),
4867 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
4869 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
4871 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
4874 GList
*sipe_actions(PurplePlugin
*plugin
, gpointer context
)
4877 PurplePluginAction
*act
;
4879 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
4880 menu
= g_list_prepend(menu
, act
);
4882 menu
= g_list_reverse(menu
);
4887 static void dummy_permit_deny(PurpleConnection
*gc
)
4891 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
4897 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
4903 static char *sipe_status_text(PurpleBuddy
*buddy
)
4905 struct sipe_account_data
*sip
;
4906 struct sipe_buddy
*sbuddy
;
4909 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
4910 if (sip
) //happens on pidgin exit
4912 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
4913 if (sbuddy
&& sbuddy
->annotation
)
4915 text
= g_strdup(sbuddy
->annotation
);
4922 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
4924 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
4925 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
4926 struct sipe_account_data
*sip
;
4927 struct sipe_buddy
*sbuddy
;
4928 char *annotation
= NULL
;
4930 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
4931 if (sip
) //happens on pidgin exit
4933 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
4936 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
4941 if (purple_presence_is_online(presence
))
4943 purple_notify_user_info_add_pair(user_info
, _("Status"), purple_status_get_name(status
));
4948 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
4955 sipe_get_account_text_table(PurpleAccount
*account
)
4958 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
4959 g_hash_table_insert(table
, "login_label", (gpointer
)_("Sign-In Name..."));
4963 static PurpleBuddy
*
4964 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
4967 const gchar
*server_alias
, *email
;
4968 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
4970 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
4972 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
4974 server_alias
= g_strdup(purple_buddy_get_server_alias(buddy
));
4976 purple_blist_server_alias_buddy(clone
, server_alias
);
4979 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
4981 purple_blist_node_set_string((PurpleBlistNode
*)clone
, "email", email
);
4984 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
4986 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
4991 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
4993 PurpleBuddy
*buddy
, *b
;
4994 PurpleConnection
*gc
;
4995 PurpleGroup
* group
= purple_find_group(group_name
);
4997 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
4999 buddy
= (PurpleBuddy
*)node
;
5001 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
5002 gc
= purple_account_get_connection(buddy
->account
);
5004 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
5006 b
= purple_blist_add_buddy_clone(group
, buddy
);
5009 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
5013 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
5016 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
5018 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
5021 char *mailto
= g_strdup_printf("mailto:%s", email
);
5022 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
5026 char *const parmList
[] = {mailto
, NULL
};
5027 if ((pid
= fork()) == -1)
5029 purple_debug_info("sipe", "fork() error\n");
5033 execvp("xdg-email", parmList
);
5034 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
5042 //@TODO resolve env variable %WINDIR% first
5043 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
5046 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
5055 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
5060 * A menu which appear when right-clicking on buddy in contact list.
5063 sipe_buddy_menu(PurpleBuddy
*buddy
)
5065 PurpleBlistNode
*g_node
;
5066 PurpleGroup
*group
, *gr_parent
;
5067 PurpleMenuAction
*act
;
5069 GList
*menu_groups
= NULL
;
5071 act
= purple_menu_action_new(_("Send Email..."),
5072 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
5074 menu
= g_list_prepend(menu
, act
);
5076 gr_parent
= purple_buddy_get_group(buddy
);
5077 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
5078 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
5081 group
= (PurpleGroup
*)g_node
;
5082 if (group
== gr_parent
)
5085 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
5088 act
= purple_menu_action_new(purple_group_get_name(group
),
5089 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
5091 menu_groups
= g_list_prepend(menu_groups
, act
);
5093 menu_groups
= g_list_reverse(menu_groups
);
5095 act
= purple_menu_action_new(_("Copy to"),
5098 menu
= g_list_prepend(menu
, act
);
5099 menu
= g_list_reverse(menu
);
5104 GList
*sipe_blist_node_menu(PurpleBlistNode
*node
) {
5105 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
5106 return sipe_buddy_menu((PurpleBuddy
*) node
);
5113 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
5115 gboolean ret
= TRUE
;
5116 char *username
= (char *)trans
->payload
;
5118 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
5119 PurpleBuddy
*pbuddy
;
5120 struct sipe_buddy
*sbuddy
;
5122 char *server_alias
= NULL
;
5124 const char *device_name
= NULL
;
5126 purple_debug_info("sipe", "Fetching %s's user info for %s\n", username
, sip
->username
);
5128 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, username
);
5129 alias
= purple_buddy_get_local_alias(pbuddy
);
5133 //will query buddy UA's capabilities and send answer to log
5134 sipe_options_request(sip
, username
);
5136 sbuddy
= g_hash_table_lookup(sip
->buddies
, username
);
5139 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
5143 if (msg
->response
!= 200) {
5144 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
5146 xmlnode
*searchResults
;
5149 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
5150 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5151 if (!searchResults
) {
5152 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
5153 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
5154 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
5155 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
5156 purple_notify_user_info_add_pair(info
, _("Job Title"), g_strdup(xmlnode_get_attrib(mrow
, "title")));
5157 purple_notify_user_info_add_pair(info
, _("Office"), g_strdup(xmlnode_get_attrib(mrow
, "office")));
5158 purple_notify_user_info_add_pair(info
, _("Business Phone"), g_strdup(xmlnode_get_attrib(mrow
, "phone")));
5159 purple_notify_user_info_add_pair(info
, _("Company"), g_strdup(xmlnode_get_attrib(mrow
, "company")));
5160 purple_notify_user_info_add_pair(info
, _("City"), g_strdup(xmlnode_get_attrib(mrow
, "city")));
5161 purple_notify_user_info_add_pair(info
, _("State"), g_strdup(xmlnode_get_attrib(mrow
, "state")));
5162 purple_notify_user_info_add_pair(info
, _("Country"), g_strdup(xmlnode_get_attrib(mrow
, "country")));
5163 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
5164 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
5165 if (!email
|| strcmp("", email
)) {
5166 if (!purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email")) {
5167 purple_blist_node_set_string((PurpleBlistNode
*)pbuddy
, "email", email
);
5171 xmlnode_free(searchResults
);
5174 purple_notify_user_info_add_section_break(info
);
5176 if (!server_alias
|| !strcmp("", server_alias
)) {
5177 g_free(server_alias
);
5178 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
5180 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
5184 // same as server alias, do not present
5185 alias
= (alias
&& server_alias
&& !strcmp(alias
, server_alias
)) ? NULL
: alias
;
5188 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
5191 if (!email
|| !strcmp("", email
)) {
5193 email
= g_strdup(purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email"));
5195 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
5201 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
5204 /* show a buddy's user info in a nice dialog box */
5205 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
5206 username
, /* buddy's username */
5208 NULL
, /* callback called when dialog closed */
5209 NULL
); /* userdata for callback */
5215 * AD search first, LDAP based
5217 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
5219 char *row
= g_strdup_printf(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
5220 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
5222 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
5223 send_soap_request_with_cb((struct sipe_account_data
*)gc
->proto_data
, body
,
5224 (TransCallback
) process_get_info_response
, (gpointer
)g_strdup(username
));
5229 static PurplePlugin
*my_protocol
= NULL
;
5231 static PurplePluginProtocolInfo prpl_info
=
5234 NULL
, /* user_splits */
5235 NULL
, /* protocol_options */
5236 NO_BUDDY_ICONS
, /* icon_spec */
5237 sipe_list_icon
, /* list_icon */
5238 NULL
, /* list_emblems */
5239 sipe_status_text
, /* status_text */
5240 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
5241 sipe_status_types
, /* away_states */
5242 sipe_blist_node_menu
, /* blist_node_menu */
5243 NULL
, /* chat_info */
5244 NULL
, /* chat_info_defaults */
5245 sipe_login
, /* login */
5246 sipe_close
, /* close */
5247 sipe_im_send
, /* send_im */
5248 NULL
, /* set_info */ // TODO maybe
5249 sipe_send_typing
, /* send_typing */
5250 sipe_get_info
, /* get_info */
5251 sipe_set_status
, /* set_status */
5252 NULL
, /* set_idle */
5253 NULL
, /* change_passwd */
5254 sipe_add_buddy
, /* add_buddy */
5255 NULL
, /* add_buddies */
5256 sipe_remove_buddy
, /* remove_buddy */
5257 NULL
, /* remove_buddies */
5258 sipe_add_permit
, /* add_permit */
5259 sipe_add_deny
, /* add_deny */
5260 sipe_add_deny
, /* rem_permit */
5261 sipe_add_permit
, /* rem_deny */
5262 dummy_permit_deny
, /* set_permit_deny */
5263 NULL
, /* join_chat */
5264 NULL
, /* reject_chat */
5265 NULL
, /* get_chat_name */
5266 NULL
, /* chat_invite */
5267 NULL
, /* chat_leave */
5268 NULL
, /* chat_whisper */
5269 NULL
, /* chat_send */
5270 sipe_keep_alive
, /* keepalive */
5271 NULL
, /* register_user */
5272 NULL
, /* get_cb_info */ // deprecated
5273 NULL
, /* get_cb_away */ // deprecated
5274 sipe_alias_buddy
, /* alias_buddy */
5275 sipe_group_buddy
, /* group_buddy */
5276 sipe_rename_group
, /* rename_group */
5277 NULL
, /* buddy_free */
5278 sipe_convo_closed
, /* convo_closed */
5279 purple_normalize_nocase
, /* normalize */
5280 NULL
, /* set_buddy_icon */
5281 sipe_remove_group
, /* remove_group */
5282 NULL
, /* get_cb_real_name */ // TODO?
5283 NULL
, /* set_chat_topic */
5284 NULL
, /* find_blist_chat */
5285 NULL
, /* roomlist_get_list */
5286 NULL
, /* roomlist_cancel */
5287 NULL
, /* roomlist_expand_category */
5288 NULL
, /* can_receive_file */
5289 NULL
, /* send_file */
5290 NULL
, /* new_xfer */
5291 NULL
, /* offline_message */
5292 NULL
, /* whiteboard_prpl_ops */
5293 sipe_send_raw
, /* send_raw */
5294 NULL
, /* roomlist_room_serialize */
5295 NULL
, /* unregister_user */
5296 NULL
, /* send_attention */
5297 NULL
, /* get_attention_types */
5299 sizeof(PurplePluginProtocolInfo
), /* struct_size */
5300 sipe_get_account_text_table
, /* get_account_text_table */
5304 static PurplePluginInfo info
= {
5305 PURPLE_PLUGIN_MAGIC
,
5306 PURPLE_MAJOR_VERSION
,
5307 PURPLE_MINOR_VERSION
,
5308 PURPLE_PLUGIN_PROTOCOL
, /**< type */
5309 NULL
, /**< ui_requirement */
5311 NULL
, /**< dependencies */
5312 PURPLE_PRIORITY_DEFAULT
, /**< priority */
5313 "prpl-sipe", /**< id */
5314 "Microsoft LCS/OCS", /**< name */
5315 VERSION
, /**< version */
5316 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
5317 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
5318 "Anibal Avelar <avelar@gmail.com>, " /**< author */
5319 "Gabriel Burt <gburt@novell.com>", /**< author */
5320 PURPLE_WEBSITE
, /**< homepage */
5321 sipe_plugin_load
, /**< load */
5322 sipe_plugin_unload
, /**< unload */
5323 sipe_plugin_destroy
, /**< destroy */
5324 NULL
, /**< ui_info */
5325 &prpl_info
, /**< extra_info */
5334 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
5338 entry
= prpl_info
.protocol_options
;
5340 purple_account_option_destroy(entry
->data
);
5341 entry
= g_list_delete_link(entry
, entry
);
5343 prpl_info
.protocol_options
= NULL
;
5345 entry
= prpl_info
.user_splits
;
5347 purple_account_user_split_destroy(entry
->data
);
5348 entry
= g_list_delete_link(entry
, entry
);
5350 prpl_info
.user_splits
= NULL
;
5353 static void init_plugin(PurplePlugin
*plugin
)
5355 PurpleAccountUserSplit
*split
;
5356 PurpleAccountOption
*option
;
5359 purple_debug_info(PACKAGE
, "bindtextdomain = %s", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
5360 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s",
5361 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
5364 purple_plugin_register(plugin
);
5366 split
= purple_account_user_split_new(_("Login \n domain\\user or\n someone@linux.com "), NULL
, ',');
5367 purple_account_user_split_set_reverse(split
, FALSE
);
5368 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
5370 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
5371 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5372 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
5373 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5375 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
5376 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5377 // Translators: noun (networking port)
5378 option
= purple_account_option_int_new(_("Port"), "port", 5061);
5379 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5381 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
5382 purple_account_option_add_list_item(option
, _("Auto"), "auto");
5383 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
5384 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
5385 purple_account_option_add_list_item(option
, _("UDP"), "udp");
5386 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5388 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
5389 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
5391 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
5392 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5394 // TODO commented out so won't show in the preferences until we fix krb message signing
5395 /*option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
5396 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5398 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
5399 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
5400 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5403 option
= purple_account_option_bool_new(_("Use Client-specified Keepalive"), "clientkeepalive", FALSE
);
5404 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5405 option
= purple_account_option_int_new(_("Keepalive Timeout"), "keepalive", 300);
5406 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5407 my_protocol
= plugin
;
5410 /* I had to redefined the function for it load, but works */
5411 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
5412 plugin
->info
= &(info
);
5413 init_plugin((plugin
));
5414 sipe_plugin_load((plugin
));
5415 return purple_plugin_register(plugin
);