6 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
7 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
8 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
9 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
12 * Thanks to Google's Summer of Code Program and the helpful mentors
15 * Session-based SIP MESSAGE documentation:
16 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 #include <sys/socket.h>
35 #include <sys/ioctl.h>
36 #include <sys/types.h>
37 #include <netinet/in.h>
41 # define _(String) ((const char *) gettext (String))
43 # define _(String) ((const char *) (String))
44 #endif /* ENABLE_NLS */
49 #define _LIBC_INTERNAL_
62 #include "accountopt.h"
64 #include "conversation.h"
81 #endif /*USE_KERBEROS*/
84 #include "sipe-sign.h"
88 /* Keep in sync with sipe_transport_type! */
89 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
90 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
92 /* Status identifiers (see also: sipe_status_types()) */
93 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
94 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
95 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
96 /* PURPLE_STATUS_UNAVAILABLE: */
97 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
98 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
99 #define SIPE_STATUS_ID_ONPHONE "on-the-phone" /* On The Phone */
100 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
101 /* PURPLE_STATUS_AWAY: */
102 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
103 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
104 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
105 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
106 /* ??? PURPLE_STATUS_MOBILE */
107 /* ??? PURPLE_STATUS_TUNE */
109 static char *gentag()
111 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
114 static gchar
*get_epid(struct sipe_account_data
*sip
)
117 sip
->epid
= sipe_uuid_get_macaddr(purple_network_get_my_ip(-1));
119 return g_strdup(sip
->epid
);
122 static char *genbranch()
124 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
125 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
126 rand() & 0xFFFF, rand() & 0xFFFF);
129 static char *gencallid()
131 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
132 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
133 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
134 rand() & 0xFFFF, rand() & 0xFFFF);
137 static gchar
*find_tag(const gchar
*hdr
)
139 gchar
* tag
= sipmsg_find_part_of_header (hdr
, "tag=", ";", NULL
);
141 // In case it's at the end and there's no trailing ;
142 tag
= sipmsg_find_part_of_header (hdr
, "tag=", NULL
, NULL
);
148 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
153 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
155 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
157 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
158 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
161 static void sipe_close(PurpleConnection
*gc
);
163 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, const char * buddy_name
);
164 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
, const char * buddy_name
);
165 static void send_presence_status(struct sipe_account_data
*sip
);
167 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
169 static void sipe_keep_alive(PurpleConnection
*gc
)
171 struct sipe_account_data
*sip
= gc
->proto_data
;
172 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
173 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
174 gchar buf
[2] = {0, 0};
175 purple_debug_info("sipe", "sending keep alive\n");
176 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
178 time_t now
= time(NULL
);
179 if ((sip
->keepalive_timeout
> 0) &&
180 ((now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
181 #if PURPLE_VERSION_CHECK(2,4,0)
182 && ((now
- gc
->last_received
) >= sip
->keepalive_timeout
)
185 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
186 sendout_pkt(gc
, "\r\n\r\n");
187 sip
->last_keepalive
= now
;
192 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
194 struct sip_connection
*ret
= NULL
;
195 GSList
*entry
= sip
->openconns
;
198 if (ret
->fd
== fd
) return ret
;
204 static void sipe_auth_free(struct sip_auth
*auth
)
208 g_free(auth
->opaque
);
212 g_free(auth
->target
);
214 g_free(auth
->digest_session_key
);
215 auth
->digest_session_key
= NULL
;
216 g_free(auth
->ntlm_key
);
217 auth
->ntlm_key
= NULL
;
218 auth
->type
= AUTH_TYPE_UNSET
;
223 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
225 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
227 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
231 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
233 struct sip_connection
*conn
= connection_find(sip
, fd
);
235 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
236 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
242 static void connection_free_all(struct sipe_account_data
*sip
)
244 struct sip_connection
*ret
= NULL
;
245 GSList
*entry
= sip
->openconns
;
248 connection_remove(sip
, ret
->fd
);
249 entry
= sip
->openconns
;
253 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
255 const gchar
*method
= msg
->method
;
256 const gchar
*target
= msg
->target
;
261 const char *authdomain
= sip
->authdomain
;
262 const char *authuser
= sip
->authuser
;
263 //const char *krb5_realm;
265 //gchar *krb5_token = NULL;
267 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
268 // and do error checking
270 // KRB realm should always be uppercase
271 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
273 if (sip
->realhostname
) {
274 host
= sip
->realhostname
;
275 } else if (purple_account_get_bool(sip
->account
, "useproxy", TRUE
)) {
276 host
= purple_account_get_string(sip
->account
, "proxy", "");
278 host
= sip
->sipdomain
;
281 /*gboolean new_auth = krb5_auth.gss_context == NULL;
283 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
286 if (new_auth || force_reauth) {
287 krb5_token = krb5_auth.base64_token;
290 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
291 krb5_token = krb5_auth.base64_token;*/
297 if (!authuser
|| strlen(authuser
) < 1) {
298 authuser
= sip
->username
;
301 if (auth
->type
== AUTH_TYPE_DIGEST
) { /* Digest */
302 sprintf(noncecount
, "%08d", auth
->nc
++);
303 response
= purple_cipher_http_digest_calculate_response(
304 "md5", method
, target
, NULL
, NULL
,
305 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
306 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
308 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
);
311 } else if (auth
->type
== AUTH_TYPE_NTLM
) { /* NTLM */
312 // If we have a signature for the message, include that
313 if (msg
->signature
) {
314 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
);
318 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
319 const gchar
*ntlm_key
;
321 #if GLIB_CHECK_VERSION(2,8,0)
322 const gchar
* hostname
= g_get_host_name();
324 static char hostname
[256];
325 int ret
= gethostname(hostname
, sizeof(hostname
));
326 hostname
[sizeof(hostname
) - 1] = '\0';
327 if (ret
== -1 || hostname
[0] == '\0') {
328 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Error when getting host name. Using \"localhost.\"\n");
330 strcpy(hostname
, "localhost");
333 /*const gchar * hostname = purple_get_host_name();*/
335 gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, hostname
, authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
336 auth
->ntlm_key
= (gchar
*)ntlm_key
;
337 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
342 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
344 } else if (auth
->type
== AUTH_TYPE_KERBEROS
) {
347 /*if (new_auth || force_reauth) {
348 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
350 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);
352 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
355 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
356 gchar * mic = "MICTODO";
357 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
358 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
359 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
360 //auth->opaque ? auth->opaque : "", auth->target);
361 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
366 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
369 sprintf(noncecount
, "%08d", auth
->nc
++);
370 response
= purple_cipher_http_digest_calculate_response(
371 "md5", method
, target
, NULL
, NULL
,
372 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
373 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
375 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
);
380 static char *parse_attribute(const char *attrname
, const char *source
)
382 const char *tmp
, *tmp2
;
384 int len
= strlen(attrname
);
386 if (!strncmp(source
, attrname
, len
)) {
388 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
390 retval
= g_strndup(tmp
, tmp2
- tmp
);
392 retval
= g_strdup(tmp
);
398 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
401 const char *authuser
;
404 //const char *krb5_realm;
407 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
408 // and do error checking
410 // KRB realm should always be uppercase
411 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
413 if (sip->realhostname) {
414 host = sip->realhostname;
415 } else if (purple_account_get_bool(sip->account, "useproxy", TRUE)) {
416 host = purple_account_get_string(sip->account, "proxy", "");
418 host = sip->sipdomain;
421 authuser
= sip
->authuser
;
423 if (!authuser
|| strlen(authuser
) < 1) {
424 authuser
= sip
->username
;
428 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
432 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
433 auth
->type
= AUTH_TYPE_NTLM
;
434 parts
= g_strsplit(hdr
+5, "\", ", 0);
437 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
438 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
440 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
443 if ((tmp
= parse_attribute("targetname=\"",
445 g_free(auth
->target
);
448 else if ((tmp
= parse_attribute("realm=\"",
453 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
454 g_free(auth
->opaque
);
461 if (!strstr(hdr
, "gssapi-data")) {
469 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
470 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
471 auth
->type
= AUTH_TYPE_KERBEROS
;
472 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
473 parts
= g_strsplit(hdr
+9, "\", ", 0);
476 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
477 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
478 /*if (krb5_auth.gss_context == NULL) {
479 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
481 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
484 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
485 g_free(auth
->target
);
487 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
490 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
491 g_free(auth
->opaque
);
501 auth
->type
= AUTH_TYPE_DIGEST
;
502 parts
= g_strsplit(hdr
, " ", 0);
504 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
508 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
516 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
518 g_free(auth
->digest_session_key
);
519 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
520 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
526 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
528 PurpleConnection
*gc
= data
;
529 struct sipe_account_data
*sip
= gc
->proto_data
;
533 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
535 if (max_write
== 0) {
536 if (sip
->tx_handler
!= 0){
537 purple_input_remove(sip
->tx_handler
);
543 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
545 if (written
< 0 && errno
== EAGAIN
)
547 else if (written
<= 0) {
548 /*TODO: do we really want to disconnect on a failure to write?*/
549 purple_connection_error(gc
, _("Could not write"));
553 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
556 static void sipe_canwrite_cb_ssl(gpointer data
, gint src
, PurpleInputCondition cond
)
558 PurpleConnection
*gc
= data
;
559 struct sipe_account_data
*sip
= gc
->proto_data
;
563 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
565 if (max_write
== 0) {
566 if (sip
->tx_handler
!= 0) {
567 purple_input_remove(sip
->tx_handler
);
573 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
575 if (written
< 0 && errno
== EAGAIN
)
577 else if (written
<= 0) {
578 /*TODO: do we really want to disconnect on a failure to write?*/
579 purple_connection_error(gc
, _("Could not write"));
583 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
586 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
588 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
590 PurpleConnection
*gc
= data
;
591 struct sipe_account_data
*sip
;
592 struct sip_connection
*conn
;
594 if (!PURPLE_CONNECTION_IS_VALID(gc
))
602 purple_connection_error(gc
, _("Could not connect"));
606 sip
= gc
->proto_data
;
608 sip
->connecting
= FALSE
;
609 sip
->last_keepalive
= time(NULL
);
611 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
613 /* If there is more to write now, we need to register a handler */
614 if (sip
->txbuf
->bufused
> 0)
615 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
617 conn
= connection_create(sip
, source
);
618 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
621 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
623 struct sipe_account_data
*sip
;
624 struct sip_connection
*conn
;
626 if (!PURPLE_CONNECTION_IS_VALID(gc
))
628 if (gsc
) purple_ssl_close(gsc
);
632 sip
= gc
->proto_data
;
635 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
636 sip
->connecting
= FALSE
;
637 sip
->last_keepalive
= time(NULL
);
639 conn
= connection_create(sip
, gsc
->fd
);
641 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
646 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
648 PurpleConnection
*gc
= data
;
649 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
650 if (sip
== NULL
) return;
652 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
654 /* If there is more to write now */
655 if (sip
->txbuf
->bufused
> 0) {
656 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
661 static void sendlater(PurpleConnection
*gc
, const char *buf
)
663 struct sipe_account_data
*sip
= gc
->proto_data
;
665 if (!sip
->connecting
) {
666 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
667 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
668 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
670 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
671 purple_connection_error(gc
, _("Couldn't create socket"));
674 sip
->connecting
= TRUE
;
677 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
678 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
680 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
683 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
685 struct sipe_account_data
*sip
= gc
->proto_data
;
686 time_t currtime
= time(NULL
);
687 int writelen
= strlen(buf
);
689 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
690 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
691 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
692 purple_debug_info("sipe", "could not send packet\n");
701 if (sip
->tx_handler
) {
706 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
708 ret
= write(sip
->fd
, buf
, writelen
);
712 if (ret
< 0 && errno
== EAGAIN
)
714 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
719 if (ret
< writelen
) {
720 if (!sip
->tx_handler
){
722 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
725 sip
->tx_handler
= purple_input_add(sip
->fd
,
726 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
731 /* XXX: is it OK to do this? You might get part of a request sent
732 with part of another. */
733 if (sip
->txbuf
->bufused
> 0)
734 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
736 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
742 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
744 sendout_pkt(gc
, buf
);
748 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
750 GSList
*tmp
= msg
->headers
;
753 GString
*outstr
= g_string_new("");
754 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
756 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
757 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
758 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
759 tmp
= g_slist_next(tmp
);
761 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
762 sendout_pkt(sip
->gc
, outstr
->str
);
763 g_string_free(outstr
, TRUE
);
766 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
769 if (sip
->registrar
.ntlm_key
) {
770 struct sipmsg_breakdown msgbd
;
771 gchar
*signature_input_str
;
773 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
774 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
775 sip
->registrar
.ntlm_num
++;
776 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
777 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
778 if (signature_input_str
!= NULL
) {
779 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
780 msg
->rand
= g_strdup(msgbd
.rand
);
781 msg
->num
= g_strdup(msgbd
.num
);
782 g_free(signature_input_str
);
784 sipmsg_breakdown_free(&msgbd
);
787 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
788 buf
= auth_header(sip
, &sip
->registrar
, msg
);
789 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
790 sipmsg_add_header(msg
, "Authorization", buf
);
792 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
795 } 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")) {
796 sip
->registrar
.nc
= 3;
797 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
799 buf
= auth_header(sip
, &sip
->registrar
, msg
);
800 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
803 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
807 static char *get_contact(struct sipe_account_data
*sip
)
809 return g_strdup(sip
->contact
);
814 static char *get_contact_service(struct sipe_account_data *sip)
816 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()));
817 //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);
821 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
822 const char *text
, const char *body
)
826 GString
*outstr
= g_string_new("");
827 struct sipe_account_data
*sip
= gc
->proto_data
;
831 sipmsg_remove_header(msg
, "ms-user-data");
833 contact
= get_contact(sip
);
834 sipmsg_remove_header(msg
, "Contact");
835 sipmsg_add_header(msg
, "Contact", contact
);
838 /* When sending the acknowlegements and errors, the content length from the original
839 message is still here, but there is no body; we need to make sure we're sending the
840 correct content length */
841 sipmsg_remove_header(msg
, "Content-Length");
844 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
845 sipmsg_add_header(msg
, "Content-Length", len
);
847 sipmsg_remove_header(msg
, "Content-Type");
848 sipmsg_add_header(msg
, "Content-Length", "0");
851 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
852 //gchar * mic = "MICTODO";
853 msg
->response
= code
;
855 sipmsg_remove_header(msg
, "Authentication-Info");
856 sign_outgoing_message(msg
, sip
, msg
->method
);
858 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
861 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
862 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
864 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
865 tmp
= g_slist_next(tmp
);
867 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
868 sendout_pkt(gc
, outstr
->str
);
869 g_string_free(outstr
, TRUE
);
872 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
874 if (trans
->msg
) sipmsg_free(trans
->msg
);
875 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
879 static struct transaction
*
880 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
882 struct transaction
*trans
= g_new0(struct transaction
, 1);
883 trans
->time
= time(NULL
);
884 trans
->msg
= (struct sipmsg
*)msg
;
885 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
886 trans
->callback
= callback
;
887 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
891 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
893 struct transaction
*trans
;
894 GSList
*transactions
= sip
->transactions
;
895 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
897 while (transactions
) {
898 trans
= transactions
->data
;
899 if (!strcmp(trans
->cseq
, cseq
)) {
902 transactions
= transactions
->next
;
908 static struct transaction
*
909 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
910 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
911 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
913 struct sipe_account_data
*sip
= gc
->proto_data
;
914 const char *addh
= "";
917 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
918 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
919 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
920 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
921 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
922 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
923 gchar
*route
= strdup("");
924 gchar
*epid
= get_epid(sip
); // TODO generate one per account/login
925 struct transaction
*trans
;
927 if (dialog
&& dialog
->routes
)
929 GSList
*iter
= dialog
->routes
;
934 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
936 iter
= g_slist_next(iter
);
940 if (!ourtag
&& !dialog
) {
944 if (!strcmp(method
, "REGISTER")) {
945 if (sip
->regcallid
) {
947 callid
= g_strdup(sip
->regcallid
);
949 sip
->regcallid
= g_strdup(callid
);
953 if (addheaders
) addh
= addheaders
;
955 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
956 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
957 "From: <sip:%s>%s%s;epid=%s\r\n"
958 "To: <%s>%s%s%s%s\r\n"
959 "Max-Forwards: 70\r\n"
964 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
966 dialog
&& dialog
->request
? dialog
->request
: url
,
967 TRANSPORT_DESCRIPTOR
,
968 purple_network_get_my_ip(-1),
970 branch
? ";branch=" : "",
971 branch
? branch
: "",
973 ourtag
? ";tag=" : "",
974 ourtag
? ourtag
: "",
977 theirtag
? ";tag=" : "",
978 theirtag
? theirtag
: "",
979 theirepid
? ";epid=" : "",
980 theirepid
? theirepid
: "",
981 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
987 body
? strlen(body
) : 0,
991 //printf ("parsing msg buf:\n%s\n\n", buf);
992 msg
= sipmsg_parse_msg(buf
);
1003 sign_outgoing_message (msg
, sip
, method
);
1005 buf
= sipmsg_to_string (msg
);
1007 /* add to ongoing transactions */
1008 trans
= transactions_add_buf(sip
, msg
, tc
);
1009 sendout_pkt(gc
, buf
);
1015 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
1017 gchar
*from
= g_strdup_printf("sip:%s", sip
->username
);
1018 gchar
*contact
= get_contact(sip
);
1019 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
1020 "Content-Type: application/SOAP+xml\r\n",contact
);
1022 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1023 tr
->payload
= payload
;
1030 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1032 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
1035 static char *get_contact_register(struct sipe_account_data
*sip
)
1037 char *epid
= get_epid(sip
);
1038 char *uuid
= generateUUIDfromEPID(epid
);
1039 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
);
1045 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1047 char *expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1048 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
1049 char *to
= g_strdup_printf("sip:%s", sip
->username
);
1050 char *contact
= get_contact_register(sip
);
1051 char *hdr
= g_strdup_printf("Contact: %s\r\n"
1052 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1053 "Event: registration\r\n"
1054 "Allow-Events: presence\r\n"
1055 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1056 "%s", contact
, expires
);
1060 sip
->registerstatus
= 1;
1062 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1063 process_register_response
);
1070 static void do_register_cb(struct sipe_account_data
*sip
)
1072 do_register_exp(sip
, -1);
1073 sip
->reregister_set
= FALSE
;
1076 static void do_register(struct sipe_account_data
*sip
)
1078 do_register_exp(sip
, -1);
1082 * Returns URI from provided To or From header.
1084 * Needs to g_free() after use.
1086 * @return URI with sip: prefix
1088 static gchar
*parse_from(const gchar
*hdr
)
1091 const gchar
*tmp
, *tmp2
= hdr
;
1093 if (!hdr
) return NULL
;
1094 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1095 tmp
= strchr(hdr
, '<');
1097 /* i hate the different SIP UA behaviours... */
1098 if (tmp
) { /* sip address in <...> */
1100 tmp
= strchr(tmp2
, '>');
1102 from
= g_strndup(tmp2
, tmp
- tmp2
);
1104 purple_debug_info("sipe", "found < without > in From\n");
1108 tmp
= strchr(tmp2
, ';');
1110 from
= g_strndup(tmp2
, tmp
- tmp2
);
1112 from
= g_strdup(tmp2
);
1115 purple_debug_info("sipe", "got %s\n", from
);
1119 static xmlnode
* xmlnode_get_descendant(xmlnode
* parent
, ...)
1122 xmlnode
* node
= NULL
;
1125 va_start(args
, parent
);
1126 while ((name
= va_arg(args
, const char *)) != NULL
) {
1127 node
= xmlnode_get_child(parent
, name
);
1128 if (node
== NULL
) return NULL
;
1138 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1140 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1141 send_soap_request(sip
, body
);
1146 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1149 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1151 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1154 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1158 void sipe_auth_user_cb(void * data
)
1160 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1163 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1168 void sipe_deny_user_cb(void * data
)
1170 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1173 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1178 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1180 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1181 sipe_contact_allow_deny(sip
, name
, TRUE
);
1185 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1187 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1188 sipe_contact_allow_deny(sip
, name
, FALSE
);
1192 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1194 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1195 sipe_contact_set_acl(sip, name, "");
1199 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1203 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1204 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1206 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1208 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1209 if (!watchers
) return;
1211 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1212 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1213 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1214 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1216 // TODO pull out optional displayName to pass as alias
1218 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1219 job
->who
= remote_user
;
1221 purple_account_request_authorization(
1235 xmlnode_free(watchers
);
1240 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1242 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1243 if (!purple_group
) {
1244 purple_group
= purple_group_new(group
->name
);
1245 purple_blist_add_group(purple_group
, NULL
);
1249 group
->purple_group
= purple_group
;
1250 sip
->groups
= g_slist_append(sip
->groups
, group
);
1251 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1253 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1257 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1259 struct sipe_group
*group
;
1265 entry
= sip
->groups
;
1267 group
= entry
->data
;
1268 if (group
->id
== id
) {
1271 entry
= entry
->next
;
1276 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, gchar
* name
)
1278 struct sipe_group
*group
;
1284 entry
= sip
->groups
;
1286 group
= entry
->data
;
1287 if (!strcmp(group
->name
, name
)) {
1290 entry
= entry
->next
;
1296 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1299 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1300 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1301 send_soap_request(sip
, body
);
1303 g_free(group
->name
);
1304 group
->name
= g_strdup(name
);
1308 * Only appends if no such value already stored.
1311 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1312 GSList
* res
= list
;
1313 if (!g_slist_find_custom(list
, data
, func
)) {
1314 res
= g_slist_insert_sorted(list
, data
, func
);
1320 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1321 return group1
->id
- group2
->id
;
1325 * Returns string like "2 4 7 8" - group ids buddy belong to.
1328 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1331 //creating array from GList, converting int to gchar*
1332 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1333 GSList
*entry
= buddy
->groups
;
1335 struct sipe_group
* group
= entry
->data
;
1336 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1337 entry
= entry
->next
;
1341 res
= g_strjoinv(" ", ids_arr
);
1342 g_strfreev(ids_arr
);
1347 * Sends buddy update to server
1350 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1352 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1353 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1355 if (buddy
&& purple_buddy
) {
1356 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1358 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1359 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1361 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1362 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1364 send_soap_request(sip
, body
);
1370 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1372 if (msg
->response
== 200) {
1373 struct sipe_group
*group
;
1374 struct group_user_context
*ctx
= (struct group_user_context
*)tc
->payload
;
1378 struct sipe_buddy
*buddy
;
1380 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1386 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1393 group_id
= xmlnode_get_data(node
);
1400 group
= g_new0(struct sipe_group
, 1);
1401 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1403 group
->name
= ctx
->group_name
;
1405 sipe_group_add(sip
, group
);
1407 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1409 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1412 sipe_group_set_user(sip
, ctx
->user_name
);
1421 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1423 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1425 ctx
->group_name
= g_strdup(name
);
1426 ctx
->user_name
= g_strdup(who
);
1428 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1429 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1435 * Should return FALSE if repetitive action is not needed
1437 gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1440 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1441 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1442 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1443 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1444 ret
= sched_action
->repetitive
;
1445 g_free(sched_action
->payload
);
1446 g_free(sched_action
->name
);
1447 g_free(sched_action
);
1452 * Do schedule action for execution in the future.
1453 * Non repetitive execution.
1455 * @param name of action (will be copied)
1456 * @param timeout in seconds
1457 * @action callback function
1458 * @payload callback data (can be NULL, otherwise caller must allocate memory)
1460 void sipe_schedule_action(gchar
*name
, int timeout
, Action action
, struct sipe_account_data
*sip
, void * payload
)
1462 struct scheduled_action
*sched_action
;
1464 purple_debug_info("sipe","scheduling action %s timeout:%d\n", name
, timeout
);
1465 sched_action
= g_new0(struct scheduled_action
, 1);
1466 sched_action
->repetitive
= FALSE
;
1467 sched_action
->name
= g_strdup(name
);
1468 sched_action
->action
= action
;
1469 sched_action
->sip
= sip
;
1470 sched_action
->payload
= payload
;
1471 sched_action
->timeout_handler
= purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1472 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1473 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1477 * Kills action timer effectively cancelling
1480 * @param name of action
1482 void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, gchar
*name
)
1486 if (!sip
->timeouts
|| !name
) return;
1488 entry
= sip
->timeouts
;
1490 struct scheduled_action
*sched_action
= entry
->data
;
1491 if(!strcmp(sched_action
->name
, name
)) {
1492 GSList
*to_delete
= entry
;
1493 entry
= entry
->next
;
1494 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1495 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1496 purple_timeout_remove(sched_action
->timeout_handler
);
1497 g_free(sched_action
->payload
);
1498 g_free(sched_action
->name
);
1499 g_free(sched_action
);
1501 entry
= entry
->next
;
1506 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1508 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1510 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1512 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1517 static void sipe_subscribe_resource_uri(const char *name
, gpointer value
, gchar
**resources_uri
)
1519 gchar
*tmp
= *resources_uri
;
1520 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1524 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1526 gchar
*tmp
= *resources_uri
;
1527 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1529 if(!sbuddy
->resubscribed
){ //Only not resubscribed contacts; the first time everybody are included
1530 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"><context/></resource>\n", tmp
, name
);
1531 sbuddy
->resubscribed
= FALSE
;
1538 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1539 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1540 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1541 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1542 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1545 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
, const char * buddy_name
){
1546 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1547 gchar
*contact
= get_contact(sip
);
1550 gchar
*resources_uri
= g_strdup("");
1551 gchar
*require
= "";
1553 gchar
*autoextend
= "";
1554 gchar
*content_type
;
1557 if (sip
->msrtc_event_categories
) {
1560 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1564 sipe_subscribe_resource_uri_with_context(buddy_name
,NULL
, &resources_uri
);
1566 require
= ", categoryList";
1567 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1568 content_type
= "application/msrtc-adrl-categorylist+xml";
1569 content
= g_strdup_printf(
1570 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1571 "<action name=\"subscribe\" id=\"63792024\">\n"
1572 "<adhocList>\n%s</adhocList>\n"
1573 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1574 "<category name=\"note\"/>\n"
1575 "<category name=\"state\"/>\n"
1578 "</batchSub>", sip
->username
, resources_uri
);
1580 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1581 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1582 content_type
= "application/adrl+xml";
1583 content
= g_strdup_printf(
1584 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1585 "<create xmlns=\"\">\n%s</create>\n"
1586 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1588 g_free(resources_uri
);
1590 request
= g_strdup_printf(
1591 "Require: adhoclist%s\r\n"
1592 "Supported: eventlist\r\n"
1593 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1594 "Supported: ms-piggyback-first-notify\r\n"
1595 "%sSupported: ms-benotify\r\n"
1596 "Proxy-Require: ms-benotify\r\n"
1597 "Event: presence\r\n"
1598 "Content-Type: %s\r\n"
1599 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1602 /* subscribe to buddy presence */
1603 //send_sip_request(sip->gc, "SUBSCRIBE", resource_uri, resource_uri, request, content, NULL, process_subscribe_response);
1604 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1612 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1613 * The user sends a single SUBSCRIBE request to the subscribed contact.
1614 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1618 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, const char * buddy_name
)
1620 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1621 gchar
*tmp
= get_contact(sip
);
1624 gchar
*autoextend
= "";
1626 if (!sip
->msrtc_event_categories
)
1627 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1629 request
= g_strdup_printf(
1630 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1631 "Supported: ms-piggyback-first-notify\r\n"
1632 "%sSupported: ms-benotify\r\n"
1633 "Proxy-Require: ms-benotify\r\n"
1634 "Event: presence\r\n"
1635 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1636 "Contact: %s\r\n", autoextend
,tmp
);
1638 content
= g_strdup_printf(
1639 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1640 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1641 "<resource uri=\"%s\"/>\n"
1643 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1644 "<category name=\"note\"/>\n"
1645 "<category name=\"state\"/>\n"
1648 "</batchSub>", sip
->username
, to
1653 /* subscribe to buddy presence */
1654 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1661 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1663 if (!purple_status_is_active(status
))
1667 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
1670 g_free(sip
->status
);
1671 sip
->status
= g_strdup(purple_status_get_id(status
));
1672 send_presence_status(sip
);
1678 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
)
1680 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1681 sipe_group_set_user(sip
, name
);
1685 sipe_group_buddy(PurpleConnection
*gc
,
1687 const char *old_group_name
,
1688 const char *new_group_name
)
1690 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1691 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1692 struct sipe_group
* old_group
= NULL
;
1693 struct sipe_group
* new_group
;
1695 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1696 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1698 if(!buddy
) { // buddy not in roaming list
1702 if (old_group_name
) {
1703 old_group
= sipe_group_find_by_name(sip
, g_strdup(old_group_name
));
1705 new_group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1708 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1709 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1713 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1715 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1716 sipe_group_set_user(sip
, who
);
1720 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1722 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1723 struct sipe_buddy
*b
;
1725 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1727 // Prepend sip: if needed
1728 if (strncmp("sip:", buddy
->name
, 4)) {
1729 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
1730 purple_blist_rename_buddy(buddy
, buf
);
1734 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1735 b
= g_new0(struct sipe_buddy
, 1);
1736 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1737 b
->name
= g_strdup(buddy
->name
);
1738 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1739 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1740 if(sip
->batched_support
){
1741 sipe_subscribe_presence_batched(sip
, b
->name
);
1743 sipe_subscribe_presence_single(sip
, b
->name
); //@TODO should go to callback
1746 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1750 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1752 g_free(buddy
->name
);
1753 g_free(buddy
->annotation
);
1754 g_free(buddy
->device_name
);
1755 g_slist_free(buddy
->groups
);
1760 * Unassociates buddy from group first.
1761 * Then see if no groups left, removes buddy completely.
1762 * Otherwise updates buddy groups on server.
1764 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1766 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1767 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1768 struct sipe_group
*g
= NULL
;
1770 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1775 g
= sipe_group_find_by_name(sip
, group
->name
);
1779 b
->groups
= g_slist_remove(b
->groups
, g
);
1780 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
1783 if (g_slist_length(b
->groups
) < 1) {
1784 gchar
*action_name
= g_strdup_printf("<%s><%s>", "presence", buddy
->name
);
1785 sipe_cancel_scheduled_action(sip
, action_name
);
1786 g_free(action_name
);
1788 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1791 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1792 send_soap_request(sip
, body
);
1798 //updates groups on server
1799 sipe_group_set_user(sip
, b
->name
);
1805 sipe_rename_group(PurpleConnection
*gc
,
1806 const char *old_name
,
1808 GList
*moved_buddies
)
1810 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1811 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1813 sipe_group_rename(sip
, s_group
, group
->name
);
1815 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1820 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1822 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1823 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1826 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1827 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1828 send_soap_request(sip
, body
);
1831 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1832 g_free(s_group
->name
);
1835 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1839 static GList
*sipe_status_types(PurpleAccount
*acc
)
1841 PurpleStatusType
*type
;
1842 GList
*types
= NULL
;
1845 type
= purple_status_type_new_with_attrs(
1846 PURPLE_STATUS_AVAILABLE
, NULL
, _("Online"), TRUE
, TRUE
, FALSE
,
1847 // Translators: noun
1848 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1850 types
= g_list_append(types
, type
);
1853 type
= purple_status_type_new_with_attrs(
1854 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_BUSY
, _("Busy"), TRUE
, TRUE
, FALSE
,
1855 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1857 types
= g_list_append(types
, type
);
1859 // Do Not Disturb (not user settable)
1860 type
= purple_status_type_new_with_attrs(
1861 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_DND
, _("Do Not Disturb"), TRUE
, FALSE
, FALSE
,
1862 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1864 types
= g_list_append(types
, type
);
1867 type
= purple_status_type_new_with_attrs(
1868 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_BRB
, _("Be Right Back"), TRUE
, TRUE
, FALSE
,
1869 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1871 types
= g_list_append(types
, type
);
1874 type
= purple_status_type_new_with_attrs(
1875 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1876 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1878 types
= g_list_append(types
, type
);
1881 type
= purple_status_type_new_with_attrs(
1882 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_ONPHONE
, _("On The Phone"), TRUE
, TRUE
, FALSE
,
1883 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1885 types
= g_list_append(types
, type
);
1888 type
= purple_status_type_new_with_attrs(
1889 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_LUNCH
, _("Out To Lunch"), TRUE
, TRUE
, FALSE
,
1890 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1892 types
= g_list_append(types
, type
);
1895 type
= purple_status_type_new_full(
1896 PURPLE_STATUS_INVISIBLE
, NULL
, _("Appear Offline"), TRUE
, TRUE
, FALSE
);
1897 types
= g_list_append(types
, type
);
1900 type
= purple_status_type_new_full(
1901 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1902 types
= g_list_append(types
, type
);
1908 * A callback for g_hash_table_foreach
1910 static void sipe_buddy_subscribe_cb(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1912 sipe_subscribe_presence_single(sip
, buddy
->name
);
1916 * Removes entries from purple buddy list
1917 * that does not correspond ones in the roaming contact list.
1919 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
1920 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1921 GSList
*entry
= buddies
;
1922 struct sipe_buddy
*buddy
;
1926 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
1927 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
1930 g
= purple_buddy_get_group(b
);
1931 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
1933 gboolean in_sipe_groups
= FALSE
;
1934 GSList
*entry2
= buddy
->groups
;
1936 struct sipe_group
*group
= entry2
->data
;
1937 if (!strcmp(group
->name
, g
->name
)) {
1938 in_sipe_groups
= TRUE
;
1941 entry2
= entry2
->next
;
1943 if(!in_sipe_groups
) {
1944 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
1945 purple_blist_remove_buddy(b
);
1948 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
1949 purple_blist_remove_buddy(b
);
1951 entry
= entry
->next
;
1955 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1957 int len
= msg
->bodylen
;
1959 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1962 const gchar
*contacts_delta
;
1963 xmlnode
*group_node
;
1964 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1968 /* Convert the contact from XML to Purple Buddies */
1969 isc
= xmlnode_from_str(msg
->body
, len
);
1974 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
1975 if (contacts_delta
) {
1976 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1980 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1981 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1982 const char *name
= xmlnode_get_attrib(group_node
, "name");
1984 if (!strncmp(name
, "~", 1)) {
1986 name
= "Other Contacts";
1988 group
->name
= g_strdup(name
);
1989 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1991 sipe_group_add(sip
, group
);
1994 // Make sure we have at least one group
1995 if (g_slist_length(sip
->groups
) == 0) {
1996 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1997 PurpleGroup
*purple_group
;
1999 group
->name
= g_strdup("Other Contacts");
2001 purple_group
= purple_group_new(group
->name
);
2002 purple_blist_add_group(purple_group
, NULL
);
2003 sip
->groups
= g_slist_append(sip
->groups
, group
);
2006 /* Parse contacts */
2007 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
2008 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
2009 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
2010 gchar
* groups
= g_strdup(xmlnode_get_attrib(item
, "groups"));
2011 gchar
* buddy_name
= g_strdup_printf("sip:%s", uri
);
2012 gchar
**item_groups
;
2013 struct sipe_group
*group
= NULL
;
2014 struct sipe_buddy
*buddy
= NULL
;
2017 // assign to group Other Contacts if nothing else received
2018 if(!groups
|| !strcmp("", groups
) ) {
2019 group
= sipe_group_find_by_name(sip
, "Other Contacts");
2020 groups
= group
? g_strdup_printf("%d", group
->id
) : "1";
2023 item_groups
= g_strsplit(groups
, " ", 0);
2025 while (item_groups
[i
]) {
2026 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2028 // If couldn't find the right group for this contact, just put them in the first group we have
2029 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2030 group
= sip
->groups
->data
;
2033 if (group
!= NULL
) {
2034 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2036 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2037 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2040 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
2041 if (name
!= NULL
&& strlen(name
) != 0) {
2042 purple_blist_alias_buddy(b
, name
);
2047 buddy
= g_new0(struct sipe_buddy
, 1);
2048 buddy
->name
= g_strdup(b
->name
);
2049 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2052 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2054 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2056 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2061 } // while, contact groups
2062 g_strfreev(item_groups
);
2072 sipe_cleanup_local_blist(sip
);
2074 //subscribe to buddies
2075 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2076 if(sip
->batched_support
){
2077 sipe_subscribe_presence_batched(sip
, NULL
);
2080 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2082 sip
->subscribed_buddies
= TRUE
;
2089 * Subscribe roaming contacts
2091 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2093 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2094 gchar
*tmp
= get_contact(sip
);
2095 gchar
*hdr
= g_strdup_printf(
2096 "Event: vnd-microsoft-roaming-contacts\r\n"
2097 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2098 "Supported: com.microsoft.autoextend\r\n"
2099 "Supported: ms-benotify\r\n"
2100 "Proxy-Require: ms-benotify\r\n"
2101 "Supported: ms-piggyback-first-notify\r\n"
2102 "Contact: %s\r\n", tmp
);
2105 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2110 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2112 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2113 gchar
*tmp
= get_contact(sip
);
2114 gchar
*hdr
= g_strdup_printf(
2115 "Event: presence.wpending\r\n"
2116 "Accept: text/xml+msrtc.wpending\r\n"
2117 "Supported: com.microsoft.autoextend\r\n"
2118 "Supported: ms-benotify\r\n"
2119 "Proxy-Require: ms-benotify\r\n"
2120 "Supported: ms-piggyback-first-notify\r\n"
2121 "Contact: %s\r\n", tmp
);
2124 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2130 * Fires on deregistration event initiated by server.
2131 * [MS-SIPREGE] SIP extension.
2136 // Content-Type: text/registration-event
2137 // subscription-state: terminated;expires=0
2138 // ms-diagnostics-public: 4141;reason="User disabled"
2140 // deregistered;event=rejected
2142 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2144 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2145 gchar
*event
= NULL
;
2146 gchar
*reason
= NULL
;
2147 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2149 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2150 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2152 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2153 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2154 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2155 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2157 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2161 if (warning
!= NULL
) {
2162 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2163 } else { // for LCS2005
2165 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2166 error_id
= 4140; // [MS-SIPREGE]
2167 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2168 reason
= g_strdup(_("You have been signed off because you've signed in at another location"));
2169 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2171 reason
= g_strdup(_("User disabled")); // [MS-OCER]
2172 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2174 reason
= g_strdup(_("User moved")); // [MS-OCER]
2178 warning
= g_strdup_printf(_("Unregistered by Server: %s."), reason
? reason
: _("no reason given"));
2181 sip
->gc
->wants_to_die
= TRUE
;
2182 purple_connection_error(sip
->gc
, warning
);
2187 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2189 const gchar
*contacts_delta
;
2192 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2198 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2201 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2210 * When we receive some self (BE) NOTIFY with a new subscriber
2211 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2215 static void sipe_process_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2221 char *display_name
= NULL
;
2222 PurpleBuddy
*pbuddy
;
2227 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2229 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2232 contact
= get_contact(sip
);
2233 to
= g_strdup_printf("sip:%s", sip
->username
);
2235 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2237 const char *acknowledged
;
2241 user
= xmlnode_get_attrib(node
, "user");
2242 if (!user
) continue;
2243 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
2244 uri_user
= g_strdup_printf("sip:%s", user
);
2245 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri_user
);
2247 alias
= purple_buddy_get_local_alias(pbuddy
);
2248 uri_alias
= g_strdup_printf("sip:%s", alias
);
2249 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
2250 if (display_name
&& !g_ascii_strcasecmp(uri_user
, uri_alias
)) { // 'bad' alias
2251 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri_user
, display_name
);
2252 purple_blist_alias_buddy(pbuddy
, display_name
);
2258 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
2259 if(!g_ascii_strcasecmp(acknowledged
,"false")){
2260 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
2261 hdr
= g_strdup_printf(
2263 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2265 body
= g_strdup_printf(
2266 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2267 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2268 "</setSubscribers>", user
);
2270 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
2281 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2283 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2284 gchar
*tmp
= get_contact(sip
);
2285 gchar
*hdr
= g_strdup_printf(
2286 "Event: vnd-microsoft-roaming-ACL\r\n"
2287 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
2288 "Supported: com.microsoft.autoextend\r\n"
2289 "Supported: ms-benotify\r\n"
2290 "Proxy-Require: ms-benotify\r\n"
2291 "Supported: ms-piggyback-first-notify\r\n"
2292 "Contact: %s\r\n", tmp
);
2295 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2301 * To request for presence information about the user, access level settings that have already been configured by the user
2302 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
2303 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
2306 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2308 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2309 gchar
*tmp
= get_contact(sip
);
2310 gchar
*hdr
= g_strdup_printf(
2311 "Event: vnd-microsoft-roaming-self\r\n"
2312 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
2313 "Supported: ms-benotify\r\n"
2314 "Proxy-Require: ms-benotify\r\n"
2315 "Supported: ms-piggyback-first-notify\r\n"
2317 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
2319 gchar
*body
=g_strdup(
2320 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
2321 "<roaming type=\"categories\"/>"
2322 "<roaming type=\"containers\"/>"
2323 "<roaming type=\"subscribers\"/></roamingList>");
2326 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2335 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2337 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2338 gchar
*tmp
= get_contact(sip
);
2339 gchar
*hdr
= g_strdup_printf(
2340 "Event: vnd-microsoft-provisioning\r\n"
2341 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
2342 "Supported: com.microsoft.autoextend\r\n"
2343 "Supported: ms-benotify\r\n"
2344 "Proxy-Require: ms-benotify\r\n"
2345 "Supported: ms-piggyback-first-notify\r\n"
2347 "Contact: %s\r\n", tmp
);
2350 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
2355 /** Subscription for provisioning information to help with initial
2356 * configuration. This subscription is a one-time query (denoted by the Expires header,
2357 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
2358 * configuration, meeting policies, and policy settings that Communicator must enforce.
2359 * TODO: for what we need this information.
2362 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2364 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2365 gchar
*tmp
= get_contact(sip
);
2366 gchar
*hdr
= g_strdup_printf(
2367 "Event: vnd-microsoft-provisioning-v2\r\n"
2368 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
2369 "Supported: com.microsoft.autoextend\r\n"
2370 "Supported: ms-benotify\r\n"
2371 "Proxy-Require: ms-benotify\r\n"
2372 "Supported: ms-piggyback-first-notify\r\n"
2375 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
2376 gchar
*body
= g_strdup(
2377 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
2378 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
2379 "<provisioningGroup name=\"ucPolicy\"/>"
2380 "</provisioningGroupList>");
2383 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2389 /* IM Session (INVITE and MESSAGE methods) */
2391 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
2393 struct sip_im_session
*session
;
2395 if (sip
== NULL
|| who
== NULL
) {
2399 entry
= sip
->im_sessions
;
2401 session
= entry
->data
;
2402 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
2405 entry
= entry
->next
;
2410 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
2412 struct sip_im_session
*session
= find_im_session(sip
, who
);
2414 session
= g_new0(struct sip_im_session
, 1);
2415 session
->with
= g_strdup(who
);
2416 session
->unconfirmed_messages
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
2417 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
2422 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2424 struct sip_dialog
*dialog
= session
->dialog
;
2427 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
2430 entry
= dialog
->routes
;
2432 g_free(entry
->data
);
2433 entry
= g_slist_remove(entry
, entry
->data
);
2435 entry
= dialog
->supported
;
2437 g_free(entry
->data
);
2438 entry
= g_slist_remove(entry
, entry
->data
);
2440 g_free(dialog
->callid
);
2441 g_free(dialog
->ourtag
);
2442 g_free(dialog
->theirtag
);
2443 g_free(dialog
->theirepid
);
2444 g_free(dialog
->request
);
2446 g_free(session
->dialog
);
2448 entry
= session
->outgoing_message_queue
;
2450 g_free(entry
->data
);
2451 entry
= g_slist_remove(entry
, entry
->data
);
2454 g_hash_table_destroy(session
->unconfirmed_messages
);
2456 g_free(session
->with
);
2461 process_options_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2463 gboolean ret
= TRUE
;
2465 if (msg
->response
!= 200) {
2466 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
2470 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
2476 * Asks UA/proxy about its capabilities.
2478 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
2480 gchar
*to
= strstr(who
, "sip:") ? g_strdup(who
) : g_strdup_printf("sip:%s", who
);
2481 gchar
*contact
= get_contact(sip
);
2483 request
= g_strdup_printf(
2484 "Accept: application/sdp\r\n"
2485 "Contact: %s\r\n", contact
);
2489 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
2495 static void sipe_present_message_undelivered_err(gchar
*with
, struct sipe_account_data
*sip
, gchar
*message
)
2497 char *msg
, *msg_tmp
;
2498 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
2499 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2501 msg_tmp
= g_strdup_printf( _("The following message could not be delivered to all recipients, "\
2502 "possibly because one or more persons are offline:\n%s") ,
2504 purple_conv_present_error(with
, sip
->account
, msg_tmp
);
2509 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
);
2512 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2514 gboolean ret
= TRUE
;
2515 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
2516 struct sip_im_session
* session
= find_im_session(sip
, with
);
2517 struct sip_dialog
*dialog
;
2523 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2528 dialog
= session
->dialog
;
2530 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2535 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2536 key
= g_strdup_printf("<%s><%d><MESSAGE>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
));
2538 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2540 if (msg
->response
!= 200) {
2541 purple_debug_info("sipe", "process_message_response: MESSAGE response not 200\n");
2543 sipe_present_message_undelivered_err(with
, sip
, message
);
2544 im_session_destroy(sip
, session
);
2547 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2548 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
2549 key
, g_hash_table_size(session
->unconfirmed_messages
));
2555 sipe_im_process_queue(sip
, session
);
2559 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
2569 if (strncmp("sip:", session
->with
, 4)) {
2570 fullto
= g_strdup_printf("sip:%s", session
->with
);
2572 fullto
= g_strdup(session
->with
);
2575 sipe_parse_html(msg
, &msgformat
, &msgtext
);
2576 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
2578 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2581 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2584 msgr
= g_strdup("");
2587 tmp
= get_contact(sip
);
2588 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
2589 //hdr = g_strdup("Content-Type: text/rtf\r\n");
2590 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
2591 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n",
2596 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msgtext
, session
->dialog
, process_message_response
);
2604 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
2606 GSList
*entry
= session
->outgoing_message_queue
;
2608 if (session
->outgoing_invite
) return; //do not send messages until INVITE responded.
2611 char *key
= g_strdup_printf("<%s><%d><MESSAGE>", session
->dialog
->callid
, (session
->dialog
->cseq
) + 1);
2612 char *queued_msg
= entry
->data
;
2613 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
2614 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
2615 key
, g_hash_table_size(session
->unconfirmed_messages
));
2617 sipe_send_message(sip
, session
, queued_msg
);
2618 entry
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2624 sipe_get_route_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2626 GSList
*hdr
= msg
->headers
;
2627 struct siphdrelement
*elem
;
2633 if(!g_ascii_strcasecmp(elem
->name
, "Record-Route"))
2635 gchar
*route
= sipmsg_find_part_of_header(elem
->value
, "<", ">", NULL
);
2636 dialog
->routes
= g_slist_append(dialog
->routes
, route
);
2638 hdr
= g_slist_next(hdr
);
2643 dialog
->routes
= g_slist_reverse(dialog
->routes
);
2648 dialog
->request
= dialog
->routes
->data
;
2649 dialog
->routes
= g_slist_remove(dialog
->routes
, dialog
->routes
->data
);
2652 contact
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Contact"), "<", ">", NULL
);
2653 dialog
->routes
= g_slist_append(dialog
->routes
, contact
);
2657 sipe_get_supported_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2659 GSList
*hdr
= msg
->headers
;
2660 struct siphdrelement
*elem
;
2664 if(!g_ascii_strcasecmp(elem
->name
, "Supported")
2665 && !g_slist_find_custom(dialog
->supported
, elem
->value
, (GCompareFunc
)g_ascii_strcasecmp
))
2667 dialog
->supported
= g_slist_append(dialog
->supported
, g_strdup(elem
->value
));
2670 hdr
= g_slist_next(hdr
);
2675 sipe_parse_dialog(struct sipmsg
* msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2677 gchar
*us
= outgoing
? "From" : "To";
2678 gchar
*them
= outgoing
? "To" : "From";
2680 g_free(dialog
->callid
);
2681 g_free(dialog
->ourtag
);
2682 g_free(dialog
->theirtag
);
2684 dialog
->callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
2685 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, us
));
2686 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, them
));
2687 if (!dialog
->theirepid
) {
2688 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", ";", NULL
);
2689 if (!dialog
->theirepid
) {
2690 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", NULL
, NULL
);
2694 sipe_get_route_header(msg
, dialog
, outgoing
);
2695 sipe_get_supported_header(msg
, dialog
, outgoing
);
2700 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2702 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2703 struct sip_im_session
* session
= find_im_session(sip
, with
);
2704 struct sip_dialog
*dialog
;
2710 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2715 dialog
= session
->dialog
;
2717 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2722 sipe_parse_dialog(msg
, dialog
, TRUE
);
2724 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2725 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
2727 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2729 if (msg
->response
!= 200) {
2730 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2732 sipe_present_message_undelivered_err(with
, sip
, message
);
2733 im_session_destroy(sip
, session
);
2739 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
2740 session
->outgoing_invite
= NULL
;
2741 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
2742 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
2743 if (session
->outgoing_message_queue
) {
2744 char *queued_msg
= session
->outgoing_message_queue
->data
;
2745 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2750 sipe_im_process_queue(sip
, session
);
2752 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2753 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
2754 key
, g_hash_table_size(session
->unconfirmed_messages
));
2762 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
*session
, const gchar
*msg_body
)
2772 char *ms_text_format
;
2777 if (session
->dialog
) {
2778 purple_debug_info("sipe", "session with %s already has a dialog open\n", session
->with
);
2782 session
->dialog
= g_new0(struct sip_dialog
, 1);
2783 session
->dialog
->callid
= gencallid();
2785 if (strstr(session
->with
, "sip:")) {
2786 to
= g_strdup(session
->with
);
2788 to
= g_strdup_printf("sip:%s", session
->with
);
2791 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
2792 purple_debug_info("sipe", "sipe_invite: msgformat=%s", msgformat
);
2794 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2798 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2802 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
2803 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
2808 key
= g_strdup_printf("<%s><%d><INVITE>", session
->dialog
->callid
, (session
->dialog
->cseq
) + 1);
2809 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
2810 purple_debug_info("sipe", "sipe_im_send: added message %s to unconfirmed_messages(count=%d)\n",
2811 key
, g_hash_table_size(session
->unconfirmed_messages
));
2814 contact
= get_contact(sip
);
2815 /* from = g_strdup_printf("sip:%s", sip->username);*/
2816 hdr
= g_strdup_printf(
2817 /*"Supported: ms-delayed-accept\r\n"*/
2818 /*"Roster-Manager: <%s>\r\n"*/
2819 /*"EndPoints: <%s>, <%s>\r\n"*/
2820 /*"Supported: com.microsoft.rtc-multiparty\r\n"*/
2822 "Content-Type: application/sdp\r\n",
2823 contact
, ms_text_format
);
2824 g_free(ms_text_format
);
2826 body
= g_strdup_printf(
2828 "o=- 0 0 IN IP4 %s\r\n"
2832 "m=message %d sip null\r\n"
2833 "a=accept-types:text/plain text/html image/gif "
2834 "multipart/alternative application/im-iscomposing+xml\r\n",
2835 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
2837 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
2838 to
, to
, hdr
, body
, session
->dialog
, process_invite_response
);
2848 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2851 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->dialog
, NULL
);
2852 im_session_destroy(sip
, session
);
2857 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
2859 struct sipe_account_data
*sip
= gc
->proto_data
;
2861 purple_debug_info("sipe", "conversation with %s closed\n", who
);
2862 im_session_close(sip
, find_im_session(sip
, who
));
2866 im_session_close_all (struct sipe_account_data
*sip
)
2868 GSList
*entry
= sip
->im_sessions
;
2870 im_session_close (sip
, entry
->data
);
2871 entry
= sip
->im_sessions
;
2875 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
2877 struct sipe_account_data
*sip
;
2879 struct sip_im_session
*session
;
2881 sip
= gc
->proto_data
;
2884 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
2886 session
= find_or_create_im_session(sip
, who
);
2888 // Queue the message
2889 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
2891 if (session
->dialog
&& session
->dialog
->callid
) {
2892 sipe_im_process_queue(sip
, session
);
2893 } else if (!session
->outgoing_invite
) {
2894 // Need to send the INVITE to get the outgoing dialog setup
2895 sipe_invite(sip
, session
, what
);
2902 /* End IM Session (INVITE and MESSAGE methods) */
2905 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
2907 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2908 struct sip_im_session
*session
;
2910 if (state
== PURPLE_NOT_TYPING
)
2913 session
= find_im_session(sip
, who
);
2915 if (session
&& session
->dialog
) {
2916 send_sip_request(gc
, "INFO", who
, who
,
2917 "Content-Type: application/xml\r\n",
2918 SIPE_SEND_TYPING
, session
->dialog
, NULL
);
2920 return SIPE_TYPING_SEND_TIMEOUT
;
2923 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
2925 GSList
*tmp
= sip
->transactions
;
2926 time_t currtime
= time(NULL
);
2928 struct transaction
*trans
= tmp
->data
;
2930 purple_debug_info("sipe", "have open transaction age: %ld\n", currtime
-trans
->time
);
2931 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
2934 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
2936 sendout_sipmsg(sip
, trans
->msg
);
2943 static void do_reauthenticate_cb(struct sipe_account_data
*sip
)
2945 /* register again when security token expires */
2946 /* we have to start a new authentication as the security token
2947 * is almost expired by sending a not signed REGISTER message */
2948 purple_debug_info("sipe", "do a full reauthentication\n");
2949 sipe_auth_free(&sip
->registrar
);
2950 sip
->registerstatus
= 0;
2952 sip
->reauthenticate_set
= FALSE
;
2955 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2959 gboolean found
= FALSE
;
2961 from
= parse_from(sipmsg_find_header(msg
, "From"));
2965 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
2967 contenttype
= sipmsg_find_header(msg
, "Content-Type");
2968 if (!strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
2970 gchar
*html
= get_html_message(contenttype
, msg
->body
);
2971 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
2973 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2976 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
2977 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2982 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
2986 state
= xmlnode_get_child(isc
, "state");
2989 purple_debug_info("sipe", "process_incoming_message: no state found\n");
2994 statedata
= xmlnode_get_data(state
);
2996 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
2997 else serv_got_typing_stopped(sip
->gc
, from
);
3002 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3006 purple_debug_info("sipe", "got unknown mime-type");
3007 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
3012 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3014 gchar
*ms_text_format
;
3017 struct sip_im_session
*session
;
3019 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? msg
->body
: "");
3021 // Only accept text invitations
3022 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
3023 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3027 from
= parse_from(sipmsg_find_header(msg
, "From"));
3028 session
= find_or_create_im_session (sip
, from
);
3030 if (session
->dialog
) {
3031 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
3033 session
->dialog
= g_new0(struct sip_dialog
, 1);
3035 sipe_parse_dialog(msg
, session
->dialog
, FALSE
);
3037 session
->dialog
->callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
3038 session
->dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
3039 session
->dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
3040 session
->dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
3043 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
3046 //ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk=
3047 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
3048 if (ms_text_format
) {
3049 if (!strncmp(ms_text_format
, "text/plain", 10) || !strncmp(ms_text_format
, "text/html", 9)) {
3051 gchar
*html
= get_html_message(ms_text_format
, NULL
);
3053 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3055 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message received
3061 sipmsg_remove_header(msg
, "Ms-Conversation-ID");
3062 sipmsg_remove_header(msg
, "Ms-Text-Format");
3063 sipmsg_remove_header(msg
, "EndPoints");
3064 sipmsg_remove_header(msg
, "User-Agent");
3065 sipmsg_remove_header(msg
, "Roster-Manager");
3067 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3068 //sipmsg_add_header(msg, "Supported", "ms-renders-gif");
3070 body
= g_strdup_printf(
3072 "o=- 0 0 IN IP4 %s\r\n"
3076 "m=message %d sip sip:%s\r\n"
3077 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
3078 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
3079 sip
->realport
, sip
->username
);
3080 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3084 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3088 sipmsg_remove_header(msg
, "Ms-Conversation-ID");
3089 sipmsg_remove_header(msg
, "EndPoints");
3090 sipmsg_remove_header(msg
, "User-Agent");
3092 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, BENOTIFY");
3093 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3095 body
= g_strdup_printf(
3097 "o=- 0 0 IN IP4 0.0.0.0\r\n"
3099 "c=IN IP4 0.0.0.0\r\n"
3101 "m=message %d sip sip:%s\r\n"
3102 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
3103 sip
->realport
, sip
->username
);
3104 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3108 static void sipe_connection_cleanup(struct sipe_account_data
*);
3109 static void create_connection(struct sipe_account_data
*, gchar
*, int);
3111 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3114 const gchar
*expires_header
;
3116 GSList
*hdr
= msg
->headers
;
3118 struct siphdrelement
*elem
;
3120 expires_header
= sipmsg_find_header(msg
, "Expires");
3121 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
3122 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
3124 switch (msg
->response
) {
3127 sip
->registerstatus
= 0;
3129 gchar
*contact_hdr
= NULL
;
3135 if (!sip
->reregister_set
) {
3136 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
3137 sipe_schedule_action(action_name
, expires
, (Action
) do_register_cb
, sip
, NULL
);
3138 g_free(action_name
);
3139 sip
->reregister_set
= TRUE
;
3142 sip
->registerstatus
= 3;
3144 if (!sip
->reauthenticate_set
) {
3145 /* we have to reauthenticate as our security token expires
3146 after eight hours (be five minutes early) */
3147 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
3148 guint reauth_timeout
= (8 * 3600) - 360;
3149 sipe_schedule_action(action_name
, reauth_timeout
, (Action
) do_reauthenticate_cb
, sip
, NULL
);
3150 g_free(action_name
);
3151 sip
->reauthenticate_set
= TRUE
;
3154 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
3156 epid
= get_epid(sip
);
3157 uuid
= generateUUIDfromEPID(epid
);
3160 // There can be multiple Contact headers (one per location where the user is logged in) so
3161 // make sure to only get the one for this uuid
3162 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
3163 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
3164 if (valid_contact
) {
3165 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
3166 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
3167 g_free(valid_contact
);
3170 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
3175 g_free(sip
->contact
);
3177 sip
->contact
= g_strdup_printf("<%s>", gruu
);
3180 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
3181 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
);
3183 sip
->msrtc_event_categories
= FALSE
;
3184 sip
->batched_support
= FALSE
;
3189 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
3190 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
3191 sip
->msrtc_event_categories
= TRUE
;
3192 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->msrtc_event_categories
);
3194 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
3195 sip
->batched_support
= TRUE
;
3196 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->batched_support
);
3199 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
3200 gchar
**caps
= g_strsplit(elem
->value
,",",0);
3203 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
3204 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
3209 hdr
= g_slist_next(hdr
);
3212 if (!sip
->subscribed
) { //do it just once, not every re-register
3213 if(!sip
->msrtc_event_categories
){ //Only for LCS2005, on OCS2007 always backs the error 504 Server time-out
3214 sipe_options_request(sip
, sip
->sipdomain
);
3216 entry
= sip
->allow_events
;
3219 if (tmp
&& !g_ascii_strcasecmp(tmp
, "vnd-microsoft-roaming-contacts")) {
3220 sipe_subscribe_roaming_contacts(sip
, msg
);
3222 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-roaming-ACL")) {
3223 sipe_subscribe_roaming_acl(sip
, msg
);
3225 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-roaming-self")) {
3226 sipe_subscribe_roaming_self(sip
, msg
);
3228 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-provisioning-v2")) {
3229 sipe_subscribe_roaming_provisioning_v2(sip
, msg
);
3230 } else if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-provisioning")) { // LSC2005
3231 sipe_subscribe_roaming_provisioning(sip
, msg
);
3233 if (tmp
&& !g_ascii_strcasecmp(tmp
,"presence.wpending")) {
3234 sipe_subscribe_presence_wpending(sip
, msg
);
3236 entry
= entry
->next
;
3238 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
3239 sip
->subscribed
= TRUE
;
3242 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
3243 "timeout=", ";", NULL
);
3244 if (timeout
!= NULL
) {
3245 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
3246 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
3247 sip
->keepalive_timeout
);
3250 sip
->keepalive_timeout
= 300;
3253 // Should we remove the transaction here?
3254 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
3255 transactions_remove(sip
, tc
);
3260 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
3262 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
3263 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
3267 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
3270 tmp
= g_strsplit(parts
[0], ":", 0);
3271 hostname
= g_strdup(tmp
[0]);
3272 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
3276 tmp
= g_strsplit(parts
[i
], "=", 0);
3278 if (g_strcasecmp("transport", tmp
[0]) == 0) {
3279 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
3280 transport
= SIPE_TRANSPORT_TCP
;
3281 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
3282 transport
= SIPE_TRANSPORT_UDP
;
3291 /* Close old connection */
3292 sipe_connection_cleanup(sip
);
3294 /* Create new connection */
3295 sip
->transport
= transport
;
3296 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
3297 hostname
, port
, TRANSPORT_DESCRIPTOR
);
3298 create_connection(sip
, hostname
, port
);
3304 if (sip
->registerstatus
!= 2) {
3305 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
3306 if (sip
->registrar
.retries
> 3) {
3307 sip
->gc
->wants_to_die
= TRUE
;
3308 purple_connection_error(sip
->gc
, _("Wrong Password"));
3311 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3312 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3314 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3316 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3317 fill_auth(sip
, tmp
, &sip
->registrar
);
3318 sip
->registerstatus
= 2;
3319 if (sip
->account
->disconnecting
) {
3320 do_register_exp(sip
, 0);
3328 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
3329 if (warning
!= NULL
) {
3331 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
3333 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
3334 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
3337 warning
= g_strdup(_("You have been rejected by the server"));
3340 sip
->gc
->wants_to_die
= TRUE
;
3341 purple_connection_error(sip
->gc
, warning
);
3348 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3349 if (warning
!= NULL
) {
3350 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3351 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
3354 warning
= g_strdup(_("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator"));
3357 sip
->gc
->wants_to_die
= TRUE
;
3358 purple_connection_error(sip
->gc
, warning
);
3365 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3366 if (warning
!= NULL
) {
3367 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3368 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
3371 warning
= g_strdup(_("Service unavailable: no reason given"));
3374 sip
->gc
->wants_to_die
= TRUE
;
3375 purple_connection_error(sip
->gc
, warning
);
3384 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3387 xmlnode
*xn_categories
;
3388 xmlnode
*xn_category
;
3390 const char *activity
= NULL
;
3392 xn_categories
= xmlnode_from_str(data
, len
);
3393 uri
= xmlnode_get_attrib(xn_categories
, "uri");
3395 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
3397 xn_category
= xmlnode_get_next_twin(xn_category
) )
3399 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
3401 if (!strcmp(attrVar
, "note"))
3404 struct sipe_buddy
*sbuddy
;
3405 xn_node
= xmlnode_get_child(xn_category
, "note");
3406 if (!xn_node
) continue;
3407 xn_node
= xmlnode_get_child(xn_node
, "body");
3408 if (!xn_node
) continue;
3410 note
= xmlnode_get_data(xn_node
);
3413 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3417 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s),note(%s)\n",uri
,note
);
3418 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3419 sbuddy
->annotation
= g_strdup(note
);
3424 else if(!strcmp(attrVar
, "state"))
3428 xn_node
= xmlnode_get_child(xn_category
, "state");
3429 if (!xn_node
) continue;
3430 xn_node
= xmlnode_get_child(xn_node
, "availability");
3431 if (!xn_node
) continue;
3433 data
= xmlnode_get_data(xn_node
);
3438 activity
= SIPE_STATUS_ID_UNKNOWN
;
3439 else if (avail
< 4500)
3440 activity
= SIPE_STATUS_ID_AVAILABLE
;
3441 else if (avail
< 6000)
3442 activity
= SIPE_STATUS_ID_BRB
;
3443 else if (avail
< 7500)
3444 activity
= SIPE_STATUS_ID_ONPHONE
;
3445 else if (avail
< 9000)
3446 activity
= SIPE_STATUS_ID_BUSY
;
3447 else if (avail
< 12000)
3448 activity
= SIPE_STATUS_ID_DND
;
3449 else if (avail
< 18000)
3450 activity
= SIPE_STATUS_ID_AWAY
;
3452 activity
= SIPE_STATUS_ID_OFFLINE
;
3456 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", activity
);
3457 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
3460 xmlnode_free(xn_categories
);
3463 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3465 const char *uri
,*state
;
3467 xmlnode
*xn_resource
;
3468 xmlnode
*xn_instance
;
3470 xn_list
= xmlnode_from_str(data
, len
);
3472 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
3474 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
3476 struct sipe_buddy
*sbuddy
;
3477 uri
= xmlnode_get_attrib(xn_resource
, "uri");
3478 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
3479 if (!xn_instance
) return;
3481 state
= xmlnode_get_attrib(xn_instance
, "state");
3482 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n",uri
,state
);
3483 if(strstr(state
,"resubscribe")){
3484 sipe_subscribe_presence_single(sip
, uri
);
3485 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3487 sbuddy
->resubscribed
= TRUE
;
3494 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3498 gchar
*activity
= NULL
;
3500 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
3501 gboolean isonline
= FALSE
;
3502 xmlnode
*display_name_node
;
3504 pidf
= xmlnode_from_str(data
, len
);
3506 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data
);
3510 uri
= xmlnode_get_attrib(pidf
, "entity");
3512 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
3514 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3515 basicstatus
= xmlnode_get_child(status
, "basic");
3520 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
3525 getbasic
= xmlnode_get_data(basicstatus
);
3527 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
3532 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
3533 if (strstr(getbasic
, "open")) {
3538 display_name_node
= xmlnode_get_child(pidf
, "display-name");
3539 // updating display name if alias was just URI
3540 if (display_name_node
) {
3541 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
3542 GSList
*entry
= buddies
;
3543 PurpleBuddy
*p_buddy
;
3544 char * display_name
= xmlnode_get_data(display_name_node
);
3547 const char *server_alias
;
3550 p_buddy
= entry
->data
;
3552 alias
= (char *)purple_buddy_get_alias(p_buddy
);
3553 alias
= alias
? g_strdup_printf("sip:%s", alias
) : NULL
;
3554 if (!alias
|| !g_ascii_strcasecmp(uri
, alias
)) {
3555 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
3556 purple_blist_alias_buddy(p_buddy
, display_name
);
3560 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3562 ( (server_alias
&& strcmp(display_name
, server_alias
))
3563 || !server_alias
|| strlen(server_alias
) == 0 )
3565 purple_blist_server_alias_buddy(p_buddy
, display_name
);
3568 entry
= entry
->next
;
3570 g_free(display_name
);
3573 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
3574 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3575 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
3576 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
3577 activity
= xmlnode_get_data(basicstatus
);
3578 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
3585 const gchar
* status_id
= NULL
;
3587 if (strstr(activity
, "busy")) {
3588 status_id
= SIPE_STATUS_ID_BUSY
;
3589 } else if (strstr(activity
, "away")) {
3590 status_id
= SIPE_STATUS_ID_AWAY
;
3595 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3598 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
3599 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
3601 purple_prpl_got_user_status(sip
->account
, uri
, SIPE_STATUS_ID_OFFLINE
, NULL
);
3608 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3610 const char *availability
;
3611 const char *activity
;
3612 const char *display_name
= NULL
;
3613 const char *activity_name
= NULL
;
3618 struct sipe_buddy
*sbuddy
;
3620 xmlnode
*xn_presentity
= xmlnode_from_str(data
, len
);
3622 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
3623 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
3624 xmlnode
*xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
3625 xmlnode
*xn_email
= xmlnode_get_child(xn_presentity
, "email");
3626 const char *email
= xn_email
? xmlnode_get_attrib(xn_email
, "email") : NULL
;
3627 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
3628 xmlnode
*xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
):NULL
;
3629 const char *avail
= xn_state
? xmlnode_get_attrib(xn_state
, "avail") : NULL
;
3631 xmlnode
*xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
3632 char *note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
3633 xmlnode
*xn_devices
= xmlnode_get_child(xn_presentity
, "devices");
3634 xmlnode
*xn_device_presence
= xn_devices
? xmlnode_get_child(xn_devices
, "devicePresence") : NULL
;
3635 xmlnode
*xn_device_name
= xn_device_presence
? xmlnode_get_child(xn_device_presence
, "deviceName") : NULL
;
3636 const char *device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
3638 name
= xmlnode_get_attrib(xn_presentity
, "uri");
3639 uri
= g_strdup_printf("sip:%s", name
);
3640 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
3641 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
3643 // updating display name if alias was just URI
3644 if (xn_display_name
) {
3645 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
3646 GSList
*entry
= buddies
;
3647 PurpleBuddy
*p_buddy
;
3648 display_name
= xmlnode_get_attrib(xn_display_name
, "displayName");
3651 const char *email_str
, *server_alias
;
3653 p_buddy
= entry
->data
;
3655 if (!g_ascii_strcasecmp(name
, purple_buddy_get_alias(p_buddy
))) {
3656 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
3657 purple_blist_alias_buddy(p_buddy
, display_name
);
3660 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3662 ( (server_alias
&& strcmp(display_name
, server_alias
))
3663 || !server_alias
|| strlen(server_alias
) == 0 )
3665 purple_blist_server_alias_buddy(p_buddy
, display_name
);
3669 email_str
= purple_blist_node_get_string((PurpleBlistNode
*)p_buddy
, "email");
3670 if (!email_str
|| g_ascii_strcasecmp(email_str
, email
)) {
3671 purple_blist_node_set_string((PurpleBlistNode
*)p_buddy
, "email", email
);
3675 entry
= entry
->next
;
3679 avl
= atoi(availability
);
3680 act
= atoi(activity
);
3682 if(sip
->msrtc_event_categories
){
3683 if (act
== 100 && avl
== 0)
3684 activity_name
= SIPE_STATUS_ID_OFFLINE
;
3685 else if (act
== 100 && avl
== 300)
3686 activity_name
= SIPE_STATUS_ID_AWAY
;
3687 else if (act
== 300 && avl
== 300)
3688 activity_name
= SIPE_STATUS_ID_BRB
;
3689 else if (act
== 400 && avl
== 300)
3690 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
3691 else if (act
== 500 && act
== 300)
3692 activity_name
= SIPE_STATUS_ID_ONPHONE
;
3693 else if (act
== 600 && avl
== 300)
3694 activity_name
= SIPE_STATUS_ID_BUSY
;
3695 else if (act
== 0 && avl
== 0){ //MSRTC elements are zero
3696 if(avail
){ //Check for LegacyInterop elements
3699 activity_name
= SIPE_STATUS_ID_OFFLINE
;
3700 else if (avl
== 3500)
3701 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
3702 else if (avl
== 15500)
3703 activity_name
= SIPE_STATUS_ID_AWAY
;
3704 else if (avl
== 6500)
3705 activity_name
= SIPE_STATUS_ID_BUSY
;
3706 else if (avl
== 12500)
3707 activity_name
= SIPE_STATUS_ID_BRB
;
3712 if(activity_name
== NULL
){
3714 activity_name
= SIPE_STATUS_ID_AWAY
;
3715 else if (act
<= 150)
3716 activity_name
= SIPE_STATUS_ID_LUNCH
;
3717 else if (act
<= 300)
3718 activity_name
= SIPE_STATUS_ID_BRB
;
3719 else if (act
<= 400)
3720 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
3721 else if (act
<= 500)
3722 activity_name
= SIPE_STATUS_ID_ONPHONE
;
3723 else if (act
<= 600)
3724 activity_name
= SIPE_STATUS_ID_BUSY
;
3726 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
3729 activity_name
= SIPE_STATUS_ID_OFFLINE
;
3732 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3735 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3736 sbuddy
->annotation
= NULL
;
3737 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
3739 if (sbuddy
->device_name
) { g_free(sbuddy
->device_name
); }
3740 sbuddy
->device_name
= NULL
;
3741 if (device_name
) { sbuddy
->device_name
= g_strdup(device_name
); }
3744 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
3745 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
3747 xmlnode_free(xn_presentity
);
3751 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3753 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
3755 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
3757 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
3758 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
3760 const char *content
= msg
->body
;
3761 unsigned length
= msg
->bodylen
;
3762 PurpleMimeDocument
*mime
= NULL
;
3764 if (strstr(ctype
, "multipart"))
3766 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
3767 const char *content_type
;
3769 mime
= purple_mime_document_parse(doc
);
3770 parts
= purple_mime_document_get_parts(mime
);
3772 content
= purple_mime_part_get_data(parts
->data
);
3773 length
= purple_mime_part_get_length(parts
->data
);
3774 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
3775 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
3777 process_incoming_notify_rlmi_resub(sip
, content
, length
);
3779 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
3781 process_incoming_notify_msrtc(sip
, content
, length
);
3785 process_incoming_notify_rlmi(sip
, content
, length
);
3787 parts
= parts
->next
;
3793 purple_mime_document_free(mime
);
3796 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
3798 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
3800 else if(strstr(ctype
, "application/rlmi+xml"))
3802 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
3805 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
3807 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
3811 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
3816 * Dispatcher for all incoming subscription information
3817 * whether it comes from NOTIFY, BENOTIFY requests or
3818 * piggy-backed to subscription's OK responce.
3820 * @param request whether initiated from BE/NOTIFY request or OK-response message.
3821 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
3823 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
3825 gchar
*event
= sipmsg_find_header(msg
, "Event");
3826 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
3827 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
3830 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event
? event
: "", msg
->body
);
3831 purple_debug_info("sipe", "process_incoming_notify: subscription_state:%s\n\n", subscription_state
);
3835 const gchar
*expires_header
;
3836 expires_header
= sipmsg_find_header(msg
, "Expires");
3837 timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
3838 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n\n", timeout
);
3839 timeout
= (timeout
- 60) > 60 ? (timeout
- 60) : timeout
; // 1 min ahead of expiration
3842 if (!subscription_state
|| strstr(subscription_state
, "active"))
3844 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
3846 sipe_process_presence(sip
, msg
);
3848 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
3850 sipe_process_roaming_contacts(sip
, msg
, NULL
);
3852 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self") )
3854 sipe_process_roaming_self(sip
, msg
);
3856 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
3858 sipe_process_roaming_acl(sip
, msg
);
3860 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
3862 sipe_process_presence_wpending(sip
, msg
);
3866 purple_debug_info("sipe", "Unable to process (BE)NOTIFY. Event is not supported:%s\n", event
? event
: "");
3870 //The server sends a (BE)NOTIFY with the status 'terminated'
3871 if(request
&& subscription_state
&& strstr(subscription_state
, "terminated") ) {
3872 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3873 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from
);
3877 if (timeout
&& event
) {// For LSC 2005 and OCS 2007
3878 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
3879 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
3881 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
3882 sipe_schedule_action(action_name, timeout, (Action) sipe_subscribe_roaming_contacts, sip, msg);
3883 g_free(action_name);
3885 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
3886 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
3888 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
3889 sipe_schedule_action(action_name, timeout, (Action) sipe_subscribe_roaming_acl, sip, msg);
3890 g_free(action_name);
3893 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
3894 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
3896 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
3897 sipe_schedule_action(action_name
, timeout
, (Action
) sipe_subscribe_presence_wpending
, sip
, NULL
);
3898 g_free(action_name
);
3900 else if (!g_ascii_strcasecmp(event
, "presence") &&
3901 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
3903 gchar
*action_name
= g_strdup_printf("<%s><%s>", "presence", who
);
3904 if(sip
->batched_support
){
3905 gchar
*my_self
= g_strdup_printf("sip:%s",sip
->username
);
3906 if(!g_ascii_strcasecmp(who
, my_self
)){
3907 sipe_schedule_action(action_name
, timeout
, (Action
) sipe_subscribe_presence_batched
, sip
,NULL
);
3908 purple_debug_info("sipe", "Resubscription full batched list in %d\n",timeout
);
3911 sipe_schedule_action(action_name
, timeout
, (Action
) sipe_subscribe_presence_single
, sip
, who
);
3912 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
,timeout
);
3917 sipe_schedule_action(action_name
, timeout
, (Action
) sipe_subscribe_presence_single
, sip
, who
);
3918 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
,timeout
);
3920 g_free(action_name
);
3924 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
3926 sipe_process_registration_notify(sip
, msg
);
3929 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
3930 if (request
&& !benotify
)
3932 sipmsg_remove_header(msg
, "Expires");
3933 sipmsg_remove_header(msg
, "subscription-state");
3934 sipmsg_remove_header(msg
, "Event");
3935 sipmsg_remove_header(msg
, "Require");
3936 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3943 static gchar* gen_xpidf(struct sipe_account_data *sip)
3945 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3947 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
3948 "<display name=\"sip:%s\"/>\r\n"
3949 "<atom id=\"1234\">\r\n"
3950 "<address uri=\"sip:%s\">\r\n"
3951 "<status status=\"%s\"/>\r\n"
3964 static gchar* gen_pidf(struct sipe_account_data *sip)
3966 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3967 "<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"
3968 "<tuple id=\"0\">\r\n"
3970 "<basic>open</basic>\r\n"
3971 "<ep:activities>\r\n"
3972 " <ep:activity>%s</ep:activity>\r\n"
3976 "<ci:display-name>%s</ci:display-name>\r\n"
3985 static void send_presence_soap(struct sipe_account_data
*sip
, const char * note
)
3987 int availability
= 300; // online
3988 int activity
= 400; // Available
3991 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
)) {
3993 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
3995 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
3997 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
3999 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4001 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4003 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_INVISIBLE
) ||
4004 !strcmp(sip
->status
, SIPE_STATUS_ID_OFFLINE
)) {
4005 availability
= 0; // offline
4008 activity
= 400; // available
4011 name
= g_strdup_printf("sip: sip:%s", sip
->username
);
4012 //@TODO: send user data - state; add hostname in upper case
4013 body
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
4014 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
4020 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
4022 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4023 if (msg
->response
== 200) {
4024 sip
->status_version
= 0;
4025 send_presence_status(sip
);
4031 process_send_presence_category_publish_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
4033 if (msg
->response
== 409) {
4034 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4035 // TODO need to parse the version #'s?
4036 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
4037 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
4041 purple_debug_info("sipe", "process_send_presence_category_publish_response = %s\n", msg
->body
);
4043 tmp
= get_contact(sip
);
4044 hdr
= g_strdup_printf("Contact: %s\r\n"
4045 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4047 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
4057 static void send_presence_category_publish(struct sipe_account_data
*sip
, const char * note
)
4064 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
) ||
4065 !strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4067 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_DND
)) {
4069 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4071 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4073 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4075 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4077 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_UNKNOWN
)) {
4080 // Offline or invisible
4084 uri
= g_strdup_printf("sip:%s", sip
->username
);
4085 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
4086 sip
->status_version
, code
,
4087 sip
->status_version
, code
,
4088 sip
->status_version
, note
? note
: "",
4089 sip
->status_version
, note
? note
: "",
4090 sip
->status_version
, note
? note
: ""
4092 sip
->status_version
++;
4094 tmp
= get_contact(sip
);
4095 hdr
= g_strdup_printf("Contact: %s\r\n"
4096 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4098 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
4106 static void send_presence_status(struct sipe_account_data
*sip
)
4108 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
4110 if (!status
) return;
4112 note
= purple_status_get_attr_string(status
, "message");
4114 if(sip
->msrtc_event_categories
){
4115 send_presence_category_publish(sip
, note
);
4117 send_presence_soap(sip
, note
);
4121 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
4123 gboolean found
= FALSE
;
4124 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
4125 if (msg
->response
== 0) { /* request */
4126 if (!strcmp(msg
->method
, "MESSAGE")) {
4127 process_incoming_message(sip
, msg
);
4129 } else if (!strcmp(msg
->method
, "NOTIFY")) {
4130 purple_debug_info("sipe","send->process_incoming_notify\n");
4131 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
4133 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
4134 purple_debug_info("sipe","send->process_incoming_benotify\n");
4135 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
4137 } else if (!strcmp(msg
->method
, "INVITE")) {
4138 process_incoming_invite(sip
, msg
);
4140 } else if (!strcmp(msg
->method
, "OPTIONS")) {
4141 process_incoming_options(sip
, msg
);
4143 } else if (!strcmp(msg
->method
, "INFO")) {
4145 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4147 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
4150 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4152 } else if (!strcmp(msg
->method
, "ACK")) {
4153 // ACK's don't need any response
4155 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
4156 // LCS 2005 sends us these - just respond 200 OK
4158 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4159 } else if (!strcmp(msg
->method
, "BYE")) {
4160 struct sip_im_session
*session
;
4162 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4164 from
= parse_from(sipmsg_find_header(msg
, "From"));
4165 session
= find_im_session (sip
, from
);
4169 // TODO Let the user know the other user left the conversation?
4170 im_session_destroy(sip
, session
);
4175 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
4177 } else { /* response */
4178 struct transaction
*trans
= transactions_find(sip
, msg
);
4180 if (msg
->response
== 407) {
4181 gchar
*resend
, *auth
, *ptmp
;
4183 if (sip
->proxy
.retries
> 30) return;
4184 sip
->proxy
.retries
++;
4185 /* do proxy authentication */
4187 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
4189 fill_auth(sip
, ptmp
, &sip
->proxy
);
4190 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
4191 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
4192 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
4194 resend
= sipmsg_to_string(trans
->msg
);
4195 /* resend request */
4196 sendout_pkt(sip
->gc
, resend
);
4199 if (msg
->response
== 100 || msg
->response
== 180) {
4200 /* ignore provisional response */
4201 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
4203 sip
->proxy
.retries
= 0;
4204 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
4205 if (msg
->response
== 401)
4207 sip
->registrar
.retries
++;
4208 sip
->registrar
.expires
= 0;
4212 sip
->registrar
.retries
= 0;
4214 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
4216 if (msg
->response
== 401) {
4217 gchar
*resend
, *auth
, *ptmp
;
4219 if (sip
->registrar
.retries
> 4) return;
4220 sip
->registrar
.retries
++;
4222 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4223 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
4225 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4228 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
4230 fill_auth(sip
, ptmp
, &sip
->registrar
);
4231 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
4232 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
4233 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
4235 //sipmsg_remove_header(trans->msg, "Authorization");
4236 //sipmsg_add_header(trans->msg, "Authorization", auth);
4238 resend
= sipmsg_to_string(trans
->msg
);
4239 /* resend request */
4240 sendout_pkt(sip
->gc
, resend
);
4245 if (trans
->callback
) {
4246 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
4247 /* call the callback to process response*/
4248 (trans
->callback
)(sip
, msg
, trans
);
4250 /* Not sure if this is needed or what needs to be done
4251 but transactions seem to be removed prematurely so
4252 this only removes them if the response is 200 OK */
4253 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
4254 /*Has a bug and it's unneccesary*/
4255 /*transactions_remove(sip, trans);*/
4261 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
4265 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
4269 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
4277 /* according to the RFC remove CRLF at the beginning */
4278 while (*cur
== '\r' || *cur
== '\n') {
4281 if (cur
!= conn
->inbuf
) {
4282 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
4283 conn
->inbufused
= strlen(conn
->inbuf
);
4286 /* Received a full Header? */
4287 sip
->processing_input
= TRUE
;
4288 while (sip
->processing_input
&&
4289 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
4290 time_t currtime
= time(NULL
);
4293 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
4294 msg
= sipmsg_parse_header(conn
->inbuf
);
4297 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
4298 if (restlen
>= msg
->bodylen
) {
4299 dummy
= g_malloc(msg
->bodylen
+ 1);
4300 memcpy(dummy
, cur
, msg
->bodylen
);
4301 dummy
[msg
->bodylen
] = '\0';
4303 cur
+= msg
->bodylen
;
4304 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
4305 conn
->inbufused
= strlen(conn
->inbuf
);
4307 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
4308 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
4314 purple_debug_info("sipe", "body:\n%s", msg->body);
4317 // Verify the signature before processing it
4318 if (sip
->registrar
.ntlm_key
) {
4319 struct sipmsg_breakdown msgbd
;
4320 gchar
*signature_input_str
;
4321 gchar
*signature
= NULL
;
4324 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
4325 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
4326 if (signature_input_str
!= NULL
) {
4327 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
4329 g_free(signature_input_str
);
4331 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
4333 if (signature
!= NULL
) {
4334 if (rspauth
!= NULL
) {
4335 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
4336 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
4337 process_input_message(sip
, msg
);
4339 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
4340 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
4341 sip
->gc
->wants_to_die
= TRUE
;
4343 } else if (msg
->response
== 401) {
4344 purple_connection_error(sip
->gc
, _("Wrong Password"));
4345 sip
->gc
->wants_to_die
= TRUE
;
4351 sipmsg_breakdown_free(&msgbd
);
4353 process_input_message(sip
, msg
);
4360 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
4362 PurpleConnection
*gc
= data
;
4363 struct sipe_account_data
*sip
= gc
->proto_data
;
4368 static char buffer
[65536];
4369 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
4371 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
4372 msg
= sipmsg_parse_msg(buffer
);
4373 if (msg
) process_input_message(sip
, msg
);
4377 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
4379 struct sipe_account_data
*sip
= gc
->proto_data
;
4380 PurpleSslConnection
*gsc
= sip
->gsc
;
4382 purple_debug_error("sipe", "%s",debug
);
4383 purple_connection_error(gc
, msg
);
4385 /* Invalidate this connection. Next send will open a new one */
4387 connection_remove(sip
, gsc
->fd
);
4388 purple_ssl_close(gsc
);
4394 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
4396 PurpleConnection
*gc
= data
;
4397 struct sipe_account_data
*sip
;
4398 struct sip_connection
*conn
;
4400 gboolean firstread
= TRUE
;
4402 /* NOTE: This check *IS* necessary */
4403 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
4404 purple_ssl_close(gsc
);
4408 sip
= gc
->proto_data
;
4409 conn
= connection_find(sip
, gsc
->fd
);
4411 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
4412 gc
->wants_to_die
= TRUE
;
4413 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
4417 /* Read all available data from the SSL connection */
4419 /* Increase input buffer size as needed */
4420 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
4421 conn
->inbuflen
+= SIMPLE_BUF_INC
;
4422 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
4423 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
4426 /* Try to read as much as there is space left in the buffer */
4427 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
4428 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
4430 if (len
< 0 && errno
== EAGAIN
) {
4431 /* Try again later */
4433 } else if (len
< 0) {
4434 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
4436 } else if (firstread
&& (len
== 0)) {
4437 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
4441 conn
->inbufused
+= len
;
4444 /* Equivalence indicates that there is possibly more data to read */
4445 } while (len
== readlen
);
4447 conn
->inbuf
[conn
->inbufused
] = '\0';
4448 process_input(sip
, conn
);
4452 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
4454 PurpleConnection
*gc
= data
;
4455 struct sipe_account_data
*sip
= gc
->proto_data
;
4457 struct sip_connection
*conn
= connection_find(sip
, source
);
4459 purple_debug_error("sipe", "Connection not found!\n");
4463 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
4464 conn
->inbuflen
+= SIMPLE_BUF_INC
;
4465 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
4468 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
4470 if (len
< 0 && errno
== EAGAIN
)
4472 else if (len
<= 0) {
4473 purple_debug_info("sipe", "sipe_input_cb: read error\n");
4474 connection_remove(sip
, source
);
4475 if (sip
->fd
== source
) sip
->fd
= -1;
4479 conn
->inbufused
+= len
;
4480 conn
->inbuf
[conn
->inbufused
] = '\0';
4482 process_input(sip
, conn
);
4485 /* Callback for new connections on incoming TCP port */
4486 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
4488 PurpleConnection
*gc
= data
;
4489 struct sipe_account_data
*sip
= gc
->proto_data
;
4490 struct sip_connection
*conn
;
4492 int newfd
= accept(source
, NULL
, NULL
);
4494 conn
= connection_create(sip
, newfd
);
4496 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4499 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
4501 PurpleConnection
*gc
= data
;
4502 struct sipe_account_data
*sip
;
4503 struct sip_connection
*conn
;
4505 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4513 purple_connection_error(gc
, _("Could not connect"));
4517 sip
= gc
->proto_data
;
4519 sip
->last_keepalive
= time(NULL
);
4521 conn
= connection_create(sip
, source
);
4525 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4528 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
4530 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
4531 if (sip
== NULL
) return;
4536 static guint
sipe_ht_hash_nick(const char *nick
)
4538 char *lc
= g_utf8_strdown(nick
, -1);
4539 guint bucket
= g_str_hash(lc
);
4545 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
4547 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
4550 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
4552 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4554 sip
->listen_data
= NULL
;
4556 if (listenfd
== -1) {
4557 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4563 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
4564 sip
->listenfd
= sip
->fd
;
4566 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
4568 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
4572 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
4574 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4577 sip
->query_data
= NULL
;
4579 if (!hosts
|| !hosts
->data
) {
4580 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
4584 addr_size
= GPOINTER_TO_INT(hosts
->data
);
4585 hosts
= g_slist_remove(hosts
, hosts
->data
);
4586 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
4587 g_free(hosts
->data
);
4588 hosts
= g_slist_remove(hosts
, hosts
->data
);
4590 hosts
= g_slist_remove(hosts
, hosts
->data
);
4591 g_free(hosts
->data
);
4592 hosts
= g_slist_remove(hosts
, hosts
->data
);
4595 /* create socket for incoming connections */
4596 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
4597 sipe_udp_host_resolved_listen_cb
, sip
);
4598 if (sip
->listen_data
== NULL
) {
4599 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4604 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
4607 PurpleConnection
*gc
= data
;
4608 struct sipe_account_data
*sip
;
4610 /* If the connection is already disconnected, we don't need to do anything else */
4611 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4614 sip
= gc
->proto_data
;
4619 case PURPLE_SSL_CONNECT_FAILED
:
4620 purple_connection_error(gc
, _("Connection Failed"));
4622 case PURPLE_SSL_HANDSHAKE_FAILED
:
4623 purple_connection_error(gc
, _("SSL Handshake Failed"));
4625 case PURPLE_SSL_CERTIFICATE_INVALID
:
4626 purple_connection_error(gc
, _("SSL Certificate Invalid"));
4632 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
4634 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4635 PurpleProxyConnectData
*connect_data
;
4637 sip
->listen_data
= NULL
;
4639 sip
->listenfd
= listenfd
;
4640 if (sip
->listenfd
== -1) {
4641 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4645 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
4646 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
4647 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
4648 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
4649 sipe_newconn_cb
, sip
->gc
);
4650 purple_debug_info("sipe", "connecting to %s port %d\n",
4651 sip
->realhostname
, sip
->realport
);
4652 /* open tcp connection to the server */
4653 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
4654 sip
->realport
, login_cb
, sip
->gc
);
4656 if (connect_data
== NULL
) {
4657 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
4662 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
4664 PurpleAccount
*account
= sip
->account
;
4665 PurpleConnection
*gc
= sip
->gc
;
4667 if (purple_account_get_bool(account
, "useport", FALSE
)) {
4668 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
4669 port
= purple_account_get_int(account
, "port", 0);
4671 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
4674 sip
->realhostname
= hostname
;
4675 sip
->realport
= port
;
4677 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
4680 /* TODO: is there a good default grow size? */
4681 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
4682 sip
->txbuf
= purple_circ_buffer_new(0);
4684 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
4686 if (!purple_ssl_is_supported()) {
4687 gc
->wants_to_die
= TRUE
;
4688 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
4692 purple_debug_info("sipe", "using SSL\n");
4694 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
4695 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
4696 if (sip
->gsc
== NULL
) {
4697 purple_connection_error(gc
, _("Could not create SSL context"));
4700 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
4702 purple_debug_info("sipe", "using UDP\n");
4704 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
4705 if (sip
->query_data
== NULL
) {
4706 purple_connection_error(gc
, _("Could not resolve hostname"));
4710 purple_debug_info("sipe", "using TCP\n");
4711 /* create socket for incoming connections */
4712 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
4713 sipe_tcp_connect_listen_cb
, sip
);
4714 if (sip
->listen_data
== NULL
) {
4715 purple_connection_error(gc
, _("Could not create listen socket"));
4721 /* Service list for autodection */
4722 static const struct sipe_service_data service_autodetect
[] = {
4723 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4724 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4725 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4726 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4730 /* Service list for SSL/TLS */
4731 static const struct sipe_service_data service_tls
[] = {
4732 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4733 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4737 /* Service list for TCP */
4738 static const struct sipe_service_data service_tcp
[] = {
4739 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4740 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4744 /* Service list for UDP */
4745 static const struct sipe_service_data service_udp
[] = {
4746 { "sip", "udp", SIPE_TRANSPORT_UDP
},
4750 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
4751 static void resolve_next_service(struct sipe_account_data
*sip
,
4752 const struct sipe_service_data
*start
)
4755 sip
->service_data
= start
;
4757 sip
->service_data
++;
4758 if (sip
->service_data
->service
== NULL
) {
4760 /* Try connecting to the SIP hostname directly */
4761 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
4762 if (sip
->auto_transport
) {
4763 // If SSL is supported, default to using it; OCS servers aren't configured
4764 // by default to accept TCP
4765 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
4766 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
4767 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
4770 hostname
= g_strdup(sip
->sipdomain
);
4771 create_connection(sip
, hostname
, 0);
4776 /* Try to resolve next service */
4777 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
4778 sip
->service_data
->transport
,
4783 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
4785 struct sipe_account_data
*sip
= data
;
4787 sip
->srv_query_data
= NULL
;
4789 /* find the host to connect to */
4791 gchar
*hostname
= g_strdup(resp
->hostname
);
4792 int port
= resp
->port
;
4793 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
4797 sip
->transport
= sip
->service_data
->type
;
4799 create_connection(sip
, hostname
, port
);
4801 resolve_next_service(sip
, NULL
);
4805 static void sipe_login(PurpleAccount
*account
)
4807 PurpleConnection
*gc
;
4808 struct sipe_account_data
*sip
;
4809 gchar
**signinname_login
, **userserver
, **domain_user
;
4810 const char *transport
;
4812 const char *username
= purple_account_get_username(account
);
4813 gc
= purple_account_get_connection(account
);
4815 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
4816 gc
->wants_to_die
= TRUE
;
4817 purple_connection_error(gc
, _("SIP Exchange username contains invalid characters"));
4821 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
4822 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
4823 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
4825 sip
->account
= account
;
4826 sip
->reregister_set
= FALSE
;
4827 sip
->reauthenticate_set
= FALSE
;
4828 sip
->subscribed
= FALSE
;
4829 sip
->subscribed_buddies
= FALSE
;
4831 signinname_login
= g_strsplit(username
, ",", 2);
4833 userserver
= g_strsplit(signinname_login
[0], "@", 2);
4834 purple_connection_set_display_name(gc
, userserver
[0]);
4835 sip
->username
= g_strjoin("@", userserver
[0], userserver
[1], NULL
);
4836 sip
->sipdomain
= g_strdup(userserver
[1]);
4838 if (strpbrk(sip
->username
, " \t\v\r\n") != NULL
) {
4839 gc
->wants_to_die
= TRUE
;
4840 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
4844 domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
4845 sip
->authdomain
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[0]) : NULL
;
4846 sip
->authuser
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[1]) : (signinname_login
? g_strdup(signinname_login
[1]) : NULL
);
4848 sip
->password
= g_strdup(purple_connection_get_password(gc
));
4850 g_strfreev(userserver
);
4851 g_strfreev(domain_user
);
4852 g_strfreev(signinname_login
);
4854 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
4856 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
4858 /* TODO: Set the status correctly. */
4859 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
4861 transport
= purple_account_get_string(account
, "transport", "auto");
4862 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
4863 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
4866 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
4867 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
4868 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
4869 } else if (strcmp(transport
, "auto") == 0) {
4870 sip
->auto_transport
= TRUE
;
4871 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
4872 } else if (strcmp(transport
, "tls") == 0) {
4873 resolve_next_service(sip
, service_tls
);
4874 } else if (strcmp(transport
, "tcp") == 0) {
4875 resolve_next_service(sip
, service_tcp
);
4877 resolve_next_service(sip
, service_udp
);
4881 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
4883 connection_free_all(sip
);
4888 if (sip
->query_data
!= NULL
)
4889 purple_dnsquery_destroy(sip
->query_data
);
4890 sip
->query_data
= NULL
;
4892 if (sip
->srv_query_data
!= NULL
)
4893 purple_srv_cancel(sip
->srv_query_data
);
4894 sip
->srv_query_data
= NULL
;
4896 if (sip
->listen_data
!= NULL
)
4897 purple_network_listen_cancel(sip
->listen_data
);
4898 sip
->listen_data
= NULL
;
4900 if (sip
->gsc
!= NULL
)
4901 purple_ssl_close(sip
->gsc
);
4904 sipe_auth_free(&sip
->registrar
);
4905 sipe_auth_free(&sip
->proxy
);
4908 purple_circ_buffer_destroy(sip
->txbuf
);
4911 g_free(sip
->realhostname
);
4912 sip
->realhostname
= NULL
;
4915 purple_input_remove(sip
->listenpa
);
4917 if (sip
->tx_handler
)
4918 purple_input_remove(sip
->tx_handler
);
4919 sip
->tx_handler
= 0;
4920 if (sip
->resendtimeout
)
4921 purple_timeout_remove(sip
->resendtimeout
);
4922 sip
->resendtimeout
= 0;
4923 if (sip
->timeouts
) {
4924 GSList
*entry
= sip
->timeouts
;
4926 struct scheduled_action
*sched_action
= entry
->data
;
4927 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
4928 purple_timeout_remove(sched_action
->timeout_handler
);
4929 g_free(sched_action
->payload
);
4930 g_free(sched_action
->name
);
4931 g_free(sched_action
);
4932 entry
= entry
->next
;
4935 g_slist_free(sip
->timeouts
);
4937 if (sip
->allow_events
) {
4938 GSList
*entry
= sip
->allow_events
;
4940 g_free(entry
->data
);
4941 entry
= entry
->next
;
4944 g_slist_free(sip
->allow_events
);
4947 g_free(sip
->contact
);
4948 sip
->contact
= NULL
;
4950 g_free(sip
->regcallid
);
4951 sip
->regcallid
= NULL
;
4954 sip
->processing_input
= FALSE
;
4958 * A callback for g_hash_table_foreach_remove
4960 static gboolean
sipe_buddy_remove(gpointer key
, struct sipe_buddy
*buddy
, gpointer user_data
)
4962 sipe_free_buddy(buddy
);
4965 static void sipe_close(PurpleConnection
*gc
)
4967 struct sipe_account_data
*sip
= gc
->proto_data
;
4970 /* leave all conversations */
4971 im_session_close_all(sip
);
4974 do_register_exp(sip
, 0);
4976 sipe_connection_cleanup(sip
);
4977 g_free(sip
->sipdomain
);
4978 g_free(sip
->username
);
4979 g_free(sip
->password
);
4980 g_free(sip
->authdomain
);
4981 g_free(sip
->authuser
);
4982 g_free(sip
->status
);
4984 g_hash_table_foreach_remove(sip
->buddies
, (GHRFunc
) sipe_buddy_remove
, NULL
);
4985 g_hash_table_destroy(sip
->buddies
);
4987 g_free(gc
->proto_data
);
4988 gc
->proto_data
= NULL
;
4991 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
4993 PurpleAccount
*acct
= purple_connection_get_account(gc
);
4994 char *id
= g_strdup_printf("sip:%s", (char *)g_list_nth_data(row
, 0));
4995 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
4997 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
4998 purple_conversation_present(conv
);
5002 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
5005 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
5006 g_list_nth_data(row
, 0), NULL
, g_list_nth_data(row
, 1));
5009 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,struct transaction
*tc
)
5011 PurpleNotifySearchResults
*results
;
5012 PurpleNotifySearchColumn
*column
;
5013 xmlnode
*searchResults
;
5015 int match_count
= 0;
5016 gboolean more
= FALSE
;
5019 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
5021 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5022 if (!searchResults
) {
5023 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
5027 results
= purple_notify_searchresults_new();
5029 if (results
== NULL
) {
5030 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
5031 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
5033 xmlnode_free(searchResults
);
5037 column
= purple_notify_searchresults_column_new(_("User Name"));
5038 purple_notify_searchresults_column_add(results
, column
);
5040 column
= purple_notify_searchresults_column_new(_("Name"));
5041 purple_notify_searchresults_column_add(results
, column
);
5043 column
= purple_notify_searchresults_column_new(_("Company"));
5044 purple_notify_searchresults_column_add(results
, column
);
5046 column
= purple_notify_searchresults_column_new(_("Country"));
5047 purple_notify_searchresults_column_add(results
, column
);
5049 column
= purple_notify_searchresults_column_new(_("Email"));
5050 purple_notify_searchresults_column_add(results
, column
);
5052 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
5055 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
5056 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
5057 g_strfreev(uri_parts
);
5059 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
5060 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
5061 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
5062 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
5064 purple_notify_searchresults_row_add(results
, row
);
5068 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
5069 char *data
= xmlnode_get_data_unescaped(mrow
);
5070 more
= (g_strcasecmp(data
, "true") == 0);
5074 secondary
= g_strdup_printf(
5075 dngettext(GETTEXT_PACKAGE
,
5076 "Found %d contact%s:",
5077 "Found %d contacts%s:", match_count
),
5078 match_count
, more
? _(" (more matched your query)") : "");
5080 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
5081 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
5082 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
5085 xmlnode_free(searchResults
);
5089 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
5091 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
5092 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
5096 PurpleRequestField
*field
= entries
->data
;
5097 const char *id
= purple_request_field_get_id(field
);
5098 const char *value
= purple_request_field_string_get_value(field
);
5100 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
5102 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
5103 } while ((entries
= g_list_next(entries
)) != NULL
);
5107 gchar
*query
= g_strjoinv(NULL
, attrs
);
5108 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
5109 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
5110 send_soap_request_with_cb(gc
->proto_data
, body
,
5111 (TransCallback
) process_search_contact_response
, NULL
);
5119 static void sipe_show_find_contact(PurplePluginAction
*action
)
5121 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
5122 PurpleRequestFields
*fields
;
5123 PurpleRequestFieldGroup
*group
;
5124 PurpleRequestField
*field
;
5126 fields
= purple_request_fields_new();
5127 group
= purple_request_field_group_new(NULL
);
5128 purple_request_fields_add_group(fields
, group
);
5130 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
5131 purple_request_field_group_add_field(group
, field
);
5132 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
5133 purple_request_field_group_add_field(group
, field
);
5134 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
5135 purple_request_field_group_add_field(group
, field
);
5136 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
5137 purple_request_field_group_add_field(group
, field
);
5139 purple_request_fields(gc
,
5141 _("Search for a Contact"),
5142 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
5144 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
5146 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
5149 GList
*sipe_actions(PurplePlugin
*plugin
, gpointer context
)
5152 PurplePluginAction
*act
;
5154 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
5155 menu
= g_list_prepend(menu
, act
);
5157 menu
= g_list_reverse(menu
);
5162 static void dummy_permit_deny(PurpleConnection
*gc
)
5166 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
5172 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
5178 static char *sipe_status_text(PurpleBuddy
*buddy
)
5180 struct sipe_account_data
*sip
;
5181 struct sipe_buddy
*sbuddy
;
5184 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
5185 if (sip
) //happens on pidgin exit
5187 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
5188 if (sbuddy
&& sbuddy
->annotation
)
5190 text
= g_strdup(sbuddy
->annotation
);
5197 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
5199 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
5200 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
5201 struct sipe_account_data
*sip
;
5202 struct sipe_buddy
*sbuddy
;
5203 char *annotation
= NULL
;
5205 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
5206 if (sip
) //happens on pidgin exit
5208 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
5211 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
5216 if (purple_presence_is_online(presence
))
5218 purple_notify_user_info_add_pair(user_info
, _("Status"), purple_status_get_name(status
));
5223 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
5230 sipe_get_account_text_table(PurpleAccount
*account
)
5233 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
5234 g_hash_table_insert(table
, "login_label", (gpointer
)_("Sign-In Name..."));
5238 static PurpleBuddy
*
5239 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
5242 const gchar
*server_alias
, *email
;
5243 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
5245 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
5247 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
5249 server_alias
= g_strdup(purple_buddy_get_server_alias(buddy
));
5251 purple_blist_server_alias_buddy(clone
, server_alias
);
5254 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
5256 purple_blist_node_set_string((PurpleBlistNode
*)clone
, "email", email
);
5259 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
5261 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
5266 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
5268 PurpleBuddy
*buddy
, *b
;
5269 PurpleConnection
*gc
;
5270 PurpleGroup
* group
= purple_find_group(group_name
);
5272 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
5274 buddy
= (PurpleBuddy
*)node
;
5276 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
5277 gc
= purple_account_get_connection(buddy
->account
);
5279 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
5281 b
= purple_blist_add_buddy_clone(group
, buddy
);
5284 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
5288 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
5291 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
5293 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
5296 char *mailto
= g_strdup_printf("mailto:%s", email
);
5297 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
5301 char *const parmList
[] = {mailto
, NULL
};
5302 if ((pid
= fork()) == -1)
5304 purple_debug_info("sipe", "fork() error\n");
5308 execvp("xdg-email", parmList
);
5309 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
5317 //@TODO resolve env variable %WINDIR% first
5318 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
5321 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
5330 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
5335 * A menu which appear when right-clicking on buddy in contact list.
5338 sipe_buddy_menu(PurpleBuddy
*buddy
)
5340 PurpleBlistNode
*g_node
;
5341 PurpleGroup
*group
, *gr_parent
;
5342 PurpleMenuAction
*act
;
5344 GList
*menu_groups
= NULL
;
5346 act
= purple_menu_action_new(_("Send Email..."),
5347 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
5349 menu
= g_list_prepend(menu
, act
);
5351 gr_parent
= purple_buddy_get_group(buddy
);
5352 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
5353 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
5356 group
= (PurpleGroup
*)g_node
;
5357 if (group
== gr_parent
)
5360 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
5363 act
= purple_menu_action_new(purple_group_get_name(group
),
5364 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
5366 menu_groups
= g_list_prepend(menu_groups
, act
);
5368 menu_groups
= g_list_reverse(menu_groups
);
5370 act
= purple_menu_action_new(_("Copy to"),
5373 menu
= g_list_prepend(menu
, act
);
5374 menu
= g_list_reverse(menu
);
5379 GList
*sipe_blist_node_menu(PurpleBlistNode
*node
) {
5380 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
5381 return sipe_buddy_menu((PurpleBuddy
*) node
);
5388 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
5390 gboolean ret
= TRUE
;
5391 char *username
= (char *)trans
->payload
;
5393 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
5394 PurpleBuddy
*pbuddy
;
5395 struct sipe_buddy
*sbuddy
;
5397 char *server_alias
= NULL
;
5399 const char *device_name
= NULL
;
5401 purple_debug_info("sipe", "Fetching %s's user info for %s\n", username
, sip
->username
);
5403 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, username
);
5404 alias
= purple_buddy_get_local_alias(pbuddy
);
5408 //will query buddy UA's capabilities and send answer to log
5409 sipe_options_request(sip
, username
);
5411 sbuddy
= g_hash_table_lookup(sip
->buddies
, username
);
5414 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
5418 if (msg
->response
!= 200) {
5419 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
5421 xmlnode
*searchResults
;
5424 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
5425 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5426 if (!searchResults
) {
5427 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
5428 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
5429 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
5430 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
5431 purple_notify_user_info_add_pair(info
, _("Job Title"), g_strdup(xmlnode_get_attrib(mrow
, "title")));
5432 purple_notify_user_info_add_pair(info
, _("Office"), g_strdup(xmlnode_get_attrib(mrow
, "office")));
5433 purple_notify_user_info_add_pair(info
, _("Business Phone"), g_strdup(xmlnode_get_attrib(mrow
, "phone")));
5434 purple_notify_user_info_add_pair(info
, _("Company"), g_strdup(xmlnode_get_attrib(mrow
, "company")));
5435 purple_notify_user_info_add_pair(info
, _("City"), g_strdup(xmlnode_get_attrib(mrow
, "city")));
5436 purple_notify_user_info_add_pair(info
, _("State"), g_strdup(xmlnode_get_attrib(mrow
, "state")));
5437 purple_notify_user_info_add_pair(info
, _("Country"), g_strdup(xmlnode_get_attrib(mrow
, "country")));
5438 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
5439 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
5440 if (!email
|| strcmp("", email
)) {
5441 if (!purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email")) {
5442 purple_blist_node_set_string((PurpleBlistNode
*)pbuddy
, "email", email
);
5446 xmlnode_free(searchResults
);
5449 purple_notify_user_info_add_section_break(info
);
5451 if (!server_alias
|| !strcmp("", server_alias
)) {
5452 g_free(server_alias
);
5453 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
5455 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
5459 // same as server alias, do not present
5460 alias
= (alias
&& server_alias
&& !strcmp(alias
, server_alias
)) ? NULL
: alias
;
5463 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
5466 if (!email
|| !strcmp("", email
)) {
5468 email
= g_strdup(purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email"));
5470 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
5476 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
5479 /* show a buddy's user info in a nice dialog box */
5480 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
5481 username
, /* buddy's username */
5483 NULL
, /* callback called when dialog closed */
5484 NULL
); /* userdata for callback */
5490 * AD search first, LDAP based
5492 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
5494 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
5495 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
5497 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
5498 send_soap_request_with_cb((struct sipe_account_data
*)gc
->proto_data
, body
,
5499 (TransCallback
) process_get_info_response
, (gpointer
)g_strdup(username
));
5504 static PurplePlugin
*my_protocol
= NULL
;
5506 static PurplePluginProtocolInfo prpl_info
=
5509 NULL
, /* user_splits */
5510 NULL
, /* protocol_options */
5511 NO_BUDDY_ICONS
, /* icon_spec */
5512 sipe_list_icon
, /* list_icon */
5513 NULL
, /* list_emblems */
5514 sipe_status_text
, /* status_text */
5515 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
5516 sipe_status_types
, /* away_states */
5517 sipe_blist_node_menu
, /* blist_node_menu */
5518 NULL
, /* chat_info */
5519 NULL
, /* chat_info_defaults */
5520 sipe_login
, /* login */
5521 sipe_close
, /* close */
5522 sipe_im_send
, /* send_im */
5523 NULL
, /* set_info */ // TODO maybe
5524 sipe_send_typing
, /* send_typing */
5525 sipe_get_info
, /* get_info */
5526 sipe_set_status
, /* set_status */
5527 NULL
, /* set_idle */
5528 NULL
, /* change_passwd */
5529 sipe_add_buddy
, /* add_buddy */
5530 NULL
, /* add_buddies */
5531 sipe_remove_buddy
, /* remove_buddy */
5532 NULL
, /* remove_buddies */
5533 sipe_add_permit
, /* add_permit */
5534 sipe_add_deny
, /* add_deny */
5535 sipe_add_deny
, /* rem_permit */
5536 sipe_add_permit
, /* rem_deny */
5537 dummy_permit_deny
, /* set_permit_deny */
5538 NULL
, /* join_chat */
5539 NULL
, /* reject_chat */
5540 NULL
, /* get_chat_name */
5541 NULL
, /* chat_invite */
5542 NULL
, /* chat_leave */
5543 NULL
, /* chat_whisper */
5544 NULL
, /* chat_send */
5545 sipe_keep_alive
, /* keepalive */
5546 NULL
, /* register_user */
5547 NULL
, /* get_cb_info */ // deprecated
5548 NULL
, /* get_cb_away */ // deprecated
5549 sipe_alias_buddy
, /* alias_buddy */
5550 sipe_group_buddy
, /* group_buddy */
5551 sipe_rename_group
, /* rename_group */
5552 NULL
, /* buddy_free */
5553 sipe_convo_closed
, /* convo_closed */
5554 purple_normalize_nocase
, /* normalize */
5555 NULL
, /* set_buddy_icon */
5556 sipe_remove_group
, /* remove_group */
5557 NULL
, /* get_cb_real_name */ // TODO?
5558 NULL
, /* set_chat_topic */
5559 NULL
, /* find_blist_chat */
5560 NULL
, /* roomlist_get_list */
5561 NULL
, /* roomlist_cancel */
5562 NULL
, /* roomlist_expand_category */
5563 NULL
, /* can_receive_file */
5564 NULL
, /* send_file */
5565 NULL
, /* new_xfer */
5566 NULL
, /* offline_message */
5567 NULL
, /* whiteboard_prpl_ops */
5568 sipe_send_raw
, /* send_raw */
5569 NULL
, /* roomlist_room_serialize */
5570 NULL
, /* unregister_user */
5571 NULL
, /* send_attention */
5572 NULL
, /* get_attention_types */
5574 sizeof(PurplePluginProtocolInfo
), /* struct_size */
5575 sipe_get_account_text_table
, /* get_account_text_table */
5579 static PurplePluginInfo info
= {
5580 PURPLE_PLUGIN_MAGIC
,
5581 PURPLE_MAJOR_VERSION
,
5582 PURPLE_MINOR_VERSION
,
5583 PURPLE_PLUGIN_PROTOCOL
, /**< type */
5584 NULL
, /**< ui_requirement */
5586 NULL
, /**< dependencies */
5587 PURPLE_PRIORITY_DEFAULT
, /**< priority */
5588 "prpl-sipe", /**< id */
5589 "Microsoft LCS/OCS", /**< name */
5590 VERSION
, /**< version */
5591 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
5592 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
5593 "Anibal Avelar <avelar@gmail.com>, " /**< author */
5594 "Gabriel Burt <gburt@novell.com>", /**< author */
5595 PURPLE_WEBSITE
, /**< homepage */
5596 sipe_plugin_load
, /**< load */
5597 sipe_plugin_unload
, /**< unload */
5598 sipe_plugin_destroy
, /**< destroy */
5599 NULL
, /**< ui_info */
5600 &prpl_info
, /**< extra_info */
5609 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
5613 entry
= prpl_info
.protocol_options
;
5615 purple_account_option_destroy(entry
->data
);
5616 entry
= g_list_delete_link(entry
, entry
);
5618 prpl_info
.protocol_options
= NULL
;
5620 entry
= prpl_info
.user_splits
;
5622 purple_account_user_split_destroy(entry
->data
);
5623 entry
= g_list_delete_link(entry
, entry
);
5625 prpl_info
.user_splits
= NULL
;
5628 static void init_plugin(PurplePlugin
*plugin
)
5630 PurpleAccountUserSplit
*split
;
5631 PurpleAccountOption
*option
;
5634 purple_debug_info(PACKAGE
, "bindtextdomain = %s", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
5635 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s",
5636 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
5639 purple_plugin_register(plugin
);
5641 split
= purple_account_user_split_new(_("Login \n domain\\user or\n someone@linux.com "), NULL
, ',');
5642 purple_account_user_split_set_reverse(split
, FALSE
);
5643 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
5645 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
5646 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5647 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
5648 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5650 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
5651 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5652 // Translators: noun (networking port)
5653 option
= purple_account_option_int_new(_("Port"), "port", 5061);
5654 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5656 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
5657 purple_account_option_add_list_item(option
, _("Auto"), "auto");
5658 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
5659 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
5660 purple_account_option_add_list_item(option
, _("UDP"), "udp");
5661 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5663 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
5664 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
5666 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
5667 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5669 // TODO commented out so won't show in the preferences until we fix krb message signing
5670 /*option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
5671 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5673 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
5674 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
5675 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5678 my_protocol
= plugin
;
5681 /* I had to redefined the function for it load, but works */
5682 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
5683 plugin
->info
= &(info
);
5684 init_plugin((plugin
));
5685 sipe_plugin_load((plugin
));
5686 return purple_plugin_register(plugin
);