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 /* Action name templates */
110 #define ACTION_NAME_PRESENCE "<presence><%s>"
112 static char *gentag()
114 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
117 static gchar
*get_epid(struct sipe_account_data
*sip
)
120 sip
->epid
= sipe_uuid_get_macaddr(purple_network_get_my_ip(-1));
122 return g_strdup(sip
->epid
);
125 static char *genbranch()
127 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
128 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
129 rand() & 0xFFFF, rand() & 0xFFFF);
132 static char *gencallid()
134 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
135 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
136 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
137 rand() & 0xFFFF, rand() & 0xFFFF);
140 static gchar
*find_tag(const gchar
*hdr
)
142 gchar
* tag
= sipmsg_find_part_of_header (hdr
, "tag=", ";", NULL
);
144 // In case it's at the end and there's no trailing ;
145 tag
= sipmsg_find_part_of_header (hdr
, "tag=", NULL
, NULL
);
151 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
156 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
158 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
160 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
161 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
164 static void sipe_close(PurpleConnection
*gc
);
166 static void send_presence_status(struct sipe_account_data
*sip
);
168 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
170 static void sipe_keep_alive(PurpleConnection
*gc
)
172 struct sipe_account_data
*sip
= gc
->proto_data
;
173 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
174 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
175 gchar buf
[2] = {0, 0};
176 purple_debug_info("sipe", "sending keep alive\n");
177 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
179 time_t now
= time(NULL
);
180 if ((sip
->keepalive_timeout
> 0) &&
181 ((now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
182 #if PURPLE_VERSION_CHECK(2,4,0)
183 && ((now
- gc
->last_received
) >= sip
->keepalive_timeout
)
186 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
187 sendout_pkt(gc
, "\r\n\r\n");
188 sip
->last_keepalive
= now
;
193 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
195 struct sip_connection
*ret
= NULL
;
196 GSList
*entry
= sip
->openconns
;
199 if (ret
->fd
== fd
) return ret
;
205 static void sipe_auth_free(struct sip_auth
*auth
)
209 g_free(auth
->opaque
);
213 g_free(auth
->target
);
215 g_free(auth
->digest_session_key
);
216 auth
->digest_session_key
= NULL
;
217 g_free(auth
->ntlm_key
);
218 auth
->ntlm_key
= NULL
;
219 auth
->type
= AUTH_TYPE_UNSET
;
224 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
226 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
228 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
232 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
234 struct sip_connection
*conn
= connection_find(sip
, fd
);
236 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
237 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
243 static void connection_free_all(struct sipe_account_data
*sip
)
245 struct sip_connection
*ret
= NULL
;
246 GSList
*entry
= sip
->openconns
;
249 connection_remove(sip
, ret
->fd
);
250 entry
= sip
->openconns
;
254 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
256 const gchar
*method
= msg
->method
;
257 const gchar
*target
= msg
->target
;
262 const char *authdomain
= sip
->authdomain
;
263 const char *authuser
= sip
->authuser
;
264 //const char *krb5_realm;
266 //gchar *krb5_token = NULL;
268 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
269 // and do error checking
271 // KRB realm should always be uppercase
272 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
274 if (sip
->realhostname
) {
275 host
= sip
->realhostname
;
276 } else if (purple_account_get_bool(sip
->account
, "useproxy", TRUE
)) {
277 host
= purple_account_get_string(sip
->account
, "proxy", "");
279 host
= sip
->sipdomain
;
282 /*gboolean new_auth = krb5_auth.gss_context == NULL;
284 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
287 if (new_auth || force_reauth) {
288 krb5_token = krb5_auth.base64_token;
291 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
292 krb5_token = krb5_auth.base64_token;*/
298 if (!authuser
|| strlen(authuser
) < 1) {
299 authuser
= sip
->username
;
302 if (auth
->type
== AUTH_TYPE_DIGEST
) { /* Digest */
303 sprintf(noncecount
, "%08d", auth
->nc
++);
304 response
= purple_cipher_http_digest_calculate_response(
305 "md5", method
, target
, NULL
, NULL
,
306 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
307 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
309 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
);
312 } else if (auth
->type
== AUTH_TYPE_NTLM
) { /* NTLM */
313 // If we have a signature for the message, include that
314 if (msg
->signature
) {
315 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
);
319 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
320 const gchar
*ntlm_key
;
322 #if GLIB_CHECK_VERSION(2,8,0)
323 const gchar
* hostname
= g_get_host_name();
325 static char hostname
[256];
326 int ret
= gethostname(hostname
, sizeof(hostname
));
327 hostname
[sizeof(hostname
) - 1] = '\0';
328 if (ret
== -1 || hostname
[0] == '\0') {
329 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Error when getting host name. Using \"localhost.\"\n");
331 strcpy(hostname
, "localhost");
334 /*const gchar * hostname = purple_get_host_name();*/
336 gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, hostname
, authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
337 auth
->ntlm_key
= (gchar
*)ntlm_key
;
338 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
343 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
345 } else if (auth
->type
== AUTH_TYPE_KERBEROS
) {
348 /*if (new_auth || force_reauth) {
349 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
351 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);
353 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
356 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
357 gchar * mic = "MICTODO";
358 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
359 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
360 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
361 //auth->opaque ? auth->opaque : "", auth->target);
362 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
367 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
370 sprintf(noncecount
, "%08d", auth
->nc
++);
371 response
= purple_cipher_http_digest_calculate_response(
372 "md5", method
, target
, NULL
, NULL
,
373 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
374 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
376 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
);
381 static char *parse_attribute(const char *attrname
, const char *source
)
383 const char *tmp
, *tmp2
;
385 int len
= strlen(attrname
);
387 if (!strncmp(source
, attrname
, len
)) {
389 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
391 retval
= g_strndup(tmp
, tmp2
- tmp
);
393 retval
= g_strdup(tmp
);
399 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
402 const char *authuser
;
405 //const char *krb5_realm;
408 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
409 // and do error checking
411 // KRB realm should always be uppercase
412 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
414 if (sip->realhostname) {
415 host = sip->realhostname;
416 } else if (purple_account_get_bool(sip->account, "useproxy", TRUE)) {
417 host = purple_account_get_string(sip->account, "proxy", "");
419 host = sip->sipdomain;
422 authuser
= sip
->authuser
;
424 if (!authuser
|| strlen(authuser
) < 1) {
425 authuser
= sip
->username
;
429 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
433 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
434 auth
->type
= AUTH_TYPE_NTLM
;
435 parts
= g_strsplit(hdr
+5, "\", ", 0);
438 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
439 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
441 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
444 if ((tmp
= parse_attribute("targetname=\"",
446 g_free(auth
->target
);
449 else if ((tmp
= parse_attribute("realm=\"",
454 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
455 g_free(auth
->opaque
);
462 if (!strstr(hdr
, "gssapi-data")) {
470 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
471 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
472 auth
->type
= AUTH_TYPE_KERBEROS
;
473 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
474 parts
= g_strsplit(hdr
+9, "\", ", 0);
477 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
478 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
479 /*if (krb5_auth.gss_context == NULL) {
480 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
482 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
485 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
486 g_free(auth
->target
);
488 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
491 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
492 g_free(auth
->opaque
);
502 auth
->type
= AUTH_TYPE_DIGEST
;
503 parts
= g_strsplit(hdr
, " ", 0);
505 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
509 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
517 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
519 g_free(auth
->digest_session_key
);
520 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
521 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
527 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
529 PurpleConnection
*gc
= data
;
530 struct sipe_account_data
*sip
= gc
->proto_data
;
534 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
536 if (max_write
== 0) {
537 if (sip
->tx_handler
!= 0){
538 purple_input_remove(sip
->tx_handler
);
544 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
546 if (written
< 0 && errno
== EAGAIN
)
548 else if (written
<= 0) {
549 /*TODO: do we really want to disconnect on a failure to write?*/
550 purple_connection_error(gc
, _("Could not write"));
554 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
557 static void sipe_canwrite_cb_ssl(gpointer data
, gint src
, PurpleInputCondition cond
)
559 PurpleConnection
*gc
= data
;
560 struct sipe_account_data
*sip
= gc
->proto_data
;
564 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
566 if (max_write
== 0) {
567 if (sip
->tx_handler
!= 0) {
568 purple_input_remove(sip
->tx_handler
);
574 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
576 if (written
< 0 && errno
== EAGAIN
)
578 else if (written
<= 0) {
579 /*TODO: do we really want to disconnect on a failure to write?*/
580 purple_connection_error(gc
, _("Could not write"));
584 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
587 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
589 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
591 PurpleConnection
*gc
= data
;
592 struct sipe_account_data
*sip
;
593 struct sip_connection
*conn
;
595 if (!PURPLE_CONNECTION_IS_VALID(gc
))
603 purple_connection_error(gc
, _("Could not connect"));
607 sip
= gc
->proto_data
;
609 sip
->connecting
= FALSE
;
610 sip
->last_keepalive
= time(NULL
);
612 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
614 /* If there is more to write now, we need to register a handler */
615 if (sip
->txbuf
->bufused
> 0)
616 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
618 conn
= connection_create(sip
, source
);
619 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
622 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
624 struct sipe_account_data
*sip
;
625 struct sip_connection
*conn
;
627 if (!PURPLE_CONNECTION_IS_VALID(gc
))
629 if (gsc
) purple_ssl_close(gsc
);
633 sip
= gc
->proto_data
;
636 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
637 sip
->connecting
= FALSE
;
638 sip
->last_keepalive
= time(NULL
);
640 conn
= connection_create(sip
, gsc
->fd
);
642 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
647 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
649 PurpleConnection
*gc
= data
;
650 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
651 if (sip
== NULL
) return;
653 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
655 /* If there is more to write now */
656 if (sip
->txbuf
->bufused
> 0) {
657 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
662 static void sendlater(PurpleConnection
*gc
, const char *buf
)
664 struct sipe_account_data
*sip
= gc
->proto_data
;
666 if (!sip
->connecting
) {
667 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
668 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
669 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
671 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
672 purple_connection_error(gc
, _("Couldn't create socket"));
675 sip
->connecting
= TRUE
;
678 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
679 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
681 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
684 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
686 struct sipe_account_data
*sip
= gc
->proto_data
;
687 time_t currtime
= time(NULL
);
688 int writelen
= strlen(buf
);
690 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
691 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
692 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
693 purple_debug_info("sipe", "could not send packet\n");
702 if (sip
->tx_handler
) {
707 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
709 ret
= write(sip
->fd
, buf
, writelen
);
713 if (ret
< 0 && errno
== EAGAIN
)
715 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
720 if (ret
< writelen
) {
721 if (!sip
->tx_handler
){
723 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
726 sip
->tx_handler
= purple_input_add(sip
->fd
,
727 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
732 /* XXX: is it OK to do this? You might get part of a request sent
733 with part of another. */
734 if (sip
->txbuf
->bufused
> 0)
735 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
737 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
743 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
745 sendout_pkt(gc
, buf
);
749 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
751 GSList
*tmp
= msg
->headers
;
754 GString
*outstr
= g_string_new("");
755 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
757 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
758 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
759 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
760 tmp
= g_slist_next(tmp
);
762 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
763 sendout_pkt(sip
->gc
, outstr
->str
);
764 g_string_free(outstr
, TRUE
);
767 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
770 if (sip
->registrar
.ntlm_key
) {
771 struct sipmsg_breakdown msgbd
;
772 gchar
*signature_input_str
;
774 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
775 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
776 sip
->registrar
.ntlm_num
++;
777 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
778 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
779 if (signature_input_str
!= NULL
) {
780 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
781 msg
->rand
= g_strdup(msgbd
.rand
);
782 msg
->num
= g_strdup(msgbd
.num
);
783 g_free(signature_input_str
);
785 sipmsg_breakdown_free(&msgbd
);
788 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
789 buf
= auth_header(sip
, &sip
->registrar
, msg
);
790 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
791 sipmsg_add_header(msg
, "Authorization", buf
);
793 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
796 } 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")) {
797 sip
->registrar
.nc
= 3;
798 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
800 buf
= auth_header(sip
, &sip
->registrar
, msg
);
801 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
804 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
808 static char *get_contact(struct sipe_account_data
*sip
)
810 return g_strdup(sip
->contact
);
815 static char *get_contact_service(struct sipe_account_data *sip)
817 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()));
818 //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);
822 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
823 const char *text
, const char *body
)
827 GString
*outstr
= g_string_new("");
828 struct sipe_account_data
*sip
= gc
->proto_data
;
832 sipmsg_remove_header(msg
, "ms-user-data");
834 contact
= get_contact(sip
);
835 sipmsg_remove_header(msg
, "Contact");
836 sipmsg_add_header(msg
, "Contact", contact
);
839 /* When sending the acknowlegements and errors, the content length from the original
840 message is still here, but there is no body; we need to make sure we're sending the
841 correct content length */
842 sipmsg_remove_header(msg
, "Content-Length");
845 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
846 sipmsg_add_header(msg
, "Content-Length", len
);
848 sipmsg_remove_header(msg
, "Content-Type");
849 sipmsg_add_header(msg
, "Content-Length", "0");
852 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
853 //gchar * mic = "MICTODO";
854 msg
->response
= code
;
856 sipmsg_remove_header(msg
, "Authentication-Info");
857 sign_outgoing_message(msg
, sip
, msg
->method
);
859 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
862 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
863 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
865 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
866 tmp
= g_slist_next(tmp
);
868 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
869 sendout_pkt(gc
, outstr
->str
);
870 g_string_free(outstr
, TRUE
);
873 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
875 if (trans
->msg
) sipmsg_free(trans
->msg
);
876 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
880 static struct transaction
*
881 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
883 struct transaction
*trans
= g_new0(struct transaction
, 1);
884 trans
->time
= time(NULL
);
885 trans
->msg
= (struct sipmsg
*)msg
;
886 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
887 trans
->callback
= callback
;
888 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
892 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
894 struct transaction
*trans
;
895 GSList
*transactions
= sip
->transactions
;
896 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
898 while (transactions
) {
899 trans
= transactions
->data
;
900 if (!strcmp(trans
->cseq
, cseq
)) {
903 transactions
= transactions
->next
;
909 static struct transaction
*
910 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
911 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
912 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
914 struct sipe_account_data
*sip
= gc
->proto_data
;
915 const char *addh
= "";
918 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
919 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
920 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
921 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
922 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
923 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
924 gchar
*route
= strdup("");
925 gchar
*epid
= get_epid(sip
); // TODO generate one per account/login
926 struct transaction
*trans
;
928 if (dialog
&& dialog
->routes
)
930 GSList
*iter
= dialog
->routes
;
935 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
937 iter
= g_slist_next(iter
);
941 if (!ourtag
&& !dialog
) {
945 if (!strcmp(method
, "REGISTER")) {
946 if (sip
->regcallid
) {
948 callid
= g_strdup(sip
->regcallid
);
950 sip
->regcallid
= g_strdup(callid
);
954 if (addheaders
) addh
= addheaders
;
956 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
957 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
958 "From: <sip:%s>%s%s;epid=%s\r\n"
959 "To: <%s>%s%s%s%s\r\n"
960 "Max-Forwards: 70\r\n"
965 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
967 dialog
&& dialog
->request
? dialog
->request
: url
,
968 TRANSPORT_DESCRIPTOR
,
969 purple_network_get_my_ip(-1),
971 branch
? ";branch=" : "",
972 branch
? branch
: "",
974 ourtag
? ";tag=" : "",
975 ourtag
? ourtag
: "",
978 theirtag
? ";tag=" : "",
979 theirtag
? theirtag
: "",
980 theirepid
? ";epid=" : "",
981 theirepid
? theirepid
: "",
982 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
988 body
? strlen(body
) : 0,
992 //printf ("parsing msg buf:\n%s\n\n", buf);
993 msg
= sipmsg_parse_msg(buf
);
1004 sign_outgoing_message (msg
, sip
, method
);
1006 buf
= sipmsg_to_string (msg
);
1008 /* add to ongoing transactions */
1009 trans
= transactions_add_buf(sip
, msg
, tc
);
1010 sendout_pkt(gc
, buf
);
1016 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
1018 gchar
*from
= g_strdup_printf("sip:%s", sip
->username
);
1019 gchar
*contact
= get_contact(sip
);
1020 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
1021 "Content-Type: application/SOAP+xml\r\n",contact
);
1023 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1024 tr
->payload
= payload
;
1031 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1033 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
1036 static char *get_contact_register(struct sipe_account_data
*sip
)
1038 char *epid
= get_epid(sip
);
1039 char *uuid
= generateUUIDfromEPID(epid
);
1040 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
);
1046 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1048 char *expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1049 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
1050 char *to
= g_strdup_printf("sip:%s", sip
->username
);
1051 char *contact
= get_contact_register(sip
);
1052 char *hdr
= g_strdup_printf("Contact: %s\r\n"
1053 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1054 "Event: registration\r\n"
1055 "Allow-Events: presence\r\n"
1056 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1057 "%s", contact
, expires
);
1061 sip
->registerstatus
= 1;
1063 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1064 process_register_response
);
1071 static void do_register_cb(struct sipe_account_data
*sip
, void *unused
)
1073 do_register_exp(sip
, -1);
1074 sip
->reregister_set
= FALSE
;
1077 static void do_register(struct sipe_account_data
*sip
)
1079 do_register_exp(sip
, -1);
1083 * Returns URI from provided To or From header.
1085 * Needs to g_free() after use.
1087 * @return URI with sip: prefix
1089 static gchar
*parse_from(const gchar
*hdr
)
1092 const gchar
*tmp
, *tmp2
= hdr
;
1094 if (!hdr
) return NULL
;
1095 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1096 tmp
= strchr(hdr
, '<');
1098 /* i hate the different SIP UA behaviours... */
1099 if (tmp
) { /* sip address in <...> */
1101 tmp
= strchr(tmp2
, '>');
1103 from
= g_strndup(tmp2
, tmp
- tmp2
);
1105 purple_debug_info("sipe", "found < without > in From\n");
1109 tmp
= strchr(tmp2
, ';');
1111 from
= g_strndup(tmp2
, tmp
- tmp2
);
1113 from
= g_strdup(tmp2
);
1116 purple_debug_info("sipe", "got %s\n", from
);
1120 static xmlnode
* xmlnode_get_descendant(xmlnode
* parent
, ...)
1123 xmlnode
* node
= NULL
;
1126 va_start(args
, parent
);
1127 while ((name
= va_arg(args
, const char *)) != NULL
) {
1128 node
= xmlnode_get_child(parent
, name
);
1129 if (node
== NULL
) return NULL
;
1139 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1141 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1142 send_soap_request(sip
, body
);
1147 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1150 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1152 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1155 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1159 void sipe_auth_user_cb(void * data
)
1161 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1164 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1169 void sipe_deny_user_cb(void * data
)
1171 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1174 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1179 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1181 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1182 sipe_contact_allow_deny(sip
, name
, TRUE
);
1186 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1188 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1189 sipe_contact_allow_deny(sip
, name
, FALSE
);
1193 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1195 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1196 sipe_contact_set_acl(sip, name, "");
1200 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1204 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1205 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1207 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1209 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1210 if (!watchers
) return;
1212 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1213 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1214 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1215 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1217 // TODO pull out optional displayName to pass as alias
1219 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1220 job
->who
= remote_user
;
1222 purple_account_request_authorization(
1236 xmlnode_free(watchers
);
1241 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1243 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1244 if (!purple_group
) {
1245 purple_group
= purple_group_new(group
->name
);
1246 purple_blist_add_group(purple_group
, NULL
);
1250 group
->purple_group
= purple_group
;
1251 sip
->groups
= g_slist_append(sip
->groups
, group
);
1252 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1254 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1258 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1260 struct sipe_group
*group
;
1266 entry
= sip
->groups
;
1268 group
= entry
->data
;
1269 if (group
->id
== id
) {
1272 entry
= entry
->next
;
1277 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, gchar
* name
)
1279 struct sipe_group
*group
;
1285 entry
= sip
->groups
;
1287 group
= entry
->data
;
1288 if (!strcmp(group
->name
, name
)) {
1291 entry
= entry
->next
;
1297 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1300 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1301 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1302 send_soap_request(sip
, body
);
1304 g_free(group
->name
);
1305 group
->name
= g_strdup(name
);
1309 * Only appends if no such value already stored.
1312 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1313 GSList
* res
= list
;
1314 if (!g_slist_find_custom(list
, data
, func
)) {
1315 res
= g_slist_insert_sorted(list
, data
, func
);
1321 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1322 return group1
->id
- group2
->id
;
1326 * Returns string like "2 4 7 8" - group ids buddy belong to.
1329 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1332 //creating array from GList, converting int to gchar*
1333 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1334 GSList
*entry
= buddy
->groups
;
1336 struct sipe_group
* group
= entry
->data
;
1337 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1338 entry
= entry
->next
;
1342 res
= g_strjoinv(" ", ids_arr
);
1343 g_strfreev(ids_arr
);
1348 * Sends buddy update to server
1351 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1353 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1354 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1356 if (buddy
&& purple_buddy
) {
1357 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1359 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1360 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1362 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1363 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1365 send_soap_request(sip
, body
);
1371 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1373 if (msg
->response
== 200) {
1374 struct sipe_group
*group
;
1375 struct group_user_context
*ctx
= (struct group_user_context
*)tc
->payload
;
1379 struct sipe_buddy
*buddy
;
1381 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1387 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1394 group_id
= xmlnode_get_data(node
);
1401 group
= g_new0(struct sipe_group
, 1);
1402 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1404 group
->name
= ctx
->group_name
;
1406 sipe_group_add(sip
, group
);
1408 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1410 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1413 sipe_group_set_user(sip
, ctx
->user_name
);
1422 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1424 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1426 ctx
->group_name
= g_strdup(name
);
1427 ctx
->user_name
= g_strdup(who
);
1429 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1430 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1435 * Data structure for scheduled actions
1437 typedef void (*Action
) (struct sipe_account_data
*, void *);
1439 struct scheduled_action
{
1442 * Format is <Event>[<Data>...]
1443 * Example: <presence><sip:user@domain.com> or <registration>
1446 guint timeout_handler
;
1447 gboolean repetitive
;
1449 struct sipe_account_data
*sip
;
1455 * Should return FALSE if repetitive action is not needed
1457 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1460 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1461 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1462 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1463 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1464 ret
= sched_action
->repetitive
;
1465 g_free(sched_action
->payload
);
1466 g_free(sched_action
->name
);
1467 g_free(sched_action
);
1472 * Kills action timer effectively cancelling
1475 * @param name of action
1477 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1481 if (!sip
->timeouts
|| !name
) return;
1483 entry
= sip
->timeouts
;
1485 struct scheduled_action
*sched_action
= entry
->data
;
1486 if(!strcmp(sched_action
->name
, name
)) {
1487 GSList
*to_delete
= entry
;
1488 entry
= entry
->next
;
1489 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1490 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1491 purple_timeout_remove(sched_action
->timeout_handler
);
1492 g_free(sched_action
->payload
);
1493 g_free(sched_action
->name
);
1494 g_free(sched_action
);
1496 entry
= entry
->next
;
1502 * Do schedule action for execution in the future.
1503 * Non repetitive execution.
1505 * @param name of action (will be copied)
1506 * @param timeout in seconds
1507 * @action callback function
1508 * @payload callback data (can be NULL, otherwise caller must allocate memory)
1510 static void sipe_schedule_action(const gchar
*name
, int timeout
, Action action
, struct sipe_account_data
*sip
, void *payload
)
1512 struct scheduled_action
*sched_action
;
1514 /* Make sure each action only exists once */
1515 sipe_cancel_scheduled_action(sip
, name
);
1517 purple_debug_info("sipe","scheduling action %s timeout:%d\n", name
, timeout
);
1518 sched_action
= g_new0(struct scheduled_action
, 1);
1519 sched_action
->repetitive
= FALSE
;
1520 sched_action
->name
= g_strdup(name
);
1521 sched_action
->action
= action
;
1522 sched_action
->sip
= sip
;
1523 sched_action
->payload
= payload
;
1524 sched_action
->timeout_handler
= purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1525 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1526 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1529 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1531 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1533 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1535 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1540 static void sipe_subscribe_resource_uri(const char *name
, gpointer value
, gchar
**resources_uri
)
1542 gchar
*tmp
= *resources_uri
;
1543 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1547 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1549 gchar
*tmp
= *resources_uri
;
1550 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1552 if(!sbuddy
->resubscribed
){ //Only not resubscribed contacts; the first time everybody are included
1553 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"><context/></resource>\n", tmp
, name
);
1560 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1561 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1562 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1563 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1564 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1567 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
, void *unused
)
1569 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1570 gchar
*contact
= get_contact(sip
);
1573 gchar
*resources_uri
= g_strdup("");
1574 gchar
*require
= "";
1576 gchar
*autoextend
= "";
1577 gchar
*content_type
;
1579 if (sip
->msrtc_event_categories
) {
1581 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1583 require
= ", categoryList";
1584 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1585 content_type
= "application/msrtc-adrl-categorylist+xml";
1586 content
= g_strdup_printf(
1587 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1588 "<action name=\"subscribe\" id=\"63792024\">\n"
1589 "<adhocList>\n%s</adhocList>\n"
1590 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1591 "<category name=\"note\"/>\n"
1592 "<category name=\"state\"/>\n"
1595 "</batchSub>", sip
->username
, resources_uri
);
1597 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1598 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1599 content_type
= "application/adrl+xml";
1600 content
= g_strdup_printf(
1601 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1602 "<create xmlns=\"\">\n%s</create>\n"
1603 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1605 g_free(resources_uri
);
1607 request
= g_strdup_printf(
1608 "Require: adhoclist%s\r\n"
1609 "Supported: eventlist\r\n"
1610 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1611 "Supported: ms-piggyback-first-notify\r\n"
1612 "%sSupported: ms-benotify\r\n"
1613 "Proxy-Require: ms-benotify\r\n"
1614 "Event: presence\r\n"
1615 "Content-Type: %s\r\n"
1616 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1619 /* subscribe to buddy presence */
1620 //send_sip_request(sip->gc, "SUBSCRIBE", resource_uri, resource_uri, request, content, NULL, process_subscribe_response);
1621 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1629 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1630 * The user sends a single SUBSCRIBE request to the subscribed contact.
1631 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1635 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
1637 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1638 gchar
*tmp
= get_contact(sip
);
1641 gchar
*autoextend
= "";
1643 if (!sip
->msrtc_event_categories
)
1644 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1646 request
= g_strdup_printf(
1647 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1648 "Supported: ms-piggyback-first-notify\r\n"
1649 "%sSupported: ms-benotify\r\n"
1650 "Proxy-Require: ms-benotify\r\n"
1651 "Event: presence\r\n"
1652 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1653 "Contact: %s\r\n", autoextend
,tmp
);
1655 content
= g_strdup_printf(
1656 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1657 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1658 "<resource uri=\"%s\"/>\n"
1660 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1661 "<category name=\"note\"/>\n"
1662 "<category name=\"state\"/>\n"
1665 "</batchSub>", sip
->username
, to
1670 /* subscribe to buddy presence */
1671 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1678 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1680 if (!purple_status_is_active(status
))
1684 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
1687 g_free(sip
->status
);
1688 sip
->status
= g_strdup(purple_status_get_id(status
));
1689 send_presence_status(sip
);
1695 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
)
1697 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1698 sipe_group_set_user(sip
, name
);
1702 sipe_group_buddy(PurpleConnection
*gc
,
1704 const char *old_group_name
,
1705 const char *new_group_name
)
1707 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1708 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1709 struct sipe_group
* old_group
= NULL
;
1710 struct sipe_group
* new_group
;
1712 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1713 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1715 if(!buddy
) { // buddy not in roaming list
1719 if (old_group_name
) {
1720 old_group
= sipe_group_find_by_name(sip
, g_strdup(old_group_name
));
1722 new_group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1725 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1726 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1730 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1732 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1733 sipe_group_set_user(sip
, who
);
1737 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1739 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1740 struct sipe_buddy
*b
;
1742 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1744 // Prepend sip: if needed
1745 if (strncmp("sip:", buddy
->name
, 4)) {
1746 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
1747 purple_blist_rename_buddy(buddy
, buf
);
1751 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1752 b
= g_new0(struct sipe_buddy
, 1);
1753 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1754 b
->name
= g_strdup(buddy
->name
);
1755 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1756 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1757 sipe_subscribe_presence_single(sip
, b
->name
); //@TODO should go to callback
1759 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1763 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1765 g_free(buddy
->name
);
1766 g_free(buddy
->annotation
);
1767 g_free(buddy
->device_name
);
1768 g_slist_free(buddy
->groups
);
1773 * Unassociates buddy from group first.
1774 * Then see if no groups left, removes buddy completely.
1775 * Otherwise updates buddy groups on server.
1777 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1779 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1780 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1781 struct sipe_group
*g
= NULL
;
1783 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1788 g
= sipe_group_find_by_name(sip
, group
->name
);
1792 b
->groups
= g_slist_remove(b
->groups
, g
);
1793 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
1796 if (g_slist_length(b
->groups
) < 1) {
1797 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
1798 sipe_cancel_scheduled_action(sip
, action_name
);
1799 g_free(action_name
);
1801 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1804 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1805 send_soap_request(sip
, body
);
1811 //updates groups on server
1812 sipe_group_set_user(sip
, b
->name
);
1818 sipe_rename_group(PurpleConnection
*gc
,
1819 const char *old_name
,
1821 GList
*moved_buddies
)
1823 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1824 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1826 sipe_group_rename(sip
, s_group
, group
->name
);
1828 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1833 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1835 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1836 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1839 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1840 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1841 send_soap_request(sip
, body
);
1844 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1845 g_free(s_group
->name
);
1848 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1852 static GList
*sipe_status_types(PurpleAccount
*acc
)
1854 PurpleStatusType
*type
;
1855 GList
*types
= NULL
;
1858 type
= purple_status_type_new_with_attrs(
1859 PURPLE_STATUS_AVAILABLE
, NULL
, _("Online"), TRUE
, TRUE
, FALSE
,
1860 // Translators: noun
1861 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1863 types
= g_list_append(types
, type
);
1866 type
= purple_status_type_new_with_attrs(
1867 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_BUSY
, _("Busy"), TRUE
, TRUE
, FALSE
,
1868 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1870 types
= g_list_append(types
, type
);
1872 // Do Not Disturb (not user settable)
1873 type
= purple_status_type_new_with_attrs(
1874 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_DND
, _("Do Not Disturb"), TRUE
, FALSE
, FALSE
,
1875 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1877 types
= g_list_append(types
, type
);
1880 type
= purple_status_type_new_with_attrs(
1881 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_BRB
, _("Be Right Back"), TRUE
, TRUE
, FALSE
,
1882 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1884 types
= g_list_append(types
, type
);
1887 type
= purple_status_type_new_with_attrs(
1888 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1889 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1891 types
= g_list_append(types
, type
);
1894 type
= purple_status_type_new_with_attrs(
1895 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_ONPHONE
, _("On The Phone"), TRUE
, TRUE
, FALSE
,
1896 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1898 types
= g_list_append(types
, type
);
1901 type
= purple_status_type_new_with_attrs(
1902 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_LUNCH
, _("Out To Lunch"), TRUE
, TRUE
, FALSE
,
1903 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1905 types
= g_list_append(types
, type
);
1908 type
= purple_status_type_new_full(
1909 PURPLE_STATUS_INVISIBLE
, NULL
, _("Appear Offline"), TRUE
, TRUE
, FALSE
);
1910 types
= g_list_append(types
, type
);
1913 type
= purple_status_type_new_full(
1914 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1915 types
= g_list_append(types
, type
);
1921 * A callback for g_hash_table_foreach
1923 static void sipe_buddy_subscribe_cb(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1925 sipe_subscribe_presence_single(sip
, buddy
->name
);
1929 * Removes entries from purple buddy list
1930 * that does not correspond ones in the roaming contact list.
1932 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
1933 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1934 GSList
*entry
= buddies
;
1935 struct sipe_buddy
*buddy
;
1939 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
1940 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
1943 g
= purple_buddy_get_group(b
);
1944 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
1946 gboolean in_sipe_groups
= FALSE
;
1947 GSList
*entry2
= buddy
->groups
;
1949 struct sipe_group
*group
= entry2
->data
;
1950 if (!strcmp(group
->name
, g
->name
)) {
1951 in_sipe_groups
= TRUE
;
1954 entry2
= entry2
->next
;
1956 if(!in_sipe_groups
) {
1957 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
1958 purple_blist_remove_buddy(b
);
1961 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
1962 purple_blist_remove_buddy(b
);
1964 entry
= entry
->next
;
1968 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1970 int len
= msg
->bodylen
;
1972 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1975 const gchar
*contacts_delta
;
1976 xmlnode
*group_node
;
1977 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1981 /* Convert the contact from XML to Purple Buddies */
1982 isc
= xmlnode_from_str(msg
->body
, len
);
1987 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
1988 if (contacts_delta
) {
1989 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1993 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1994 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1995 const char *name
= xmlnode_get_attrib(group_node
, "name");
1997 if (!strncmp(name
, "~", 1)) {
1999 name
= "Other Contacts";
2001 group
->name
= g_strdup(name
);
2002 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
2004 sipe_group_add(sip
, group
);
2007 // Make sure we have at least one group
2008 if (g_slist_length(sip
->groups
) == 0) {
2009 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2010 PurpleGroup
*purple_group
;
2012 group
->name
= g_strdup("Other Contacts");
2014 purple_group
= purple_group_new(group
->name
);
2015 purple_blist_add_group(purple_group
, NULL
);
2016 sip
->groups
= g_slist_append(sip
->groups
, group
);
2019 /* Parse contacts */
2020 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
2021 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
2022 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
2023 gchar
* groups
= g_strdup(xmlnode_get_attrib(item
, "groups"));
2024 gchar
* buddy_name
= g_strdup_printf("sip:%s", uri
);
2025 gchar
**item_groups
;
2026 struct sipe_group
*group
= NULL
;
2027 struct sipe_buddy
*buddy
= NULL
;
2030 // assign to group Other Contacts if nothing else received
2031 if(!groups
|| !strcmp("", groups
) ) {
2032 group
= sipe_group_find_by_name(sip
, "Other Contacts");
2033 groups
= group
? g_strdup_printf("%d", group
->id
) : "1";
2036 item_groups
= g_strsplit(groups
, " ", 0);
2038 while (item_groups
[i
]) {
2039 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2041 // If couldn't find the right group for this contact, just put them in the first group we have
2042 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2043 group
= sip
->groups
->data
;
2046 if (group
!= NULL
) {
2047 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2049 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2050 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2053 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
2054 if (name
!= NULL
&& strlen(name
) != 0) {
2055 purple_blist_alias_buddy(b
, name
);
2060 buddy
= g_new0(struct sipe_buddy
, 1);
2061 buddy
->name
= g_strdup(b
->name
);
2062 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2065 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2067 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2069 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2074 } // while, contact groups
2075 g_strfreev(item_groups
);
2085 sipe_cleanup_local_blist(sip
);
2087 //subscribe to buddies
2088 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2089 if(sip
->batched_support
){
2090 sipe_subscribe_presence_batched(sip
, NULL
);
2093 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2095 sip
->subscribed_buddies
= TRUE
;
2102 * Subscribe roaming contacts
2104 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2106 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2107 gchar
*tmp
= get_contact(sip
);
2108 gchar
*hdr
= g_strdup_printf(
2109 "Event: vnd-microsoft-roaming-contacts\r\n"
2110 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2111 "Supported: com.microsoft.autoextend\r\n"
2112 "Supported: ms-benotify\r\n"
2113 "Proxy-Require: ms-benotify\r\n"
2114 "Supported: ms-piggyback-first-notify\r\n"
2115 "Contact: %s\r\n", tmp
);
2118 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2123 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
, void *unused
)
2125 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2126 gchar
*tmp
= get_contact(sip
);
2127 gchar
*hdr
= g_strdup_printf(
2128 "Event: presence.wpending\r\n"
2129 "Accept: text/xml+msrtc.wpending\r\n"
2130 "Supported: com.microsoft.autoextend\r\n"
2131 "Supported: ms-benotify\r\n"
2132 "Proxy-Require: ms-benotify\r\n"
2133 "Supported: ms-piggyback-first-notify\r\n"
2134 "Contact: %s\r\n", tmp
);
2137 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2143 * Fires on deregistration event initiated by server.
2144 * [MS-SIPREGE] SIP extension.
2149 // Content-Type: text/registration-event
2150 // subscription-state: terminated;expires=0
2151 // ms-diagnostics-public: 4141;reason="User disabled"
2153 // deregistered;event=rejected
2155 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2157 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2158 gchar
*event
= NULL
;
2159 gchar
*reason
= NULL
;
2160 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2162 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2163 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2165 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2166 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2167 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2168 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2170 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2174 if (warning
!= NULL
) {
2175 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2176 } else { // for LCS2005
2178 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2179 error_id
= 4140; // [MS-SIPREGE]
2180 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2181 reason
= g_strdup(_("You have been signed off because you've signed in at another location"));
2182 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2184 reason
= g_strdup(_("User disabled")); // [MS-OCER]
2185 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2187 reason
= g_strdup(_("User moved")); // [MS-OCER]
2191 warning
= g_strdup_printf(_("Unregistered by Server: %s."), reason
? reason
: _("no reason given"));
2194 sip
->gc
->wants_to_die
= TRUE
;
2195 purple_connection_error(sip
->gc
, warning
);
2200 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2202 const gchar
*contacts_delta
;
2205 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2211 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2214 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2223 * When we receive some self (BE) NOTIFY with a new subscriber
2224 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2228 static void sipe_process_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2234 char *display_name
= NULL
;
2235 PurpleBuddy
*pbuddy
;
2240 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2242 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2245 contact
= get_contact(sip
);
2246 to
= g_strdup_printf("sip:%s", sip
->username
);
2248 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2250 const char *acknowledged
;
2254 user
= xmlnode_get_attrib(node
, "user");
2255 if (!user
) continue;
2256 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
2257 uri_user
= g_strdup_printf("sip:%s", user
);
2258 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri_user
);
2260 alias
= purple_buddy_get_local_alias(pbuddy
);
2261 uri_alias
= g_strdup_printf("sip:%s", alias
);
2262 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
2263 if (display_name
&& !g_ascii_strcasecmp(uri_user
, uri_alias
)) { // 'bad' alias
2264 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri_user
, display_name
);
2265 purple_blist_alias_buddy(pbuddy
, display_name
);
2271 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
2272 if(!g_ascii_strcasecmp(acknowledged
,"false")){
2273 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
2274 hdr
= g_strdup_printf(
2276 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2278 body
= g_strdup_printf(
2279 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2280 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2281 "</setSubscribers>", user
);
2283 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
2294 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2296 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2297 gchar
*tmp
= get_contact(sip
);
2298 gchar
*hdr
= g_strdup_printf(
2299 "Event: vnd-microsoft-roaming-ACL\r\n"
2300 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
2301 "Supported: com.microsoft.autoextend\r\n"
2302 "Supported: ms-benotify\r\n"
2303 "Proxy-Require: ms-benotify\r\n"
2304 "Supported: ms-piggyback-first-notify\r\n"
2305 "Contact: %s\r\n", tmp
);
2308 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2314 * To request for presence information about the user, access level settings that have already been configured by the user
2315 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
2316 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
2319 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2321 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2322 gchar
*tmp
= get_contact(sip
);
2323 gchar
*hdr
= g_strdup_printf(
2324 "Event: vnd-microsoft-roaming-self\r\n"
2325 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
2326 "Supported: ms-benotify\r\n"
2327 "Proxy-Require: ms-benotify\r\n"
2328 "Supported: ms-piggyback-first-notify\r\n"
2330 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
2332 gchar
*body
=g_strdup(
2333 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
2334 "<roaming type=\"categories\"/>"
2335 "<roaming type=\"containers\"/>"
2336 "<roaming type=\"subscribers\"/></roamingList>");
2339 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2348 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2350 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2351 gchar
*tmp
= get_contact(sip
);
2352 gchar
*hdr
= g_strdup_printf(
2353 "Event: vnd-microsoft-provisioning\r\n"
2354 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
2355 "Supported: com.microsoft.autoextend\r\n"
2356 "Supported: ms-benotify\r\n"
2357 "Proxy-Require: ms-benotify\r\n"
2358 "Supported: ms-piggyback-first-notify\r\n"
2360 "Contact: %s\r\n", tmp
);
2363 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
2368 /** Subscription for provisioning information to help with initial
2369 * configuration. This subscription is a one-time query (denoted by the Expires header,
2370 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
2371 * configuration, meeting policies, and policy settings that Communicator must enforce.
2372 * TODO: for what we need this information.
2375 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2377 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2378 gchar
*tmp
= get_contact(sip
);
2379 gchar
*hdr
= g_strdup_printf(
2380 "Event: vnd-microsoft-provisioning-v2\r\n"
2381 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
2382 "Supported: com.microsoft.autoextend\r\n"
2383 "Supported: ms-benotify\r\n"
2384 "Proxy-Require: ms-benotify\r\n"
2385 "Supported: ms-piggyback-first-notify\r\n"
2388 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
2389 gchar
*body
= g_strdup(
2390 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
2391 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
2392 "<provisioningGroup name=\"ucPolicy\"/>"
2393 "</provisioningGroupList>");
2396 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2402 /* IM Session (INVITE and MESSAGE methods) */
2404 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
2406 struct sip_im_session
*session
;
2408 if (sip
== NULL
|| who
== NULL
) {
2412 entry
= sip
->im_sessions
;
2414 session
= entry
->data
;
2415 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
2418 entry
= entry
->next
;
2423 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
2425 struct sip_im_session
*session
= find_im_session(sip
, who
);
2427 session
= g_new0(struct sip_im_session
, 1);
2428 session
->with
= g_strdup(who
);
2429 session
->unconfirmed_messages
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
2430 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
2435 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2437 struct sip_dialog
*dialog
= session
->dialog
;
2440 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
2443 entry
= dialog
->routes
;
2445 g_free(entry
->data
);
2446 entry
= g_slist_remove(entry
, entry
->data
);
2448 entry
= dialog
->supported
;
2450 g_free(entry
->data
);
2451 entry
= g_slist_remove(entry
, entry
->data
);
2453 g_free(dialog
->callid
);
2454 g_free(dialog
->ourtag
);
2455 g_free(dialog
->theirtag
);
2456 g_free(dialog
->theirepid
);
2457 g_free(dialog
->request
);
2459 g_free(session
->dialog
);
2461 entry
= session
->outgoing_message_queue
;
2463 g_free(entry
->data
);
2464 entry
= g_slist_remove(entry
, entry
->data
);
2467 g_hash_table_destroy(session
->unconfirmed_messages
);
2469 g_free(session
->with
);
2474 process_options_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2476 gboolean ret
= TRUE
;
2478 if (msg
->response
!= 200) {
2479 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
2483 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
2489 * Asks UA/proxy about its capabilities.
2491 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
2493 gchar
*to
= strstr(who
, "sip:") ? g_strdup(who
) : g_strdup_printf("sip:%s", who
);
2494 gchar
*contact
= get_contact(sip
);
2496 request
= g_strdup_printf(
2497 "Accept: application/sdp\r\n"
2498 "Contact: %s\r\n", contact
);
2502 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
2508 static void sipe_present_message_undelivered_err(gchar
*with
, struct sipe_account_data
*sip
, gchar
*message
)
2510 char *msg
, *msg_tmp
;
2511 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
2512 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2514 msg_tmp
= g_strdup_printf( _("The following message could not be delivered to all recipients, "\
2515 "possibly because one or more persons are offline:\n%s") ,
2517 purple_conv_present_error(with
, sip
->account
, msg_tmp
);
2522 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
);
2525 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2527 gboolean ret
= TRUE
;
2528 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
2529 struct sip_im_session
* session
= find_im_session(sip
, with
);
2530 struct sip_dialog
*dialog
;
2536 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2541 dialog
= session
->dialog
;
2543 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2548 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2549 key
= g_strdup_printf("<%s><%d><MESSAGE>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
));
2551 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2553 if (msg
->response
!= 200) {
2554 purple_debug_info("sipe", "process_message_response: MESSAGE response not 200\n");
2556 sipe_present_message_undelivered_err(with
, sip
, message
);
2557 im_session_destroy(sip
, session
);
2560 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2561 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
2562 key
, g_hash_table_size(session
->unconfirmed_messages
));
2568 sipe_im_process_queue(sip
, session
);
2572 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
2582 if (strncmp("sip:", session
->with
, 4)) {
2583 fullto
= g_strdup_printf("sip:%s", session
->with
);
2585 fullto
= g_strdup(session
->with
);
2588 sipe_parse_html(msg
, &msgformat
, &msgtext
);
2589 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
2591 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2594 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2597 msgr
= g_strdup("");
2600 tmp
= get_contact(sip
);
2601 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
2602 //hdr = g_strdup("Content-Type: text/rtf\r\n");
2603 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
2604 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n",
2609 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msgtext
, session
->dialog
, process_message_response
);
2617 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
2619 GSList
*entry
= session
->outgoing_message_queue
;
2621 if (session
->outgoing_invite
) return; //do not send messages until INVITE responded.
2624 char *key
= g_strdup_printf("<%s><%d><MESSAGE>", session
->dialog
->callid
, (session
->dialog
->cseq
) + 1);
2625 char *queued_msg
= entry
->data
;
2626 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
2627 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
2628 key
, g_hash_table_size(session
->unconfirmed_messages
));
2630 sipe_send_message(sip
, session
, queued_msg
);
2631 entry
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2637 sipe_get_route_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2639 GSList
*hdr
= msg
->headers
;
2640 struct siphdrelement
*elem
;
2646 if(!g_ascii_strcasecmp(elem
->name
, "Record-Route"))
2648 gchar
*route
= sipmsg_find_part_of_header(elem
->value
, "<", ">", NULL
);
2649 dialog
->routes
= g_slist_append(dialog
->routes
, route
);
2651 hdr
= g_slist_next(hdr
);
2656 dialog
->routes
= g_slist_reverse(dialog
->routes
);
2661 dialog
->request
= dialog
->routes
->data
;
2662 dialog
->routes
= g_slist_remove(dialog
->routes
, dialog
->routes
->data
);
2665 contact
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Contact"), "<", ">", NULL
);
2666 dialog
->routes
= g_slist_append(dialog
->routes
, contact
);
2670 sipe_get_supported_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2672 GSList
*hdr
= msg
->headers
;
2673 struct siphdrelement
*elem
;
2677 if(!g_ascii_strcasecmp(elem
->name
, "Supported")
2678 && !g_slist_find_custom(dialog
->supported
, elem
->value
, (GCompareFunc
)g_ascii_strcasecmp
))
2680 dialog
->supported
= g_slist_append(dialog
->supported
, g_strdup(elem
->value
));
2683 hdr
= g_slist_next(hdr
);
2688 sipe_parse_dialog(struct sipmsg
* msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2690 gchar
*us
= outgoing
? "From" : "To";
2691 gchar
*them
= outgoing
? "To" : "From";
2693 g_free(dialog
->callid
);
2694 g_free(dialog
->ourtag
);
2695 g_free(dialog
->theirtag
);
2697 dialog
->callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
2698 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, us
));
2699 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, them
));
2700 if (!dialog
->theirepid
) {
2701 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", ";", NULL
);
2702 if (!dialog
->theirepid
) {
2703 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", NULL
, NULL
);
2707 sipe_get_route_header(msg
, dialog
, outgoing
);
2708 sipe_get_supported_header(msg
, dialog
, outgoing
);
2713 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2715 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2716 struct sip_im_session
* session
= find_im_session(sip
, with
);
2717 struct sip_dialog
*dialog
;
2723 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2728 dialog
= session
->dialog
;
2730 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2735 sipe_parse_dialog(msg
, dialog
, TRUE
);
2737 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2738 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
2740 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2742 if (msg
->response
!= 200) {
2743 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2745 sipe_present_message_undelivered_err(with
, sip
, message
);
2746 im_session_destroy(sip
, session
);
2752 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
2753 session
->outgoing_invite
= NULL
;
2754 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
2755 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
2756 if (session
->outgoing_message_queue
) {
2757 char *queued_msg
= session
->outgoing_message_queue
->data
;
2758 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2763 sipe_im_process_queue(sip
, session
);
2765 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2766 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
2767 key
, g_hash_table_size(session
->unconfirmed_messages
));
2775 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
*session
, const gchar
*msg_body
)
2785 char *ms_text_format
;
2790 if (session
->dialog
) {
2791 purple_debug_info("sipe", "session with %s already has a dialog open\n", session
->with
);
2795 session
->dialog
= g_new0(struct sip_dialog
, 1);
2796 session
->dialog
->callid
= gencallid();
2798 if (!(session
->dialog
->ourtag
)) {
2799 session
->dialog
->ourtag
= gentag();
2803 if (strstr(session
->with
, "sip:")) {
2804 to
= g_strdup(session
->with
);
2806 to
= g_strdup_printf("sip:%s", session
->with
);
2809 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
2810 purple_debug_info("sipe", "sipe_invite: msgformat=%s", msgformat
);
2812 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2816 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2820 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
2821 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
2826 key
= g_strdup_printf("<%s><%d><INVITE>", session
->dialog
->callid
, (session
->dialog
->cseq
) + 1);
2827 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
2828 purple_debug_info("sipe", "sipe_im_send: added message %s to unconfirmed_messages(count=%d)\n",
2829 key
, g_hash_table_size(session
->unconfirmed_messages
));
2832 contact
= get_contact(sip
);
2833 /* from = g_strdup_printf("sip:%s", sip->username);*/
2834 hdr
= g_strdup_printf(
2835 /*"Supported: ms-delayed-accept\r\n"*/
2836 /*"Roster-Manager: <%s>\r\n"*/
2837 /*"EndPoints: <%s>, <%s>\r\n"*/
2838 /*"Supported: com.microsoft.rtc-multiparty\r\n"*/
2840 "Content-Type: application/sdp\r\n",
2841 contact
, ms_text_format
);
2842 g_free(ms_text_format
);
2844 body
= g_strdup_printf(
2846 "o=- 0 0 IN IP4 %s\r\n"
2850 "m=message %d sip null\r\n"
2851 "a=accept-types:text/plain text/html image/gif "
2852 "multipart/alternative application/im-iscomposing+xml\r\n",
2853 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
2855 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
2856 to
, to
, hdr
, body
, session
->dialog
, process_invite_response
);
2866 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2869 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->dialog
, NULL
);
2870 im_session_destroy(sip
, session
);
2875 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
2877 struct sipe_account_data
*sip
= gc
->proto_data
;
2879 purple_debug_info("sipe", "conversation with %s closed\n", who
);
2880 im_session_close(sip
, find_im_session(sip
, who
));
2884 im_session_close_all (struct sipe_account_data
*sip
)
2886 GSList
*entry
= sip
->im_sessions
;
2888 im_session_close (sip
, entry
->data
);
2889 entry
= sip
->im_sessions
;
2893 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
2895 struct sipe_account_data
*sip
;
2897 struct sip_im_session
*session
;
2899 sip
= gc
->proto_data
;
2902 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
2904 session
= find_or_create_im_session(sip
, who
);
2906 // Queue the message
2907 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
2909 if (session
->dialog
&& session
->dialog
->callid
) {
2910 sipe_im_process_queue(sip
, session
);
2911 } else if (!session
->outgoing_invite
) {
2912 // Need to send the INVITE to get the outgoing dialog setup
2913 sipe_invite(sip
, session
, what
);
2920 /* End IM Session (INVITE and MESSAGE methods) */
2923 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
2925 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2926 struct sip_im_session
*session
;
2928 if (state
== PURPLE_NOT_TYPING
)
2931 session
= find_im_session(sip
, who
);
2933 if (session
&& session
->dialog
) {
2934 send_sip_request(gc
, "INFO", who
, who
,
2935 "Content-Type: application/xml\r\n",
2936 SIPE_SEND_TYPING
, session
->dialog
, NULL
);
2938 return SIPE_TYPING_SEND_TIMEOUT
;
2941 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
2943 GSList
*tmp
= sip
->transactions
;
2944 time_t currtime
= time(NULL
);
2946 struct transaction
*trans
= tmp
->data
;
2948 purple_debug_info("sipe", "have open transaction age: %ld\n", currtime
-trans
->time
);
2949 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
2952 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
2954 sendout_sipmsg(sip
, trans
->msg
);
2961 static void do_reauthenticate_cb(struct sipe_account_data
*sip
, void *unused
)
2963 /* register again when security token expires */
2964 /* we have to start a new authentication as the security token
2965 * is almost expired by sending a not signed REGISTER message */
2966 purple_debug_info("sipe", "do a full reauthentication\n");
2967 sipe_auth_free(&sip
->registrar
);
2968 sip
->registerstatus
= 0;
2970 sip
->reauthenticate_set
= FALSE
;
2973 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2977 gboolean found
= FALSE
;
2979 from
= parse_from(sipmsg_find_header(msg
, "From"));
2983 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
2985 contenttype
= sipmsg_find_header(msg
, "Content-Type");
2986 if (!strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
2988 gchar
*html
= get_html_message(contenttype
, msg
->body
);
2989 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
2991 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2994 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
2995 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3000 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
3004 state
= xmlnode_get_child(isc
, "state");
3007 purple_debug_info("sipe", "process_incoming_message: no state found\n");
3012 statedata
= xmlnode_get_data(state
);
3014 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
3015 else serv_got_typing_stopped(sip
->gc
, from
);
3020 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3024 purple_debug_info("sipe", "got unknown mime-type");
3025 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
3030 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3032 gchar
*ms_text_format
;
3035 struct sip_im_session
*session
;
3037 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? msg
->body
: "");
3039 // Only accept text invitations
3040 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
3041 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3045 from
= parse_from(sipmsg_find_header(msg
, "From"));
3046 session
= find_or_create_im_session (sip
, from
);
3048 if (session
->dialog
) {
3049 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
3051 session
->dialog
= g_new0(struct sip_dialog
, 1);
3053 sipe_parse_dialog(msg
, session
->dialog
, FALSE
);
3055 session
->dialog
->callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
3056 session
->dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
3057 session
->dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
3058 session
->dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
3061 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
3064 //ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk=
3065 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
3066 if (ms_text_format
) {
3067 if (!strncmp(ms_text_format
, "text/plain", 10) || !strncmp(ms_text_format
, "text/html", 9)) {
3069 gchar
*html
= get_html_message(ms_text_format
, NULL
);
3071 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3073 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message received
3079 sipmsg_remove_header(msg
, "Ms-Conversation-ID");
3080 sipmsg_remove_header(msg
, "Ms-Text-Format");
3081 sipmsg_remove_header(msg
, "EndPoints");
3082 sipmsg_remove_header(msg
, "User-Agent");
3083 sipmsg_remove_header(msg
, "Roster-Manager");
3085 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3086 //sipmsg_add_header(msg, "Supported", "ms-renders-gif");
3088 body
= g_strdup_printf(
3090 "o=- 0 0 IN IP4 %s\r\n"
3094 "m=message %d sip sip:%s\r\n"
3095 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
3096 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
3097 sip
->realport
, sip
->username
);
3098 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3102 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3106 sipmsg_remove_header(msg
, "Ms-Conversation-ID");
3107 sipmsg_remove_header(msg
, "EndPoints");
3108 sipmsg_remove_header(msg
, "User-Agent");
3110 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, BENOTIFY");
3111 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3113 body
= g_strdup_printf(
3115 "o=- 0 0 IN IP4 0.0.0.0\r\n"
3117 "c=IN IP4 0.0.0.0\r\n"
3119 "m=message %d sip sip:%s\r\n"
3120 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
3121 sip
->realport
, sip
->username
);
3122 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3126 static void sipe_connection_cleanup(struct sipe_account_data
*);
3127 static void create_connection(struct sipe_account_data
*, gchar
*, int);
3129 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3132 const gchar
*expires_header
;
3134 GSList
*hdr
= msg
->headers
;
3136 struct siphdrelement
*elem
;
3138 expires_header
= sipmsg_find_header(msg
, "Expires");
3139 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
3140 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
3142 switch (msg
->response
) {
3145 sip
->registerstatus
= 0;
3147 gchar
*contact_hdr
= NULL
;
3153 if (!sip
->reregister_set
) {
3154 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
3155 sipe_schedule_action(action_name
, expires
, do_register_cb
, sip
, NULL
);
3156 g_free(action_name
);
3157 sip
->reregister_set
= TRUE
;
3160 sip
->registerstatus
= 3;
3162 if (!sip
->reauthenticate_set
) {
3163 /* we have to reauthenticate as our security token expires
3164 after eight hours (be five minutes early) */
3165 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
3166 guint reauth_timeout
= (8 * 3600) - 360;
3167 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, sip
, NULL
);
3168 g_free(action_name
);
3169 sip
->reauthenticate_set
= TRUE
;
3172 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
3174 epid
= get_epid(sip
);
3175 uuid
= generateUUIDfromEPID(epid
);
3178 // There can be multiple Contact headers (one per location where the user is logged in) so
3179 // make sure to only get the one for this uuid
3180 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
3181 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
3182 if (valid_contact
) {
3183 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
3184 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
3185 g_free(valid_contact
);
3188 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
3193 g_free(sip
->contact
);
3195 sip
->contact
= g_strdup_printf("<%s>", gruu
);
3198 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
3199 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
);
3201 sip
->msrtc_event_categories
= FALSE
;
3202 sip
->batched_support
= FALSE
;
3207 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
3208 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
3209 sip
->msrtc_event_categories
= TRUE
;
3210 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->msrtc_event_categories
);
3212 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
3213 sip
->batched_support
= TRUE
;
3214 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->batched_support
);
3217 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
3218 gchar
**caps
= g_strsplit(elem
->value
,",",0);
3221 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
3222 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
3227 hdr
= g_slist_next(hdr
);
3230 if (!sip
->subscribed
) { //do it just once, not every re-register
3231 if(!sip
->msrtc_event_categories
){ //Only for LCS2005, on OCS2007 always backs the error 504 Server time-out
3232 sipe_options_request(sip
, sip
->sipdomain
);
3234 entry
= sip
->allow_events
;
3237 if (tmp
&& !g_ascii_strcasecmp(tmp
, "vnd-microsoft-roaming-contacts")) {
3238 sipe_subscribe_roaming_contacts(sip
, msg
);
3240 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-roaming-ACL")) {
3241 sipe_subscribe_roaming_acl(sip
, msg
);
3243 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-roaming-self")) {
3244 sipe_subscribe_roaming_self(sip
, msg
);
3246 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-provisioning-v2")) {
3247 sipe_subscribe_roaming_provisioning_v2(sip
, msg
);
3248 } else if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-provisioning")) { // LSC2005
3249 sipe_subscribe_roaming_provisioning(sip
, msg
);
3251 if (tmp
&& !g_ascii_strcasecmp(tmp
,"presence.wpending")) {
3252 sipe_subscribe_presence_wpending(sip
, msg
);
3254 entry
= entry
->next
;
3256 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
3257 sip
->subscribed
= TRUE
;
3260 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
3261 "timeout=", ";", NULL
);
3262 if (timeout
!= NULL
) {
3263 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
3264 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
3265 sip
->keepalive_timeout
);
3268 sip
->keepalive_timeout
= 300;
3271 // Should we remove the transaction here?
3272 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
3273 transactions_remove(sip
, tc
);
3278 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
3280 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
3281 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
3285 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
3288 tmp
= g_strsplit(parts
[0], ":", 0);
3289 hostname
= g_strdup(tmp
[0]);
3290 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
3294 tmp
= g_strsplit(parts
[i
], "=", 0);
3296 if (g_strcasecmp("transport", tmp
[0]) == 0) {
3297 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
3298 transport
= SIPE_TRANSPORT_TCP
;
3299 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
3300 transport
= SIPE_TRANSPORT_UDP
;
3309 /* Close old connection */
3310 sipe_connection_cleanup(sip
);
3312 /* Create new connection */
3313 sip
->transport
= transport
;
3314 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
3315 hostname
, port
, TRANSPORT_DESCRIPTOR
);
3316 create_connection(sip
, hostname
, port
);
3322 if (sip
->registerstatus
!= 2) {
3323 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
3324 if (sip
->registrar
.retries
> 3) {
3325 sip
->gc
->wants_to_die
= TRUE
;
3326 purple_connection_error(sip
->gc
, _("Wrong Password"));
3329 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3330 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3332 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3334 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3335 fill_auth(sip
, tmp
, &sip
->registrar
);
3336 sip
->registerstatus
= 2;
3337 if (sip
->account
->disconnecting
) {
3338 do_register_exp(sip
, 0);
3346 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
3347 if (warning
!= NULL
) {
3349 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
3351 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
3352 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
3355 warning
= g_strdup(_("You have been rejected by the server"));
3358 sip
->gc
->wants_to_die
= TRUE
;
3359 purple_connection_error(sip
->gc
, warning
);
3366 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3367 if (warning
!= NULL
) {
3368 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3369 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
3372 warning
= g_strdup(_("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator"));
3375 sip
->gc
->wants_to_die
= TRUE
;
3376 purple_connection_error(sip
->gc
, warning
);
3383 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3384 if (warning
!= NULL
) {
3385 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3386 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
3389 warning
= g_strdup(_("Service unavailable: no reason given"));
3392 sip
->gc
->wants_to_die
= TRUE
;
3393 purple_connection_error(sip
->gc
, warning
);
3402 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3405 xmlnode
*xn_categories
;
3406 xmlnode
*xn_category
;
3408 const char *activity
= NULL
;
3410 xn_categories
= xmlnode_from_str(data
, len
);
3411 uri
= xmlnode_get_attrib(xn_categories
, "uri");
3413 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
3415 xn_category
= xmlnode_get_next_twin(xn_category
) )
3417 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
3419 if (!strcmp(attrVar
, "note"))
3422 struct sipe_buddy
*sbuddy
;
3423 xn_node
= xmlnode_get_child(xn_category
, "note");
3424 if (!xn_node
) continue;
3425 xn_node
= xmlnode_get_child(xn_node
, "body");
3426 if (!xn_node
) continue;
3428 note
= xmlnode_get_data(xn_node
);
3431 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3435 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s),note(%s)\n",uri
,note
);
3436 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3437 sbuddy
->annotation
= g_strdup(note
);
3442 else if(!strcmp(attrVar
, "state"))
3446 xn_node
= xmlnode_get_child(xn_category
, "state");
3447 if (!xn_node
) continue;
3448 xn_node
= xmlnode_get_child(xn_node
, "availability");
3449 if (!xn_node
) continue;
3451 data
= xmlnode_get_data(xn_node
);
3456 activity
= SIPE_STATUS_ID_UNKNOWN
;
3457 else if (avail
< 4500)
3458 activity
= SIPE_STATUS_ID_AVAILABLE
;
3459 else if (avail
< 6000)
3460 activity
= SIPE_STATUS_ID_BRB
;
3461 else if (avail
< 7500)
3462 activity
= SIPE_STATUS_ID_ONPHONE
;
3463 else if (avail
< 9000)
3464 activity
= SIPE_STATUS_ID_BUSY
;
3465 else if (avail
< 12000)
3466 activity
= SIPE_STATUS_ID_DND
;
3467 else if (avail
< 18000)
3468 activity
= SIPE_STATUS_ID_AWAY
;
3470 activity
= SIPE_STATUS_ID_OFFLINE
;
3474 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", activity
);
3475 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
3478 xmlnode_free(xn_categories
);
3481 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3484 xmlnode
*xn_resource
;
3486 xn_list
= xmlnode_from_str(data
, len
);
3488 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
3490 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
3492 const char *uri
, *state
;
3493 xmlnode
*xn_instance
;
3495 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
3496 if (!xn_instance
) continue;
3498 uri
= xmlnode_get_attrib(xn_resource
, "uri");
3499 state
= xmlnode_get_attrib(xn_instance
, "state");
3500 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
3502 if (strstr(state
, "resubscribe")) {
3503 struct sipe_buddy
*sbuddy
;
3504 sipe_subscribe_presence_single(sip
, (void *) uri
);
3505 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3507 sbuddy
->resubscribed
= TRUE
;
3512 xmlnode_free(xn_list
);
3515 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3519 gchar
*activity
= NULL
;
3521 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
3522 gboolean isonline
= FALSE
;
3523 xmlnode
*display_name_node
;
3525 pidf
= xmlnode_from_str(data
, len
);
3527 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data
);
3531 uri
= xmlnode_get_attrib(pidf
, "entity");
3533 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
3535 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3536 basicstatus
= xmlnode_get_child(status
, "basic");
3541 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
3546 getbasic
= xmlnode_get_data(basicstatus
);
3548 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
3553 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
3554 if (strstr(getbasic
, "open")) {
3559 display_name_node
= xmlnode_get_child(pidf
, "display-name");
3560 // updating display name if alias was just URI
3561 if (display_name_node
) {
3562 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
3563 GSList
*entry
= buddies
;
3564 PurpleBuddy
*p_buddy
;
3565 char * display_name
= xmlnode_get_data(display_name_node
);
3568 const char *server_alias
;
3571 p_buddy
= entry
->data
;
3573 alias
= (char *)purple_buddy_get_alias(p_buddy
);
3574 alias
= alias
? g_strdup_printf("sip:%s", alias
) : NULL
;
3575 if (!alias
|| !g_ascii_strcasecmp(uri
, alias
)) {
3576 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
3577 purple_blist_alias_buddy(p_buddy
, display_name
);
3581 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3583 ( (server_alias
&& strcmp(display_name
, server_alias
))
3584 || !server_alias
|| strlen(server_alias
) == 0 )
3586 purple_blist_server_alias_buddy(p_buddy
, display_name
);
3589 entry
= entry
->next
;
3591 g_free(display_name
);
3594 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
3595 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3596 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
3597 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
3598 activity
= xmlnode_get_data(basicstatus
);
3599 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
3606 const gchar
* status_id
= NULL
;
3608 if (strstr(activity
, "busy")) {
3609 status_id
= SIPE_STATUS_ID_BUSY
;
3610 } else if (strstr(activity
, "away")) {
3611 status_id
= SIPE_STATUS_ID_AWAY
;
3616 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3619 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
3620 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
3622 purple_prpl_got_user_status(sip
->account
, uri
, SIPE_STATUS_ID_OFFLINE
, NULL
);
3629 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3631 const char *availability
;
3632 const char *activity
;
3633 const char *display_name
= NULL
;
3634 const char *activity_name
= NULL
;
3639 struct sipe_buddy
*sbuddy
;
3641 xmlnode
*xn_presentity
= xmlnode_from_str(data
, len
);
3643 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
3644 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
3645 xmlnode
*xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
3646 xmlnode
*xn_email
= xmlnode_get_child(xn_presentity
, "email");
3647 const char *email
= xn_email
? xmlnode_get_attrib(xn_email
, "email") : NULL
;
3648 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
3649 xmlnode
*xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
):NULL
;
3650 const char *avail
= xn_state
? xmlnode_get_attrib(xn_state
, "avail") : NULL
;
3652 xmlnode
*xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
3653 char *note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
3654 xmlnode
*xn_devices
= xmlnode_get_child(xn_presentity
, "devices");
3655 xmlnode
*xn_device_presence
= xn_devices
? xmlnode_get_child(xn_devices
, "devicePresence") : NULL
;
3656 xmlnode
*xn_device_name
= xn_device_presence
? xmlnode_get_child(xn_device_presence
, "deviceName") : NULL
;
3657 const char *device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
3659 name
= xmlnode_get_attrib(xn_presentity
, "uri");
3660 uri
= g_strdup_printf("sip:%s", name
);
3661 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
3662 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
3664 // updating display name if alias was just URI
3665 if (xn_display_name
) {
3666 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
3667 GSList
*entry
= buddies
;
3668 PurpleBuddy
*p_buddy
;
3669 display_name
= xmlnode_get_attrib(xn_display_name
, "displayName");
3672 const char *email_str
, *server_alias
;
3674 p_buddy
= entry
->data
;
3676 if (!g_ascii_strcasecmp(name
, purple_buddy_get_alias(p_buddy
))) {
3677 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
3678 purple_blist_alias_buddy(p_buddy
, display_name
);
3681 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3683 ( (server_alias
&& strcmp(display_name
, server_alias
))
3684 || !server_alias
|| strlen(server_alias
) == 0 )
3686 purple_blist_server_alias_buddy(p_buddy
, display_name
);
3690 email_str
= purple_blist_node_get_string((PurpleBlistNode
*)p_buddy
, "email");
3691 if (!email_str
|| g_ascii_strcasecmp(email_str
, email
)) {
3692 purple_blist_node_set_string((PurpleBlistNode
*)p_buddy
, "email", email
);
3696 entry
= entry
->next
;
3700 avl
= atoi(availability
);
3701 act
= atoi(activity
);
3703 if(sip
->msrtc_event_categories
){
3704 if (act
== 100 && avl
== 0)
3705 activity_name
= SIPE_STATUS_ID_OFFLINE
;
3706 else if (act
== 100 && avl
== 300)
3707 activity_name
= SIPE_STATUS_ID_AWAY
;
3708 else if (act
== 300 && avl
== 300)
3709 activity_name
= SIPE_STATUS_ID_BRB
;
3710 else if (act
== 400 && avl
== 300)
3711 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
3712 else if (act
== 500 && act
== 300)
3713 activity_name
= SIPE_STATUS_ID_ONPHONE
;
3714 else if (act
== 600 && avl
== 300)
3715 activity_name
= SIPE_STATUS_ID_BUSY
;
3716 else if (act
== 0 && avl
== 0){ //MSRTC elements are zero
3717 if(avail
){ //Check for LegacyInterop elements
3720 activity_name
= SIPE_STATUS_ID_OFFLINE
;
3721 else if (avl
== 3500)
3722 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
3723 else if (avl
== 15500)
3724 activity_name
= SIPE_STATUS_ID_AWAY
;
3725 else if (avl
== 6500)
3726 activity_name
= SIPE_STATUS_ID_BUSY
;
3727 else if (avl
== 12500)
3728 activity_name
= SIPE_STATUS_ID_BRB
;
3733 if(activity_name
== NULL
){
3735 activity_name
= SIPE_STATUS_ID_AWAY
;
3736 else if (act
<= 150)
3737 activity_name
= SIPE_STATUS_ID_LUNCH
;
3738 else if (act
<= 300)
3739 activity_name
= SIPE_STATUS_ID_BRB
;
3740 else if (act
<= 400)
3741 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
3742 else if (act
<= 500)
3743 activity_name
= SIPE_STATUS_ID_ONPHONE
;
3744 else if (act
<= 600)
3745 activity_name
= SIPE_STATUS_ID_BUSY
;
3747 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
3750 activity_name
= SIPE_STATUS_ID_OFFLINE
;
3753 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3756 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3757 sbuddy
->annotation
= NULL
;
3758 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
3760 if (sbuddy
->device_name
) { g_free(sbuddy
->device_name
); }
3761 sbuddy
->device_name
= NULL
;
3762 if (device_name
) { sbuddy
->device_name
= g_strdup(device_name
); }
3765 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
3766 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
3768 xmlnode_free(xn_presentity
);
3772 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3774 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
3776 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
3778 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
3779 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
3781 const char *content
= msg
->body
;
3782 unsigned length
= msg
->bodylen
;
3783 PurpleMimeDocument
*mime
= NULL
;
3785 if (strstr(ctype
, "multipart"))
3787 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
3788 const char *content_type
;
3790 mime
= purple_mime_document_parse(doc
);
3791 parts
= purple_mime_document_get_parts(mime
);
3793 content
= purple_mime_part_get_data(parts
->data
);
3794 length
= purple_mime_part_get_length(parts
->data
);
3795 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
3796 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
3798 process_incoming_notify_rlmi_resub(sip
, content
, length
);
3800 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
3802 process_incoming_notify_msrtc(sip
, content
, length
);
3806 process_incoming_notify_rlmi(sip
, content
, length
);
3808 parts
= parts
->next
;
3814 purple_mime_document_free(mime
);
3817 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
3819 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
3821 else if(strstr(ctype
, "application/rlmi+xml"))
3823 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
3826 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
3828 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
3832 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
3837 * Dispatcher for all incoming subscription information
3838 * whether it comes from NOTIFY, BENOTIFY requests or
3839 * piggy-backed to subscription's OK responce.
3841 * @param request whether initiated from BE/NOTIFY request or OK-response message.
3842 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
3844 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
3846 gchar
*event
= sipmsg_find_header(msg
, "Event");
3847 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
3850 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event
? event
: "", msg
->body
);
3851 purple_debug_info("sipe", "process_incoming_notify: subscription_state:%s\n\n", subscription_state
);
3855 const gchar
*expires_header
;
3856 expires_header
= sipmsg_find_header(msg
, "Expires");
3857 timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
3858 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n\n", timeout
);
3859 timeout
= (timeout
- 60) > 60 ? (timeout
- 60) : timeout
; // 1 min ahead of expiration
3862 if (!subscription_state
|| strstr(subscription_state
, "active"))
3864 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
3866 sipe_process_presence(sip
, msg
);
3868 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
3870 sipe_process_roaming_contacts(sip
, msg
, NULL
);
3872 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self") )
3874 sipe_process_roaming_self(sip
, msg
);
3876 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
3878 sipe_process_roaming_acl(sip
, msg
);
3880 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
3882 sipe_process_presence_wpending(sip
, msg
);
3886 purple_debug_info("sipe", "Unable to process (BE)NOTIFY. Event is not supported:%s\n", event
? event
: "");
3890 //The server sends a (BE)NOTIFY with the status 'terminated'
3891 if (request
&& subscription_state
&& strstr(subscription_state
, "terminated") ) {
3892 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3893 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from
);
3897 if (timeout
&& event
) {// For LSC 2005 and OCS 2007
3898 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
3899 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
3901 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
3902 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_contacts, sip, msg);
3903 g_free(action_name);
3905 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
3906 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
3908 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
3909 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_acl, sip, msg);
3910 g_free(action_name);
3913 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
3914 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
3916 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
3917 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, sip
, NULL
);
3918 g_free(action_name
);
3920 else if (!g_ascii_strcasecmp(event
, "presence") &&
3921 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
3923 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
3924 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
3925 if(sip
->batched_support
) {
3926 gchar
*my_self
= g_strdup_printf("sip:%s",sip
->username
);
3927 if(!g_ascii_strcasecmp(who
, my_self
)){
3928 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_batched
, sip
, NULL
);
3929 purple_debug_info("sipe", "Resubscription full batched list in %d\n",timeout
);
3930 g_free(who
); /* unused */
3933 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, sip
, who
);
3934 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
,timeout
);
3939 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, sip
, who
);
3940 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
,timeout
);
3942 g_free(action_name
);
3943 /* "who" will be freed by the action we just scheduled */
3947 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
3949 sipe_process_registration_notify(sip
, msg
);
3952 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
3953 if (request
&& !benotify
)
3955 sipmsg_remove_header(msg
, "Expires");
3956 sipmsg_remove_header(msg
, "subscription-state");
3957 sipmsg_remove_header(msg
, "Event");
3958 sipmsg_remove_header(msg
, "Require");
3959 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3966 static gchar* gen_xpidf(struct sipe_account_data *sip)
3968 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3970 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
3971 "<display name=\"sip:%s\"/>\r\n"
3972 "<atom id=\"1234\">\r\n"
3973 "<address uri=\"sip:%s\">\r\n"
3974 "<status status=\"%s\"/>\r\n"
3987 static gchar* gen_pidf(struct sipe_account_data *sip)
3989 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3990 "<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"
3991 "<tuple id=\"0\">\r\n"
3993 "<basic>open</basic>\r\n"
3994 "<ep:activities>\r\n"
3995 " <ep:activity>%s</ep:activity>\r\n"
3999 "<ci:display-name>%s</ci:display-name>\r\n"
4008 static void send_presence_soap(struct sipe_account_data
*sip
, const char * note
)
4010 int availability
= 300; // online
4011 int activity
= 400; // Available
4014 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
)) {
4016 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4018 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4020 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4022 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4024 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4026 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_INVISIBLE
) ||
4027 !strcmp(sip
->status
, SIPE_STATUS_ID_OFFLINE
)) {
4028 availability
= 0; // offline
4031 activity
= 400; // available
4034 name
= g_strdup_printf("sip: sip:%s", sip
->username
);
4035 //@TODO: send user data - state; add hostname in upper case
4036 body
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
4037 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
4043 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
4045 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4046 if (msg
->response
== 200) {
4047 sip
->status_version
= 0;
4048 send_presence_status(sip
);
4054 process_send_presence_category_publish_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
4056 if (msg
->response
== 409) {
4057 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4058 // TODO need to parse the version #'s?
4059 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
4060 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
4064 purple_debug_info("sipe", "process_send_presence_category_publish_response = %s\n", msg
->body
);
4066 tmp
= get_contact(sip
);
4067 hdr
= g_strdup_printf("Contact: %s\r\n"
4068 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4070 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
4080 static void send_presence_category_publish(struct sipe_account_data
*sip
, const char * note
)
4087 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
) ||
4088 !strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4090 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_DND
)) {
4092 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4094 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4096 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4098 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4100 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_UNKNOWN
)) {
4103 // Offline or invisible
4107 uri
= g_strdup_printf("sip:%s", sip
->username
);
4108 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
4109 sip
->status_version
, code
,
4110 sip
->status_version
, code
,
4111 sip
->status_version
, note
? note
: "",
4112 sip
->status_version
, note
? note
: "",
4113 sip
->status_version
, note
? note
: ""
4115 sip
->status_version
++;
4117 tmp
= get_contact(sip
);
4118 hdr
= g_strdup_printf("Contact: %s\r\n"
4119 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4121 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
4129 static void send_presence_status(struct sipe_account_data
*sip
)
4131 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
4133 if (!status
) return;
4135 note
= purple_status_get_attr_string(status
, "message");
4137 if(sip
->msrtc_event_categories
){
4138 send_presence_category_publish(sip
, note
);
4140 send_presence_soap(sip
, note
);
4144 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
4146 gboolean found
= FALSE
;
4147 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
4148 if (msg
->response
== 0) { /* request */
4149 if (!strcmp(msg
->method
, "MESSAGE")) {
4150 process_incoming_message(sip
, msg
);
4152 } else if (!strcmp(msg
->method
, "NOTIFY")) {
4153 purple_debug_info("sipe","send->process_incoming_notify\n");
4154 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
4156 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
4157 purple_debug_info("sipe","send->process_incoming_benotify\n");
4158 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
4160 } else if (!strcmp(msg
->method
, "INVITE")) {
4161 process_incoming_invite(sip
, msg
);
4163 } else if (!strcmp(msg
->method
, "OPTIONS")) {
4164 process_incoming_options(sip
, msg
);
4166 } else if (!strcmp(msg
->method
, "INFO")) {
4168 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4170 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
4173 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4175 } else if (!strcmp(msg
->method
, "ACK")) {
4176 // ACK's don't need any response
4178 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
4179 // LCS 2005 sends us these - just respond 200 OK
4181 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4182 } else if (!strcmp(msg
->method
, "BYE")) {
4183 struct sip_im_session
*session
;
4185 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4187 from
= parse_from(sipmsg_find_header(msg
, "From"));
4188 session
= find_im_session (sip
, from
);
4192 // TODO Let the user know the other user left the conversation?
4193 im_session_destroy(sip
, session
);
4198 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
4200 } else { /* response */
4201 struct transaction
*trans
= transactions_find(sip
, msg
);
4203 if (msg
->response
== 407) {
4204 gchar
*resend
, *auth
, *ptmp
;
4206 if (sip
->proxy
.retries
> 30) return;
4207 sip
->proxy
.retries
++;
4208 /* do proxy authentication */
4210 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
4212 fill_auth(sip
, ptmp
, &sip
->proxy
);
4213 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
4214 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
4215 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
4217 resend
= sipmsg_to_string(trans
->msg
);
4218 /* resend request */
4219 sendout_pkt(sip
->gc
, resend
);
4222 if (msg
->response
== 100 || msg
->response
== 180) {
4223 /* ignore provisional response */
4224 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
4226 sip
->proxy
.retries
= 0;
4227 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
4228 if (msg
->response
== 401)
4230 sip
->registrar
.retries
++;
4231 sip
->registrar
.expires
= 0;
4235 sip
->registrar
.retries
= 0;
4237 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
4239 if (msg
->response
== 401) {
4240 gchar
*resend
, *auth
, *ptmp
;
4242 if (sip
->registrar
.retries
> 4) return;
4243 sip
->registrar
.retries
++;
4245 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4246 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
4248 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4251 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
4253 fill_auth(sip
, ptmp
, &sip
->registrar
);
4254 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
4255 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
4256 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
4258 //sipmsg_remove_header(trans->msg, "Authorization");
4259 //sipmsg_add_header(trans->msg, "Authorization", auth);
4261 resend
= sipmsg_to_string(trans
->msg
);
4262 /* resend request */
4263 sendout_pkt(sip
->gc
, resend
);
4268 if (trans
->callback
) {
4269 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
4270 /* call the callback to process response*/
4271 (trans
->callback
)(sip
, msg
, trans
);
4273 /* Not sure if this is needed or what needs to be done
4274 but transactions seem to be removed prematurely so
4275 this only removes them if the response is 200 OK */
4276 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
4277 /*Has a bug and it's unneccesary*/
4278 /*transactions_remove(sip, trans);*/
4284 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
4288 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
4292 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
4300 /* according to the RFC remove CRLF at the beginning */
4301 while (*cur
== '\r' || *cur
== '\n') {
4304 if (cur
!= conn
->inbuf
) {
4305 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
4306 conn
->inbufused
= strlen(conn
->inbuf
);
4309 /* Received a full Header? */
4310 sip
->processing_input
= TRUE
;
4311 while (sip
->processing_input
&&
4312 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
4313 time_t currtime
= time(NULL
);
4316 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
4317 msg
= sipmsg_parse_header(conn
->inbuf
);
4320 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
4321 if (restlen
>= msg
->bodylen
) {
4322 dummy
= g_malloc(msg
->bodylen
+ 1);
4323 memcpy(dummy
, cur
, msg
->bodylen
);
4324 dummy
[msg
->bodylen
] = '\0';
4326 cur
+= msg
->bodylen
;
4327 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
4328 conn
->inbufused
= strlen(conn
->inbuf
);
4330 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
4331 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
4337 purple_debug_info("sipe", "body:\n%s", msg->body);
4340 // Verify the signature before processing it
4341 if (sip
->registrar
.ntlm_key
) {
4342 struct sipmsg_breakdown msgbd
;
4343 gchar
*signature_input_str
;
4344 gchar
*signature
= NULL
;
4347 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
4348 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
4349 if (signature_input_str
!= NULL
) {
4350 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
4352 g_free(signature_input_str
);
4354 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
4356 if (signature
!= NULL
) {
4357 if (rspauth
!= NULL
) {
4358 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
4359 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
4360 process_input_message(sip
, msg
);
4362 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
4363 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
4364 sip
->gc
->wants_to_die
= TRUE
;
4366 } else if (msg
->response
== 401) {
4367 purple_connection_error(sip
->gc
, _("Wrong Password"));
4368 sip
->gc
->wants_to_die
= TRUE
;
4374 sipmsg_breakdown_free(&msgbd
);
4376 process_input_message(sip
, msg
);
4383 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
4385 PurpleConnection
*gc
= data
;
4386 struct sipe_account_data
*sip
= gc
->proto_data
;
4391 static char buffer
[65536];
4392 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
4394 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
4395 msg
= sipmsg_parse_msg(buffer
);
4396 if (msg
) process_input_message(sip
, msg
);
4400 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
4402 struct sipe_account_data
*sip
= gc
->proto_data
;
4403 PurpleSslConnection
*gsc
= sip
->gsc
;
4405 purple_debug_error("sipe", "%s",debug
);
4406 purple_connection_error(gc
, msg
);
4408 /* Invalidate this connection. Next send will open a new one */
4410 connection_remove(sip
, gsc
->fd
);
4411 purple_ssl_close(gsc
);
4417 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
4419 PurpleConnection
*gc
= data
;
4420 struct sipe_account_data
*sip
;
4421 struct sip_connection
*conn
;
4423 gboolean firstread
= TRUE
;
4425 /* NOTE: This check *IS* necessary */
4426 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
4427 purple_ssl_close(gsc
);
4431 sip
= gc
->proto_data
;
4432 conn
= connection_find(sip
, gsc
->fd
);
4434 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
4435 gc
->wants_to_die
= TRUE
;
4436 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
4440 /* Read all available data from the SSL connection */
4442 /* Increase input buffer size as needed */
4443 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
4444 conn
->inbuflen
+= SIMPLE_BUF_INC
;
4445 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
4446 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
4449 /* Try to read as much as there is space left in the buffer */
4450 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
4451 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
4453 if (len
< 0 && errno
== EAGAIN
) {
4454 /* Try again later */
4456 } else if (len
< 0) {
4457 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
4459 } else if (firstread
&& (len
== 0)) {
4460 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
4464 conn
->inbufused
+= len
;
4467 /* Equivalence indicates that there is possibly more data to read */
4468 } while (len
== readlen
);
4470 conn
->inbuf
[conn
->inbufused
] = '\0';
4471 process_input(sip
, conn
);
4475 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
4477 PurpleConnection
*gc
= data
;
4478 struct sipe_account_data
*sip
= gc
->proto_data
;
4480 struct sip_connection
*conn
= connection_find(sip
, source
);
4482 purple_debug_error("sipe", "Connection not found!\n");
4486 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
4487 conn
->inbuflen
+= SIMPLE_BUF_INC
;
4488 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
4491 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
4493 if (len
< 0 && errno
== EAGAIN
)
4495 else if (len
<= 0) {
4496 purple_debug_info("sipe", "sipe_input_cb: read error\n");
4497 connection_remove(sip
, source
);
4498 if (sip
->fd
== source
) sip
->fd
= -1;
4502 conn
->inbufused
+= len
;
4503 conn
->inbuf
[conn
->inbufused
] = '\0';
4505 process_input(sip
, conn
);
4508 /* Callback for new connections on incoming TCP port */
4509 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
4511 PurpleConnection
*gc
= data
;
4512 struct sipe_account_data
*sip
= gc
->proto_data
;
4513 struct sip_connection
*conn
;
4515 int newfd
= accept(source
, NULL
, NULL
);
4517 conn
= connection_create(sip
, newfd
);
4519 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4522 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
4524 PurpleConnection
*gc
= data
;
4525 struct sipe_account_data
*sip
;
4526 struct sip_connection
*conn
;
4528 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4536 purple_connection_error(gc
, _("Could not connect"));
4540 sip
= gc
->proto_data
;
4542 sip
->last_keepalive
= time(NULL
);
4544 conn
= connection_create(sip
, source
);
4548 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4551 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
4553 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
4554 if (sip
== NULL
) return;
4559 static guint
sipe_ht_hash_nick(const char *nick
)
4561 char *lc
= g_utf8_strdown(nick
, -1);
4562 guint bucket
= g_str_hash(lc
);
4568 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
4570 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
4573 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
4575 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4577 sip
->listen_data
= NULL
;
4579 if (listenfd
== -1) {
4580 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4586 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
4587 sip
->listenfd
= sip
->fd
;
4589 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
4591 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
4595 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
4597 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4600 sip
->query_data
= NULL
;
4602 if (!hosts
|| !hosts
->data
) {
4603 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
4607 addr_size
= GPOINTER_TO_INT(hosts
->data
);
4608 hosts
= g_slist_remove(hosts
, hosts
->data
);
4609 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
4610 g_free(hosts
->data
);
4611 hosts
= g_slist_remove(hosts
, hosts
->data
);
4613 hosts
= g_slist_remove(hosts
, hosts
->data
);
4614 g_free(hosts
->data
);
4615 hosts
= g_slist_remove(hosts
, hosts
->data
);
4618 /* create socket for incoming connections */
4619 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
4620 sipe_udp_host_resolved_listen_cb
, sip
);
4621 if (sip
->listen_data
== NULL
) {
4622 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4627 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
4630 PurpleConnection
*gc
= data
;
4631 struct sipe_account_data
*sip
;
4633 /* If the connection is already disconnected, we don't need to do anything else */
4634 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4637 sip
= gc
->proto_data
;
4642 case PURPLE_SSL_CONNECT_FAILED
:
4643 purple_connection_error(gc
, _("Connection Failed"));
4645 case PURPLE_SSL_HANDSHAKE_FAILED
:
4646 purple_connection_error(gc
, _("SSL Handshake Failed"));
4648 case PURPLE_SSL_CERTIFICATE_INVALID
:
4649 purple_connection_error(gc
, _("SSL Certificate Invalid"));
4655 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
4657 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4658 PurpleProxyConnectData
*connect_data
;
4660 sip
->listen_data
= NULL
;
4662 sip
->listenfd
= listenfd
;
4663 if (sip
->listenfd
== -1) {
4664 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4668 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
4669 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
4670 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
4671 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
4672 sipe_newconn_cb
, sip
->gc
);
4673 purple_debug_info("sipe", "connecting to %s port %d\n",
4674 sip
->realhostname
, sip
->realport
);
4675 /* open tcp connection to the server */
4676 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
4677 sip
->realport
, login_cb
, sip
->gc
);
4679 if (connect_data
== NULL
) {
4680 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
4685 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
4687 PurpleAccount
*account
= sip
->account
;
4688 PurpleConnection
*gc
= sip
->gc
;
4690 if (purple_account_get_bool(account
, "useport", FALSE
)) {
4691 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
4692 port
= purple_account_get_int(account
, "port", 0);
4694 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
4697 sip
->realhostname
= hostname
;
4698 sip
->realport
= port
;
4700 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
4703 /* TODO: is there a good default grow size? */
4704 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
4705 sip
->txbuf
= purple_circ_buffer_new(0);
4707 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
4709 if (!purple_ssl_is_supported()) {
4710 gc
->wants_to_die
= TRUE
;
4711 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
4715 purple_debug_info("sipe", "using SSL\n");
4717 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
4718 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
4719 if (sip
->gsc
== NULL
) {
4720 purple_connection_error(gc
, _("Could not create SSL context"));
4723 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
4725 purple_debug_info("sipe", "using UDP\n");
4727 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
4728 if (sip
->query_data
== NULL
) {
4729 purple_connection_error(gc
, _("Could not resolve hostname"));
4733 purple_debug_info("sipe", "using TCP\n");
4734 /* create socket for incoming connections */
4735 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
4736 sipe_tcp_connect_listen_cb
, sip
);
4737 if (sip
->listen_data
== NULL
) {
4738 purple_connection_error(gc
, _("Could not create listen socket"));
4744 /* Service list for autodection */
4745 static const struct sipe_service_data service_autodetect
[] = {
4746 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4747 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4748 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4749 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4753 /* Service list for SSL/TLS */
4754 static const struct sipe_service_data service_tls
[] = {
4755 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4756 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4760 /* Service list for TCP */
4761 static const struct sipe_service_data service_tcp
[] = {
4762 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4763 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4767 /* Service list for UDP */
4768 static const struct sipe_service_data service_udp
[] = {
4769 { "sip", "udp", SIPE_TRANSPORT_UDP
},
4773 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
4774 static void resolve_next_service(struct sipe_account_data
*sip
,
4775 const struct sipe_service_data
*start
)
4778 sip
->service_data
= start
;
4780 sip
->service_data
++;
4781 if (sip
->service_data
->service
== NULL
) {
4783 /* Try connecting to the SIP hostname directly */
4784 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
4785 if (sip
->auto_transport
) {
4786 // If SSL is supported, default to using it; OCS servers aren't configured
4787 // by default to accept TCP
4788 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
4789 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
4790 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
4793 hostname
= g_strdup(sip
->sipdomain
);
4794 create_connection(sip
, hostname
, 0);
4799 /* Try to resolve next service */
4800 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
4801 sip
->service_data
->transport
,
4806 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
4808 struct sipe_account_data
*sip
= data
;
4810 sip
->srv_query_data
= NULL
;
4812 /* find the host to connect to */
4814 gchar
*hostname
= g_strdup(resp
->hostname
);
4815 int port
= resp
->port
;
4816 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
4820 sip
->transport
= sip
->service_data
->type
;
4822 create_connection(sip
, hostname
, port
);
4824 resolve_next_service(sip
, NULL
);
4828 static void sipe_login(PurpleAccount
*account
)
4830 PurpleConnection
*gc
;
4831 struct sipe_account_data
*sip
;
4832 gchar
**signinname_login
, **userserver
, **domain_user
;
4833 const char *transport
;
4835 const char *username
= purple_account_get_username(account
);
4836 gc
= purple_account_get_connection(account
);
4838 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
4839 gc
->wants_to_die
= TRUE
;
4840 purple_connection_error(gc
, _("SIP Exchange username contains invalid characters"));
4844 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
4845 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
4846 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
4848 sip
->account
= account
;
4849 sip
->reregister_set
= FALSE
;
4850 sip
->reauthenticate_set
= FALSE
;
4851 sip
->subscribed
= FALSE
;
4852 sip
->subscribed_buddies
= FALSE
;
4854 signinname_login
= g_strsplit(username
, ",", 2);
4856 userserver
= g_strsplit(signinname_login
[0], "@", 2);
4857 purple_connection_set_display_name(gc
, userserver
[0]);
4858 sip
->username
= g_strjoin("@", userserver
[0], userserver
[1], NULL
);
4859 sip
->sipdomain
= g_strdup(userserver
[1]);
4861 if (strpbrk(sip
->username
, " \t\v\r\n") != NULL
) {
4862 gc
->wants_to_die
= TRUE
;
4863 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
4867 domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
4868 sip
->authdomain
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[0]) : NULL
;
4869 sip
->authuser
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[1]) : (signinname_login
? g_strdup(signinname_login
[1]) : NULL
);
4871 sip
->password
= g_strdup(purple_connection_get_password(gc
));
4873 g_strfreev(userserver
);
4874 g_strfreev(domain_user
);
4875 g_strfreev(signinname_login
);
4877 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
4879 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
4881 /* TODO: Set the status correctly. */
4882 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
4884 transport
= purple_account_get_string(account
, "transport", "auto");
4885 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
4886 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
4889 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
4890 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
4891 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
4892 } else if (strcmp(transport
, "auto") == 0) {
4893 sip
->auto_transport
= TRUE
;
4894 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
4895 } else if (strcmp(transport
, "tls") == 0) {
4896 resolve_next_service(sip
, service_tls
);
4897 } else if (strcmp(transport
, "tcp") == 0) {
4898 resolve_next_service(sip
, service_tcp
);
4900 resolve_next_service(sip
, service_udp
);
4904 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
4906 connection_free_all(sip
);
4911 if (sip
->query_data
!= NULL
)
4912 purple_dnsquery_destroy(sip
->query_data
);
4913 sip
->query_data
= NULL
;
4915 if (sip
->srv_query_data
!= NULL
)
4916 purple_srv_cancel(sip
->srv_query_data
);
4917 sip
->srv_query_data
= NULL
;
4919 if (sip
->listen_data
!= NULL
)
4920 purple_network_listen_cancel(sip
->listen_data
);
4921 sip
->listen_data
= NULL
;
4923 if (sip
->gsc
!= NULL
)
4924 purple_ssl_close(sip
->gsc
);
4927 sipe_auth_free(&sip
->registrar
);
4928 sipe_auth_free(&sip
->proxy
);
4931 purple_circ_buffer_destroy(sip
->txbuf
);
4934 g_free(sip
->realhostname
);
4935 sip
->realhostname
= NULL
;
4938 purple_input_remove(sip
->listenpa
);
4940 if (sip
->tx_handler
)
4941 purple_input_remove(sip
->tx_handler
);
4942 sip
->tx_handler
= 0;
4943 if (sip
->resendtimeout
)
4944 purple_timeout_remove(sip
->resendtimeout
);
4945 sip
->resendtimeout
= 0;
4946 if (sip
->timeouts
) {
4947 GSList
*entry
= sip
->timeouts
;
4949 struct scheduled_action
*sched_action
= entry
->data
;
4950 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
4951 purple_timeout_remove(sched_action
->timeout_handler
);
4952 g_free(sched_action
->payload
);
4953 g_free(sched_action
->name
);
4954 g_free(sched_action
);
4955 entry
= entry
->next
;
4958 g_slist_free(sip
->timeouts
);
4960 if (sip
->allow_events
) {
4961 GSList
*entry
= sip
->allow_events
;
4963 g_free(entry
->data
);
4964 entry
= entry
->next
;
4967 g_slist_free(sip
->allow_events
);
4970 g_free(sip
->contact
);
4971 sip
->contact
= NULL
;
4973 g_free(sip
->regcallid
);
4974 sip
->regcallid
= NULL
;
4977 sip
->processing_input
= FALSE
;
4981 * A callback for g_hash_table_foreach_remove
4983 static gboolean
sipe_buddy_remove(gpointer key
, struct sipe_buddy
*buddy
, gpointer user_data
)
4985 sipe_free_buddy(buddy
);
4988 static void sipe_close(PurpleConnection
*gc
)
4990 struct sipe_account_data
*sip
= gc
->proto_data
;
4993 /* leave all conversations */
4994 im_session_close_all(sip
);
4997 do_register_exp(sip
, 0);
4999 sipe_connection_cleanup(sip
);
5000 g_free(sip
->sipdomain
);
5001 g_free(sip
->username
);
5002 g_free(sip
->password
);
5003 g_free(sip
->authdomain
);
5004 g_free(sip
->authuser
);
5005 g_free(sip
->status
);
5007 g_hash_table_foreach_remove(sip
->buddies
, (GHRFunc
) sipe_buddy_remove
, NULL
);
5008 g_hash_table_destroy(sip
->buddies
);
5010 g_free(gc
->proto_data
);
5011 gc
->proto_data
= NULL
;
5014 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
5016 PurpleAccount
*acct
= purple_connection_get_account(gc
);
5017 char *id
= g_strdup_printf("sip:%s", (char *)g_list_nth_data(row
, 0));
5018 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
5020 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
5021 purple_conversation_present(conv
);
5025 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
5028 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
5029 g_list_nth_data(row
, 0), NULL
, g_list_nth_data(row
, 1));
5032 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,struct transaction
*tc
)
5034 PurpleNotifySearchResults
*results
;
5035 PurpleNotifySearchColumn
*column
;
5036 xmlnode
*searchResults
;
5038 int match_count
= 0;
5039 gboolean more
= FALSE
;
5042 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
5044 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5045 if (!searchResults
) {
5046 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
5050 results
= purple_notify_searchresults_new();
5052 if (results
== NULL
) {
5053 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
5054 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
5056 xmlnode_free(searchResults
);
5060 column
= purple_notify_searchresults_column_new(_("User Name"));
5061 purple_notify_searchresults_column_add(results
, column
);
5063 column
= purple_notify_searchresults_column_new(_("Name"));
5064 purple_notify_searchresults_column_add(results
, column
);
5066 column
= purple_notify_searchresults_column_new(_("Company"));
5067 purple_notify_searchresults_column_add(results
, column
);
5069 column
= purple_notify_searchresults_column_new(_("Country"));
5070 purple_notify_searchresults_column_add(results
, column
);
5072 column
= purple_notify_searchresults_column_new(_("Email"));
5073 purple_notify_searchresults_column_add(results
, column
);
5075 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
5078 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
5079 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
5080 g_strfreev(uri_parts
);
5082 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
5083 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
5084 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
5085 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
5087 purple_notify_searchresults_row_add(results
, row
);
5091 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
5092 char *data
= xmlnode_get_data_unescaped(mrow
);
5093 more
= (g_strcasecmp(data
, "true") == 0);
5097 secondary
= g_strdup_printf(
5098 dngettext(GETTEXT_PACKAGE
,
5099 "Found %d contact%s:",
5100 "Found %d contacts%s:", match_count
),
5101 match_count
, more
? _(" (more matched your query)") : "");
5103 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
5104 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
5105 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
5108 xmlnode_free(searchResults
);
5112 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
5114 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
5115 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
5119 PurpleRequestField
*field
= entries
->data
;
5120 const char *id
= purple_request_field_get_id(field
);
5121 const char *value
= purple_request_field_string_get_value(field
);
5123 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
5125 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
5126 } while ((entries
= g_list_next(entries
)) != NULL
);
5130 gchar
*query
= g_strjoinv(NULL
, attrs
);
5131 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
5132 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
5133 send_soap_request_with_cb(gc
->proto_data
, body
,
5134 (TransCallback
) process_search_contact_response
, NULL
);
5142 static void sipe_show_find_contact(PurplePluginAction
*action
)
5144 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
5145 PurpleRequestFields
*fields
;
5146 PurpleRequestFieldGroup
*group
;
5147 PurpleRequestField
*field
;
5149 fields
= purple_request_fields_new();
5150 group
= purple_request_field_group_new(NULL
);
5151 purple_request_fields_add_group(fields
, group
);
5153 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
5154 purple_request_field_group_add_field(group
, field
);
5155 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
5156 purple_request_field_group_add_field(group
, field
);
5157 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
5158 purple_request_field_group_add_field(group
, field
);
5159 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
5160 purple_request_field_group_add_field(group
, field
);
5162 purple_request_fields(gc
,
5164 _("Search for a Contact"),
5165 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
5167 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
5169 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
5172 GList
*sipe_actions(PurplePlugin
*plugin
, gpointer context
)
5175 PurplePluginAction
*act
;
5177 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
5178 menu
= g_list_prepend(menu
, act
);
5180 menu
= g_list_reverse(menu
);
5185 static void dummy_permit_deny(PurpleConnection
*gc
)
5189 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
5195 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
5201 static char *sipe_status_text(PurpleBuddy
*buddy
)
5203 struct sipe_account_data
*sip
;
5204 struct sipe_buddy
*sbuddy
;
5207 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
5208 if (sip
) //happens on pidgin exit
5210 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
5211 if (sbuddy
&& sbuddy
->annotation
)
5213 text
= g_strdup(sbuddy
->annotation
);
5220 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
5222 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
5223 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
5224 struct sipe_account_data
*sip
;
5225 struct sipe_buddy
*sbuddy
;
5226 char *annotation
= NULL
;
5228 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
5229 if (sip
) //happens on pidgin exit
5231 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
5234 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
5239 if (purple_presence_is_online(presence
))
5241 purple_notify_user_info_add_pair(user_info
, _("Status"), purple_status_get_name(status
));
5246 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
5253 sipe_get_account_text_table(PurpleAccount
*account
)
5256 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
5257 g_hash_table_insert(table
, "login_label", (gpointer
)_("Sign-In Name..."));
5261 static PurpleBuddy
*
5262 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
5265 const gchar
*server_alias
, *email
;
5266 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
5268 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
5270 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
5272 server_alias
= g_strdup(purple_buddy_get_server_alias(buddy
));
5274 purple_blist_server_alias_buddy(clone
, server_alias
);
5277 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
5279 purple_blist_node_set_string((PurpleBlistNode
*)clone
, "email", email
);
5282 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
5284 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
5289 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
5291 PurpleBuddy
*buddy
, *b
;
5292 PurpleConnection
*gc
;
5293 PurpleGroup
* group
= purple_find_group(group_name
);
5295 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
5297 buddy
= (PurpleBuddy
*)node
;
5299 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
5300 gc
= purple_account_get_connection(buddy
->account
);
5302 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
5304 b
= purple_blist_add_buddy_clone(group
, buddy
);
5307 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
5311 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
5314 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
5316 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
5319 char *mailto
= g_strdup_printf("mailto:%s", email
);
5320 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
5324 char *const parmList
[] = {mailto
, NULL
};
5325 if ((pid
= fork()) == -1)
5327 purple_debug_info("sipe", "fork() error\n");
5331 execvp("xdg-email", parmList
);
5332 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
5340 //@TODO resolve env variable %WINDIR% first
5341 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
5344 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
5353 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
5358 * A menu which appear when right-clicking on buddy in contact list.
5361 sipe_buddy_menu(PurpleBuddy
*buddy
)
5363 PurpleBlistNode
*g_node
;
5364 PurpleGroup
*group
, *gr_parent
;
5365 PurpleMenuAction
*act
;
5367 GList
*menu_groups
= NULL
;
5369 act
= purple_menu_action_new(_("Send Email..."),
5370 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
5372 menu
= g_list_prepend(menu
, act
);
5374 gr_parent
= purple_buddy_get_group(buddy
);
5375 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
5376 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
5379 group
= (PurpleGroup
*)g_node
;
5380 if (group
== gr_parent
)
5383 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
5386 act
= purple_menu_action_new(purple_group_get_name(group
),
5387 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
5389 menu_groups
= g_list_prepend(menu_groups
, act
);
5391 menu_groups
= g_list_reverse(menu_groups
);
5393 act
= purple_menu_action_new(_("Copy to"),
5396 menu
= g_list_prepend(menu
, act
);
5397 menu
= g_list_reverse(menu
);
5402 GList
*sipe_blist_node_menu(PurpleBlistNode
*node
) {
5403 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
5404 return sipe_buddy_menu((PurpleBuddy
*) node
);
5411 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
5413 gboolean ret
= TRUE
;
5414 char *username
= (char *)trans
->payload
;
5416 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
5417 PurpleBuddy
*pbuddy
;
5418 struct sipe_buddy
*sbuddy
;
5420 char *server_alias
= NULL
;
5422 const char *device_name
= NULL
;
5424 purple_debug_info("sipe", "Fetching %s's user info for %s\n", username
, sip
->username
);
5426 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, username
);
5427 alias
= purple_buddy_get_local_alias(pbuddy
);
5431 //will query buddy UA's capabilities and send answer to log
5432 sipe_options_request(sip
, username
);
5434 sbuddy
= g_hash_table_lookup(sip
->buddies
, username
);
5437 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
5441 if (msg
->response
!= 200) {
5442 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
5444 xmlnode
*searchResults
;
5447 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
5448 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5449 if (!searchResults
) {
5450 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
5451 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
5452 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
5453 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
5454 purple_notify_user_info_add_pair(info
, _("Job Title"), g_strdup(xmlnode_get_attrib(mrow
, "title")));
5455 purple_notify_user_info_add_pair(info
, _("Office"), g_strdup(xmlnode_get_attrib(mrow
, "office")));
5456 purple_notify_user_info_add_pair(info
, _("Business Phone"), g_strdup(xmlnode_get_attrib(mrow
, "phone")));
5457 purple_notify_user_info_add_pair(info
, _("Company"), g_strdup(xmlnode_get_attrib(mrow
, "company")));
5458 purple_notify_user_info_add_pair(info
, _("City"), g_strdup(xmlnode_get_attrib(mrow
, "city")));
5459 purple_notify_user_info_add_pair(info
, _("State"), g_strdup(xmlnode_get_attrib(mrow
, "state")));
5460 purple_notify_user_info_add_pair(info
, _("Country"), g_strdup(xmlnode_get_attrib(mrow
, "country")));
5461 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
5462 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
5463 if (!email
|| strcmp("", email
)) {
5464 if (!purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email")) {
5465 purple_blist_node_set_string((PurpleBlistNode
*)pbuddy
, "email", email
);
5469 xmlnode_free(searchResults
);
5472 purple_notify_user_info_add_section_break(info
);
5474 if (!server_alias
|| !strcmp("", server_alias
)) {
5475 g_free(server_alias
);
5476 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
5478 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
5482 // same as server alias, do not present
5483 alias
= (alias
&& server_alias
&& !strcmp(alias
, server_alias
)) ? NULL
: alias
;
5486 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
5489 if (!email
|| !strcmp("", email
)) {
5491 email
= g_strdup(purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email"));
5493 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
5499 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
5502 /* show a buddy's user info in a nice dialog box */
5503 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
5504 username
, /* buddy's username */
5506 NULL
, /* callback called when dialog closed */
5507 NULL
); /* userdata for callback */
5513 * AD search first, LDAP based
5515 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
5517 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
5518 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
5520 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
5521 send_soap_request_with_cb((struct sipe_account_data
*)gc
->proto_data
, body
,
5522 (TransCallback
) process_get_info_response
, (gpointer
)g_strdup(username
));
5527 static PurplePlugin
*my_protocol
= NULL
;
5529 static PurplePluginProtocolInfo prpl_info
=
5532 NULL
, /* user_splits */
5533 NULL
, /* protocol_options */
5534 NO_BUDDY_ICONS
, /* icon_spec */
5535 sipe_list_icon
, /* list_icon */
5536 NULL
, /* list_emblems */
5537 sipe_status_text
, /* status_text */
5538 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
5539 sipe_status_types
, /* away_states */
5540 sipe_blist_node_menu
, /* blist_node_menu */
5541 NULL
, /* chat_info */
5542 NULL
, /* chat_info_defaults */
5543 sipe_login
, /* login */
5544 sipe_close
, /* close */
5545 sipe_im_send
, /* send_im */
5546 NULL
, /* set_info */ // TODO maybe
5547 sipe_send_typing
, /* send_typing */
5548 sipe_get_info
, /* get_info */
5549 sipe_set_status
, /* set_status */
5550 NULL
, /* set_idle */
5551 NULL
, /* change_passwd */
5552 sipe_add_buddy
, /* add_buddy */
5553 NULL
, /* add_buddies */
5554 sipe_remove_buddy
, /* remove_buddy */
5555 NULL
, /* remove_buddies */
5556 sipe_add_permit
, /* add_permit */
5557 sipe_add_deny
, /* add_deny */
5558 sipe_add_deny
, /* rem_permit */
5559 sipe_add_permit
, /* rem_deny */
5560 dummy_permit_deny
, /* set_permit_deny */
5561 NULL
, /* join_chat */
5562 NULL
, /* reject_chat */
5563 NULL
, /* get_chat_name */
5564 NULL
, /* chat_invite */
5565 NULL
, /* chat_leave */
5566 NULL
, /* chat_whisper */
5567 NULL
, /* chat_send */
5568 sipe_keep_alive
, /* keepalive */
5569 NULL
, /* register_user */
5570 NULL
, /* get_cb_info */ // deprecated
5571 NULL
, /* get_cb_away */ // deprecated
5572 sipe_alias_buddy
, /* alias_buddy */
5573 sipe_group_buddy
, /* group_buddy */
5574 sipe_rename_group
, /* rename_group */
5575 NULL
, /* buddy_free */
5576 sipe_convo_closed
, /* convo_closed */
5577 purple_normalize_nocase
, /* normalize */
5578 NULL
, /* set_buddy_icon */
5579 sipe_remove_group
, /* remove_group */
5580 NULL
, /* get_cb_real_name */ // TODO?
5581 NULL
, /* set_chat_topic */
5582 NULL
, /* find_blist_chat */
5583 NULL
, /* roomlist_get_list */
5584 NULL
, /* roomlist_cancel */
5585 NULL
, /* roomlist_expand_category */
5586 NULL
, /* can_receive_file */
5587 NULL
, /* send_file */
5588 NULL
, /* new_xfer */
5589 NULL
, /* offline_message */
5590 NULL
, /* whiteboard_prpl_ops */
5591 sipe_send_raw
, /* send_raw */
5592 NULL
, /* roomlist_room_serialize */
5593 NULL
, /* unregister_user */
5594 NULL
, /* send_attention */
5595 NULL
, /* get_attention_types */
5597 sizeof(PurplePluginProtocolInfo
), /* struct_size */
5598 sipe_get_account_text_table
, /* get_account_text_table */
5602 static PurplePluginInfo info
= {
5603 PURPLE_PLUGIN_MAGIC
,
5604 PURPLE_MAJOR_VERSION
,
5605 PURPLE_MINOR_VERSION
,
5606 PURPLE_PLUGIN_PROTOCOL
, /**< type */
5607 NULL
, /**< ui_requirement */
5609 NULL
, /**< dependencies */
5610 PURPLE_PRIORITY_DEFAULT
, /**< priority */
5611 "prpl-sipe", /**< id */
5612 "Microsoft LCS/OCS", /**< name */
5613 VERSION
, /**< version */
5614 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
5615 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
5616 "Anibal Avelar <avelar@gmail.com>, " /**< author */
5617 "Gabriel Burt <gburt@novell.com>", /**< author */
5618 PURPLE_WEBSITE
, /**< homepage */
5619 sipe_plugin_load
, /**< load */
5620 sipe_plugin_unload
, /**< unload */
5621 sipe_plugin_destroy
, /**< destroy */
5622 NULL
, /**< ui_info */
5623 &prpl_info
, /**< extra_info */
5632 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
5636 entry
= prpl_info
.protocol_options
;
5638 purple_account_option_destroy(entry
->data
);
5639 entry
= g_list_delete_link(entry
, entry
);
5641 prpl_info
.protocol_options
= NULL
;
5643 entry
= prpl_info
.user_splits
;
5645 purple_account_user_split_destroy(entry
->data
);
5646 entry
= g_list_delete_link(entry
, entry
);
5648 prpl_info
.user_splits
= NULL
;
5651 static void init_plugin(PurplePlugin
*plugin
)
5653 PurpleAccountUserSplit
*split
;
5654 PurpleAccountOption
*option
;
5657 purple_debug_info(PACKAGE
, "bindtextdomain = %s", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
5658 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s",
5659 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
5662 purple_plugin_register(plugin
);
5664 split
= purple_account_user_split_new(_("Login \n domain\\user or\n someone@linux.com "), NULL
, ',');
5665 purple_account_user_split_set_reverse(split
, FALSE
);
5666 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
5668 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
5669 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5670 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
5671 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5673 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
5674 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5675 // Translators: noun (networking port)
5676 option
= purple_account_option_int_new(_("Port"), "port", 5061);
5677 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5679 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
5680 purple_account_option_add_list_item(option
, _("Auto"), "auto");
5681 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
5682 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
5683 purple_account_option_add_list_item(option
, _("UDP"), "udp");
5684 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5686 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
5687 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
5689 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
5690 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5692 // TODO commented out so won't show in the preferences until we fix krb message signing
5693 /*option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
5694 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5696 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
5697 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
5698 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5701 my_protocol
= plugin
;
5704 /* I had to redefined the function for it load, but works */
5705 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
5706 plugin
->info
= &(info
);
5707 init_plugin((plugin
));
5708 sipe_plugin_load((plugin
));
5709 return purple_plugin_register(plugin
);