Cleaned some warnings in the code
[siplcs.git] / src / sipe.c
blob6fcbe3a2644c191f4c3c59f257bc9c24d1fa6a97
1 /**
2 * @file sipe.c
4 * pidgin-sipe
5 *
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>
11 * ***
12 * Thanks to Google's Summer of Code Program and the helpful mentors
13 * ***
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
33 #ifndef _WIN32
34 #include <sys/socket.h>
35 #include <sys/ioctl.h>
36 #include <sys/types.h>
37 #include <netinet/in.h>
38 #include <net/if.h>
39 #ifdef ENABLE_NLS
40 # include <libintl.h>
41 # define _(String) ((const char *) gettext (String))
42 #else
43 # define _(String) ((const char *) (String))
44 #endif /* ENABLE_NLS */
45 #else
46 #ifdef _DLL
47 #define _WS2TCPIP_H_
48 #define _WINSOCK2API_
49 #define _LIBC_INTERNAL_
50 #endif /* _DLL */
52 #include "internal.h"
53 #endif /* _WIN32 */
55 #include <time.h>
56 #include <stdio.h>
57 #include <errno.h>
58 #include <string.h>
59 #include <glib.h>
62 #include "accountopt.h"
63 #include "blist.h"
64 #include "conversation.h"
65 #include "dnsquery.h"
66 #include "debug.h"
67 #include "notify.h"
68 #include "privacy.h"
69 #include "prpl.h"
70 #include "plugin.h"
71 #include "util.h"
72 #include "version.h"
73 #include "network.h"
74 #include "xmlnode.h"
75 #include "mime.h"
77 #include "sipe.h"
78 #include "sip-ntlm.h"
79 #ifdef USE_KERBEROS
80 #include "sipkrb5.h"
81 #endif /*USE_KERBEROS*/
83 #include "sipmsg.h"
84 #include "sipe-sign.h"
85 #include "dnssrv.h"
86 #include "request.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)
119 if (!sip->epid) {
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);
143 if (!tag) {
144 // In case it's at the end and there's no trailing ;
145 tag = sipmsg_find_part_of_header (hdr, "tag=", NULL, NULL);
147 return tag;
151 static const char *sipe_list_icon(PurpleAccount *a, PurpleBuddy *b)
153 return "sipe";
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,
162 gpointer data);
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));
178 } else {
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)
184 #endif
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;
197 while (entry) {
198 ret = entry->data;
199 if (ret->fd == fd) return ret;
200 entry = entry->next;
202 return NULL;
205 static void sipe_auth_free(struct sip_auth *auth)
207 g_free(auth->nonce);
208 auth->nonce = NULL;
209 g_free(auth->opaque);
210 auth->opaque = NULL;
211 g_free(auth->realm);
212 auth->realm = NULL;
213 g_free(auth->target);
214 auth->target = NULL;
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;
220 auth->retries = 0;
221 auth->expires = 0;
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);
227 ret->fd = fd;
228 sip->openconns = g_slist_append(sip->openconns, ret);
229 return ret;
232 static void connection_remove(struct sipe_account_data *sip, int fd)
234 struct sip_connection *conn = connection_find(sip, fd);
235 if (conn) {
236 sip->openconns = g_slist_remove(sip->openconns, conn);
237 if (conn->inputhandler) purple_input_remove(conn->inputhandler);
238 g_free(conn->inbuf);
239 g_free(conn);
243 static void connection_free_all(struct sipe_account_data *sip)
245 struct sip_connection *ret = NULL;
246 GSList *entry = sip->openconns;
247 while (entry) {
248 ret = entry->data;
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;
258 gchar noncecount[9];
259 gchar *response;
260 gchar *ret;
261 gchar *tmp = NULL;
262 const char *authdomain = sip->authdomain;
263 const char *authuser = sip->authuser;
264 //const char *krb5_realm;
265 const char *host;
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", "");
278 } else {
279 host = sip->sipdomain;
282 /*gboolean new_auth = krb5_auth.gss_context == NULL;
283 if (new_auth) {
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;*/
294 if (!authdomain) {
295 authdomain = "";
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);
310 g_free(response);
311 return ret;
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);
316 return tmp;
319 if (auth->nc == 3 && auth->nonce && auth->ntlm_key == NULL) {
320 const gchar *ntlm_key;
321 gchar *gssapi_data;
322 #if GLIB_CHECK_VERSION(2,8,0)
323 const gchar * hostname = g_get_host_name();
324 #else
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");
330 g_strerror(errno);
331 strcpy(hostname, "localhost");
333 #endif
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);
339 g_free(gssapi_data);
340 return tmp;
343 tmp = g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth->realm, auth->target);
344 return tmp;
345 } else if (auth->type == AUTH_TYPE_KERBEROS) {
346 /* Kerberos */
347 if (auth->nc == 3) {
348 /*if (new_auth || force_reauth) {
349 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
350 if (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);
352 } else {
353 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
355 } else {
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);
363 //g_free(mic);
365 return tmp;
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);
377 g_free(response);
378 return ret;
381 static char *parse_attribute(const char *attrname, const char *source)
383 const char *tmp, *tmp2;
384 char *retval = NULL;
385 int len = strlen(attrname);
387 if (!strncmp(source, attrname, len)) {
388 tmp = source + len;
389 tmp2 = g_strstr_len(tmp, strlen(tmp), "\"");
390 if (tmp2)
391 retval = g_strndup(tmp, tmp2 - tmp);
392 else
393 retval = g_strdup(tmp);
396 return retval;
399 static void fill_auth(struct sipe_account_data *sip, gchar *hdr, struct sip_auth *auth)
401 int i = 0;
402 const char *authuser;
403 char *tmp;
404 gchar **parts;
405 //const char *krb5_realm;
406 //const char *host;
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", "");
418 } else {
419 host = sip->sipdomain;
422 authuser = sip->authuser;
424 if (!authuser || strlen(authuser) < 1) {
425 authuser = sip->username;
428 if (!hdr) {
429 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
430 return;
433 if (!g_strncasecmp(hdr, "NTLM", 4)) {
434 auth->type = AUTH_TYPE_NTLM;
435 parts = g_strsplit(hdr+5, "\", ", 0);
436 i = 0;
437 while (parts[i]) {
438 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
439 if ((tmp = parse_attribute("gssapi-data=\"", parts[i]))) {
440 g_free(auth->nonce);
441 auth->nonce = g_memdup(purple_ntlm_parse_challenge(tmp, &auth->flags), 8);
442 g_free(tmp);
444 if ((tmp = parse_attribute("targetname=\"",
445 parts[i]))) {
446 g_free(auth->target);
447 auth->target = tmp;
449 else if ((tmp = parse_attribute("realm=\"",
450 parts[i]))) {
451 g_free(auth->realm);
452 auth->realm = tmp;
454 else if ((tmp = parse_attribute("opaque=\"", parts[i]))) {
455 g_free(auth->opaque);
456 auth->opaque = tmp;
458 i++;
460 g_strfreev(parts);
461 auth->nc = 1;
462 if (!strstr(hdr, "gssapi-data")) {
463 auth->nc = 1;
464 } else {
465 auth->nc = 3;
467 return;
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);
475 i = 0;
476 while (parts[i]) {
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);*/
483 g_free(tmp);
485 if ((tmp = parse_attribute("targetname=\"", parts[i]))) {
486 g_free(auth->target);
487 auth->target = tmp;
488 } else if ((tmp = parse_attribute("realm=\"", parts[i]))) {
489 g_free(auth->realm);
490 auth->realm = tmp;
491 } else if ((tmp = parse_attribute("opaque=\"", parts[i]))) {
492 g_free(auth->opaque);
493 auth->opaque = tmp;
495 i++;
497 g_strfreev(parts);
498 auth->nc = 3;
499 return;
502 auth->type = AUTH_TYPE_DIGEST;
503 parts = g_strsplit(hdr, " ", 0);
504 while (parts[i]) {
505 if ((tmp = parse_attribute("nonce=\"", parts[i]))) {
506 g_free(auth->nonce);
507 auth->nonce = tmp;
509 else if ((tmp = parse_attribute("realm=\"", parts[i]))) {
510 g_free(auth->realm);
511 auth->realm = tmp;
513 i++;
515 g_strfreev(parts);
517 purple_debug(PURPLE_DEBUG_MISC, "sipe", "nonce: %s realm: %s\n", auth->nonce ? auth->nonce : "(null)", auth->realm ? auth->realm : "(null)");
518 if (auth->realm) {
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);
523 auth->nc = 1;
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;
531 gsize max_write;
532 gssize written;
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);
539 sip->tx_handler = 0;
541 return;
544 written = write(sip->fd, sip->txbuf->outptr, max_write);
546 if (written < 0 && errno == EAGAIN)
547 written = 0;
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"));
551 return;
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;
561 gsize max_write;
562 gssize written;
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);
569 sip->tx_handler = 0;
570 return;
574 written = purple_ssl_write(sip->gsc, sip->txbuf->outptr, max_write);
576 if (written < 0 && errno == EAGAIN)
577 written = 0;
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"));
581 return;
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))
597 if (source >= 0)
598 close(source);
599 return;
602 if (source < 0) {
603 purple_connection_error(gc, _("Could not connect"));
604 return;
607 sip = gc->proto_data;
608 sip->fd = source;
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);
630 return NULL;
633 sip = gc->proto_data;
634 sip->fd = gsc->fd;
635 sip->gsc = gsc;
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);
644 return sip;
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);
670 } else {
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");
695 } else {
696 int ret;
697 if (sip->fd < 0) {
698 sendlater(gc, buf);
699 return;
702 if (sip->tx_handler) {
703 ret = -1;
704 errno = EAGAIN;
705 } else{
706 if (sip->gsc){
707 ret = purple_ssl_write(sip->gsc, buf, writelen);
708 }else{
709 ret = write(sip->fd, buf, writelen);
713 if (ret < 0 && errno == EAGAIN)
714 ret = 0;
715 else if (ret <= 0) { /* XXX: When does this happen legitimately? */
716 sendlater(gc, buf);
717 return;
720 if (ret < writelen) {
721 if (!sip->tx_handler){
722 if (sip->gsc){
723 sip->tx_handler = purple_input_add(sip->gsc->fd, PURPLE_INPUT_WRITE, sipe_canwrite_cb_ssl, gc);
725 else{
726 sip->tx_handler = purple_input_add(sip->fd,
727 PURPLE_INPUT_WRITE, sipe_canwrite_cb,
728 gc);
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,
738 writelen - ret);
743 static int sipe_send_raw(PurpleConnection *gc, const char *buf, int len)
745 sendout_pkt(gc, buf);
746 return len;
749 static void sendout_sipmsg(struct sipe_account_data *sip, struct sipmsg *msg)
751 GSList *tmp = msg->headers;
752 gchar *name;
753 gchar *value;
754 GString *outstr = g_string_new("");
755 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n", msg->method, msg->target);
756 while (tmp) {
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)
769 gchar * buf;
770 if (sip->registrar.ntlm_key) {
771 struct sipmsg_breakdown msgbd;
772 gchar *signature_input_str;
773 msgbd.msg = msg;
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);
792 } else {
793 sipmsg_add_header_pos(msg, "Proxy-Authorization", buf, 5);
795 g_free(buf);
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);
802 g_free(buf);
803 } else {
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);
814 * unused. Needed?
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)
825 gchar *name;
826 gchar *value;
827 GString *outstr = g_string_new("");
828 struct sipe_account_data *sip = gc->proto_data;
829 gchar *contact;
830 GSList *tmp;
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);
837 g_free(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");
843 if (body) {
844 gchar len[12];
845 sprintf(len, "%" G_GSIZE_FORMAT , strlen(body));
846 sipmsg_add_header(msg, "Content-Length", len);
847 } else {
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);
860 tmp = msg->headers;
861 while (tmp) {
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);
877 g_free(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);
889 return 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)) {
901 return trans;
903 transactions = transactions->next;
906 return NULL;
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 = "";
916 char *buf;
917 struct sipmsg *msg;
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;
932 while(iter)
934 char *tmp = route;
935 route = g_strdup_printf("%sRoute: <%s>\r\n", route, (char *)iter->data);
936 g_free(tmp);
937 iter = g_slist_next(iter);
941 if (!ourtag && !dialog) {
942 ourtag = gentag();
945 if (!strcmp(method, "REGISTER")) {
946 if (sip->regcallid) {
947 g_free(callid);
948 callid = g_strdup(sip->regcallid);
949 } else {
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"
961 "CSeq: %d %s\r\n"
962 "User-Agent: %s\r\n"
963 "Call-ID: %s\r\n"
964 "%s%s"
965 "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n%s",
966 method,
967 dialog && dialog->request ? dialog->request : url,
968 TRANSPORT_DESCRIPTOR,
969 purple_network_get_my_ip(-1),
970 sip->listenport,
971 branch ? ";branch=" : "",
972 branch ? branch : "",
973 sip->username,
974 ourtag ? ";tag=" : "",
975 ourtag ? ourtag : "",
976 epid,
978 theirtag ? ";tag=" : "",
979 theirtag ? theirtag : "",
980 theirepid ? ";epid=" : "",
981 theirepid ? theirepid : "",
982 dialog ? ++dialog->cseq : ++sip->cseq,
983 method,
984 useragent,
985 callid,
986 route,
987 addh,
988 body ? strlen(body) : 0,
989 body ? body : "");
992 //printf ("parsing msg buf:\n%s\n\n", buf);
993 msg = sipmsg_parse_msg(buf);
995 g_free(buf);
996 g_free(ourtag);
997 g_free(theirtag);
998 g_free(theirepid);
999 g_free(branch);
1000 g_free(callid);
1001 g_free(route);
1002 g_free(epid);
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);
1011 g_free(buf);
1013 return trans;
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;
1026 g_free(from);
1027 g_free(contact);
1028 g_free(hdr);
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);
1041 g_free(uuid);
1042 g_free(epid);
1043 return(buf);
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);
1058 g_free(contact);
1059 g_free(expires);
1061 sip->registerstatus = 1;
1063 send_sip_request(sip->gc, "REGISTER", uri, to, hdr, "", NULL,
1064 process_register_response);
1066 g_free(hdr);
1067 g_free(uri);
1068 g_free(to);
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)
1091 gchar *from;
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 <...> */
1100 tmp2 = tmp + 1;
1101 tmp = strchr(tmp2, '>');
1102 if (tmp) {
1103 from = g_strndup(tmp2, tmp - tmp2);
1104 } else {
1105 purple_debug_info("sipe", "found < without > in From\n");
1106 return NULL;
1108 } else {
1109 tmp = strchr(tmp2, ';');
1110 if (tmp) {
1111 from = g_strndup(tmp2, tmp - tmp2);
1112 } else {
1113 from = g_strdup(tmp2);
1116 purple_debug_info("sipe", "got %s\n", from);
1117 return from;
1120 static xmlnode * xmlnode_get_descendant(xmlnode * parent, ...)
1122 va_list args;
1123 xmlnode * node = NULL;
1124 const gchar * name;
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;
1130 parent = node;
1132 va_end(args);
1134 return node;
1138 static void
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);
1143 g_free(body);
1146 static void
1147 sipe_contact_allow_deny (struct sipe_account_data *sip, const gchar * who, gboolean allow)
1149 if (allow) {
1150 purple_debug_info("sipe", "Authorizing contact %s\n", who);
1151 } else {
1152 purple_debug_info("sipe", "Blocking contact %s\n", who);
1155 sipe_contact_set_acl (sip, who, allow ? "AA" : "BD");
1158 static
1159 void sipe_auth_user_cb(void * data)
1161 struct sipe_auth_job * job = (struct sipe_auth_job *) data;
1162 if (!job) return;
1164 sipe_contact_allow_deny (job->sip, job->who, TRUE);
1165 g_free(job);
1168 static
1169 void sipe_deny_user_cb(void * data)
1171 struct sipe_auth_job * job = (struct sipe_auth_job *) data;
1172 if (!job) return;
1174 sipe_contact_allow_deny (job->sip, job->who, FALSE);
1175 g_free(job);
1178 static void
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);
1185 static void
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);
1192 /*static void
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, "");
1199 static void
1200 sipe_process_presence_wpending (struct sipe_account_data *sip, struct sipmsg * msg)
1202 xmlnode *watchers;
1203 xmlnode *watcher;
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
1218 if (remote_user) {
1219 struct sipe_auth_job * job = g_new0(struct sipe_auth_job, 1);
1220 job->who = remote_user;
1221 job->sip = sip;
1222 purple_account_request_authorization(
1223 sip->account,
1224 remote_user,
1225 NULL, // id
1226 alias,
1227 NULL, // message
1228 on_list,
1229 sipe_auth_user_cb,
1230 sipe_deny_user_cb,
1231 (void *) job);
1236 xmlnode_free(watchers);
1237 return;
1240 static void
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);
1249 if (purple_group) {
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);
1253 } else {
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;
1261 GSList *entry;
1262 if (sip == NULL) {
1263 return NULL;
1266 entry = sip->groups;
1267 while (entry) {
1268 group = entry->data;
1269 if (group->id == id) {
1270 return group;
1272 entry = entry->next;
1274 return NULL;
1277 static struct sipe_group * sipe_group_find_by_name (struct sipe_account_data *sip, gchar * name)
1279 struct sipe_group *group;
1280 GSList *entry;
1281 if (sip == NULL) {
1282 return NULL;
1285 entry = sip->groups;
1286 while (entry) {
1287 group = entry->data;
1288 if (!strcmp(group->name, name)) {
1289 return group;
1291 entry = entry->next;
1293 return NULL;
1296 static void
1297 sipe_group_rename (struct sipe_account_data *sip, struct sipe_group * group, gchar * name)
1299 gchar *body;
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);
1303 g_free(body);
1304 g_free(group->name);
1305 group->name = g_strdup(name);
1309 * Only appends if no such value already stored.
1310 * Like Set in Java.
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);
1317 return res;
1320 static int
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.
1328 static gchar *
1329 sipe_get_buddy_groups_string (struct sipe_buddy *buddy) {
1330 int i = 0;
1331 gchar *res;
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;
1335 while (entry) {
1336 struct sipe_group * group = entry->data;
1337 ids_arr[i] = g_strdup_printf("%d", group->id);
1338 entry = entry->next;
1339 i++;
1341 ids_arr[i] = NULL;
1342 res = g_strjoinv(" ", ids_arr);
1343 g_strfreev(ids_arr);
1344 return res;
1348 * Sends buddy update to server
1350 static void
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);
1358 gchar *body;
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);
1366 g_free(groups);
1367 g_free(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;
1376 xmlnode *xml;
1377 xmlnode *node;
1378 char *group_id;
1379 struct sipe_buddy *buddy;
1381 xml = xmlnode_from_str(msg->body, msg->bodylen);
1382 if (!xml) {
1383 g_free(ctx);
1384 return FALSE;
1387 node = xmlnode_get_descendant(xml, "Body", "addGroup", "groupID", NULL);
1388 if (!node) {
1389 g_free(ctx);
1390 xmlnode_free(xml);
1391 return FALSE;
1394 group_id = xmlnode_get_data(node);
1395 if (!group_id) {
1396 g_free(ctx);
1397 xmlnode_free(xml);
1398 return FALSE;
1401 group = g_new0(struct sipe_group, 1);
1402 group->id = (int)g_ascii_strtod(group_id, NULL);
1403 g_free(group_id);
1404 group->name = ctx->group_name;
1406 sipe_group_add(sip, group);
1408 buddy = g_hash_table_lookup(sip->buddies, ctx->user_name);
1409 if (buddy) {
1410 buddy->groups = slist_insert_unique_sorted(buddy->groups, group, (GCompareFunc)sipe_group_compare);
1413 sipe_group_set_user(sip, ctx->user_name);
1415 g_free(ctx);
1416 xmlnode_free(xml);
1417 return TRUE;
1419 return FALSE;
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);
1425 gchar *body;
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);
1431 g_free(body);
1435 * Data structure for scheduled actions
1437 typedef void (*Action) (struct sipe_account_data *, void *);
1439 struct scheduled_action {
1440 /**
1441 * Name of action.
1442 * Format is <Event>[<Data>...]
1443 * Example: <presence><sip:user@domain.com> or <registration>
1445 gchar *name;
1446 guint timeout_handler;
1447 gboolean repetitive;
1448 Action action;
1449 struct sipe_account_data *sip;
1450 void *payload;
1454 * A timer callback
1455 * Should return FALSE if repetitive action is not needed
1457 static gboolean sipe_scheduled_exec(struct scheduled_action *sched_action)
1459 gboolean ret;
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);
1468 return ret;
1472 * Kills action timer effectively cancelling
1473 * scheduled action
1475 * @param name of action
1477 static void sipe_cancel_scheduled_action(struct sipe_account_data *sip, const gchar *name)
1479 GSList *entry;
1481 if (!sip->timeouts || !name) return;
1483 entry = sip->timeouts;
1484 while (entry) {
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);
1495 } else {
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);
1537 return TRUE;
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);
1544 g_free(tmp);
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;
1551 if(sbuddy){
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);
1556 g_free(tmp);
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);
1571 gchar *request;
1572 gchar *content;
1573 gchar *resources_uri = g_strdup("");
1574 gchar *require = "";
1575 gchar *accept = "";
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"
1593 "</categoryList>\n"
1594 "</action>\n"
1595 "</batchSub>", sip->username, resources_uri);
1596 } else {
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);
1617 g_free(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);
1623 g_free(content);
1624 g_free(to);
1625 g_free(request);
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((char *)buddy_name, "sip:") ? g_strdup((char *)buddy_name) : g_strdup_printf("sip:%s", (char *)buddy_name);
1638 gchar *tmp = get_contact(sip);
1639 gchar *request;
1640 gchar *content;
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"
1659 "</adhocList>\n"
1660 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1661 "<category name=\"note\"/>\n"
1662 "<category name=\"state\"/>\n"
1663 "</categoryList>\n"
1664 "</action>\n"
1665 "</batchSub>", sip->username, to
1668 g_free(tmp);
1670 /* subscribe to buddy presence */
1671 send_sip_request(sip->gc, "SUBSCRIBE", to, to, request, content, NULL, process_subscribe_response);
1673 g_free(content);
1674 g_free(to);
1675 g_free(request);
1678 static void sipe_set_status(PurpleAccount *account, PurpleStatus *status)
1680 if (!purple_status_is_active(status))
1681 return;
1683 if (account->gc) {
1684 struct sipe_account_data *sip = account->gc->proto_data;
1686 if (sip) {
1687 g_free(sip->status);
1688 sip->status = g_strdup(purple_status_get_id(status));
1689 send_presence_status(sip);
1694 static void
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);
1701 static void
1702 sipe_group_buddy(PurpleConnection *gc,
1703 const char *who,
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
1716 return;
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));
1724 if (old_group) {
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);
1729 if (!new_group) {
1730 sipe_group_create(sip, g_strdup(new_group_name), g_strdup(who));
1731 } else {
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);
1748 g_free(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
1758 } else {
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);
1769 g_free(buddy);
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 : "");
1785 if (!b) return;
1787 if (group) {
1788 g = sipe_group_find_by_name(sip, group->name);
1791 if (g) {
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);
1803 if (b->name) {
1804 gchar * body = g_strdup_printf(SIPE_SOAP_DEL_CONTACT, b->name, sip->contacts_delta++);
1805 send_soap_request(sip, body);
1806 g_free(body);
1809 sipe_free_buddy(b);
1810 } else {
1811 //updates groups on server
1812 sipe_group_set_user(sip, b->name);
1817 static void
1818 sipe_rename_group(PurpleConnection *gc,
1819 const char *old_name,
1820 PurpleGroup *group,
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));
1825 if (group) {
1826 sipe_group_rename(sip, s_group, group->name);
1827 } else {
1828 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name);
1832 static void
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);
1837 if (s_group) {
1838 gchar *body;
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);
1842 g_free(body);
1844 sip->groups = g_slist_remove(sip->groups, s_group);
1845 g_free(s_group->name);
1846 g_free(s_group);
1847 } else {
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;
1857 // Online
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),
1862 NULL);
1863 types = g_list_append(types, type);
1865 // Busy
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),
1869 NULL);
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),
1876 NULL);
1877 types = g_list_append(types, type);
1879 // Be Right Back
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),
1883 NULL);
1884 types = g_list_append(types, type);
1886 // Away
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),
1890 NULL);
1891 types = g_list_append(types, type);
1893 //On The Phone
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),
1897 NULL);
1898 types = g_list_append(types, type);
1900 //Out To Lunch
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),
1904 NULL);
1905 types = g_list_append(types, type);
1907 //Appear Offline
1908 type = purple_status_type_new_full(
1909 PURPLE_STATUS_INVISIBLE, NULL, _("Appear Offline"), TRUE, TRUE, FALSE);
1910 types = g_list_append(types, type);
1912 // Offline
1913 type = purple_status_type_new_full(
1914 PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE);
1915 types = g_list_append(types, type);
1917 return types;
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;
1936 PurpleBuddy *b;
1937 PurpleGroup *g;
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));
1941 while (entry) {
1942 b = entry->data;
1943 g = purple_buddy_get_group(b);
1944 buddy = g_hash_table_lookup(sip->buddies, b->name);
1945 if(buddy) {
1946 gboolean in_sipe_groups = FALSE;
1947 GSList *entry2 = buddy->groups;
1948 while (entry2) {
1949 struct sipe_group *group = entry2->data;
1950 if (!strcmp(group->name, g->name)) {
1951 in_sipe_groups = TRUE;
1952 break;
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);
1960 } else {
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");
1973 xmlnode *item;
1974 xmlnode *isc;
1975 const gchar *contacts_delta;
1976 xmlnode *group_node;
1977 if (!tmp || strncmp(tmp, "vnd-microsoft-roaming-contacts", 30)) {
1978 return FALSE;
1981 /* Convert the contact from XML to Purple Buddies */
1982 isc = xmlnode_from_str(msg->body, len);
1983 if (!isc) {
1984 return FALSE;
1987 contacts_delta = xmlnode_get_attrib(isc, "deltaNum");
1988 if (contacts_delta) {
1989 sip->contacts_delta = (int)g_ascii_strtod(contacts_delta, NULL);
1992 /* Parse groups */
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)) {
1998 // TODO translate
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;
2011 // TODO translate
2012 group->name = g_strdup("Other Contacts");
2013 group->id = 1;
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;
2028 int i = 0;
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);
2048 if (!b){
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);
2059 if (!buddy) {
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);
2068 } else {
2069 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2070 name);
2073 i++;
2074 } // while, contact groups
2075 g_strfreev(item_groups);
2076 g_free(groups);
2077 g_free(name);
2078 g_free(buddy_name);
2079 g_free(uri);
2081 } // for, contacts
2083 xmlnode_free(isc);
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);
2092 else{
2093 g_hash_table_foreach(sip->buddies, (GHFunc)sipe_buddy_subscribe_cb, (gpointer)sip);
2095 sip->subscribed_buddies = TRUE;
2098 return 0;
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);
2116 g_free(tmp);
2118 send_sip_request(sip->gc, "SUBSCRIBE", to, to, hdr, "", NULL, process_subscribe_response);
2119 g_free(to);
2120 g_free(hdr);
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);
2135 g_free(tmp);
2137 send_sip_request(sip->gc, "SUBSCRIBE", to, to, hdr, "", NULL, process_subscribe_response);
2138 g_free(to);
2139 g_free(hdr);
2143 * Fires on deregistration event initiated by server.
2144 * [MS-SIPREGE] SIP extension.
2147 // 2007 Example
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);
2169 } else {
2170 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2171 return;
2174 if (warning != NULL) {
2175 reason = sipmsg_find_part_of_header(warning, "reason=\"", "\"", NULL);
2176 } else { // for LCS2005
2177 int error_id = 0;
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")) {
2183 error_id = 4141;
2184 reason = g_strdup(_("User disabled")); // [MS-OCER]
2185 } else if (event && !g_ascii_strcasecmp(event, "deactivated")) {
2186 error_id = 4142;
2187 reason = g_strdup(_("User moved")); // [MS-OCER]
2190 g_free(event);
2191 warning = g_strdup_printf(_("Unregistered by Server: %s."), reason ? reason : _("no reason given"));
2192 g_free(reason);
2194 sip->gc->wants_to_die = TRUE;
2195 purple_connection_error(sip->gc, warning);
2196 g_free(warning);
2200 static void sipe_process_roaming_acl(struct sipe_account_data *sip, struct sipmsg *msg)
2202 const gchar *contacts_delta;
2203 xmlnode *xml;
2205 xml = xmlnode_from_str(msg->body, msg->bodylen);
2206 if (!xml)
2208 return;
2211 contacts_delta = xmlnode_get_attrib(xml, "deltaNum");
2212 if (contacts_delta)
2214 sip->acl_delta = (int)g_ascii_strtod(contacts_delta, NULL);
2217 xmlnode_free(xml);
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)
2230 gchar *contact;
2231 gchar *to;
2232 xmlnode *xml;
2233 xmlnode *node;
2234 char *display_name = NULL;
2235 PurpleBuddy *pbuddy;
2236 const char *alias;
2237 char *uri_alias;
2238 char *uri_user;
2240 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2242 xml = xmlnode_from_str(msg->body, msg->bodylen);
2243 if (!xml) return;
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)) {
2249 const char *user;
2250 const char *acknowledged;
2251 gchar *hdr;
2252 gchar *body;
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);
2259 if(pbuddy){
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);
2267 g_free(uri_alias);
2269 g_free(uri_user);
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(
2275 "Contact: %s\r\n"
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);
2284 g_free(body);
2285 g_free(hdr);
2289 g_free(to);
2290 g_free(contact);
2291 xmlnode_free(xml);
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);
2306 g_free(tmp);
2308 send_sip_request(sip->gc, "SUBSCRIBE", to, to, hdr, "", NULL, process_subscribe_response);
2309 g_free(to);
2310 g_free(hdr);
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"
2329 "Contact: %s\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>");
2338 g_free(tmp);
2339 send_sip_request(sip->gc, "SUBSCRIBE", to, to, hdr, body, NULL, process_subscribe_response);
2340 g_free(body);
2341 g_free(to);
2342 g_free(hdr);
2346 * For 2005 version
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"
2359 "Expires: 0\r\n"
2360 "Contact: %s\r\n", tmp);
2362 g_free(tmp);
2363 send_sip_request(sip->gc, "SUBSCRIBE", to, to, hdr, NULL, NULL, process_subscribe_response);
2364 g_free(to);
2365 g_free(hdr);
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"
2386 "Expires: 0\r\n"
2387 "Contact: %s\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>");
2395 g_free(tmp);
2396 send_sip_request(sip->gc, "SUBSCRIBE", to, to, hdr, body, NULL, process_subscribe_response);
2397 g_free(body);
2398 g_free(to);
2399 g_free(hdr);
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;
2407 GSList *entry;
2408 if (sip == NULL || who == NULL) {
2409 return NULL;
2412 entry = sip->im_sessions;
2413 while (entry) {
2414 session = entry->data;
2415 if ((who != NULL && !strcmp(who, session->with))) {
2416 return session;
2418 entry = entry->next;
2420 return NULL;
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);
2426 if (!session) {
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);
2432 return session;
2435 static void im_session_destroy(struct sipe_account_data *sip, struct sip_im_session * session)
2437 struct sip_dialog *dialog = session->dialog;
2438 GSList *entry;
2440 sip->im_sessions = g_slist_remove(sip->im_sessions, session);
2442 if (dialog) {
2443 entry = dialog->routes;
2444 while (entry) {
2445 g_free(entry->data);
2446 entry = g_slist_remove(entry, entry->data);
2448 entry = dialog->supported;
2449 while (entry) {
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;
2462 while (entry) {
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);
2470 g_free(session);
2473 static gboolean
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);
2480 return FALSE;
2483 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg->body ? msg->body : "");
2485 return ret;
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);
2495 gchar *request;
2496 request = g_strdup_printf(
2497 "Accept: application/sdp\r\n"
2498 "Contact: %s\r\n", contact);
2500 g_free(contact);
2502 send_sip_request(sip->gc, "OPTIONS", to, to, request, NULL, NULL, process_options_response);
2504 g_free(to);
2505 g_free(request);
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;
2513 g_free(msg_tmp);
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") ,
2516 msg ? msg : "");
2517 purple_conv_present_error(with, sip->account, msg_tmp);
2518 g_free(msg);
2519 g_free(msg_tmp);
2522 static void sipe_im_process_queue (struct sipe_account_data * sip, struct sip_im_session * session);
2524 static gboolean
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;
2531 gchar *cseq;
2532 char *key;
2533 gchar *message;
2535 if (!session) {
2536 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2537 g_free(with);
2538 return FALSE;
2541 dialog = session->dialog;
2542 if (!dialog) {
2543 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2544 g_free(with);
2545 return FALSE;
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));
2550 g_free(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);
2558 ret = FALSE;
2559 } else {
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));
2565 g_free(key);
2566 g_free(with);
2568 sipe_im_process_queue(sip, session);
2569 return ret;
2572 static void sipe_send_message(struct sipe_account_data *sip, struct sip_im_session * session, const char *msg)
2574 gchar *hdr;
2575 gchar *fullto;
2576 gchar *tmp;
2577 char *msgformat;
2578 char *msgtext;
2579 gchar *msgr_value;
2580 gchar *msgr;
2582 if (strncmp("sip:", session->with, 4)) {
2583 fullto = g_strdup_printf("sip:%s", session->with);
2584 } else {
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);
2592 g_free(msgformat);
2593 if (msgr_value) {
2594 msgr = g_strdup_printf(";msgr=%s", msgr_value);
2595 g_free(msgr_value);
2596 } else {
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",
2605 tmp, msgr);
2606 g_free(tmp);
2607 g_free(msgr);
2609 send_sip_request(sip->gc, "MESSAGE", fullto, fullto, hdr, msgtext, session->dialog, process_message_response);
2610 g_free(msgtext);
2611 g_free(hdr);
2612 g_free(fullto);
2616 static void
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.
2623 while (entry) {
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));
2629 g_free(key);
2630 sipe_send_message(sip, session, queued_msg);
2631 entry = session->outgoing_message_queue = g_slist_remove(session->outgoing_message_queue, queued_msg);
2632 g_free(queued_msg);
2636 static void
2637 sipe_get_route_header(struct sipmsg *msg, struct sip_dialog * dialog, gboolean outgoing)
2639 GSList *hdr = msg->headers;
2640 struct siphdrelement *elem;
2641 gchar *contact;
2643 while(hdr)
2645 elem = hdr->data;
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);
2654 if (outgoing)
2656 dialog->routes = g_slist_reverse(dialog->routes);
2659 if (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);
2669 static void
2670 sipe_get_supported_header(struct sipmsg *msg, struct sip_dialog * dialog, gboolean outgoing)
2672 GSList *hdr = msg->headers;
2673 struct siphdrelement *elem;
2674 while(hdr)
2676 elem = hdr->data;
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);
2687 static void
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 // Catch a tag on the end of the To Header and get rid of it.
2708 if (strstr(dialog->theirepid, "tag=")) {
2709 dialog->theirepid = strtok(dialog->theirepid, ";");
2712 sipe_get_route_header(msg, dialog, outgoing);
2713 sipe_get_supported_header(msg, dialog, outgoing);
2717 static gboolean
2718 process_invite_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *trans)
2720 gchar *with = parse_from(sipmsg_find_header(msg, "To"));
2721 struct sip_im_session * session = find_im_session(sip, with);
2722 struct sip_dialog *dialog;
2723 char *cseq;
2724 char *key;
2725 gchar *message;
2727 if (!session) {
2728 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2729 g_free(with);
2730 return FALSE;
2733 dialog = session->dialog;
2734 if (!dialog) {
2735 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2736 g_free(with);
2737 return FALSE;
2740 sipe_parse_dialog(msg, dialog, TRUE);
2742 cseq = sipmsg_find_part_of_header(sipmsg_find_header(msg, "CSeq"), NULL, " ", NULL);
2743 key = g_strdup_printf("<%s><%d><INVITE>", dialog->callid, atoi(cseq));
2744 g_free(cseq);
2745 message = g_hash_table_lookup(session->unconfirmed_messages, key);
2747 if (msg->response != 200) {
2748 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2750 sipe_present_message_undelivered_err(with, sip, message);
2751 im_session_destroy(sip, session);
2752 g_free(with);
2753 return FALSE;
2756 dialog->cseq = 0;
2757 send_sip_request(sip->gc, "ACK", session->with, session->with, NULL, NULL, dialog, NULL);
2758 session->outgoing_invite = NULL;
2759 if(g_slist_find_custom(dialog->supported, "ms-text-format", (GCompareFunc)g_ascii_strcasecmp)) {
2760 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
2761 if (session->outgoing_message_queue) {
2762 char *queued_msg = session->outgoing_message_queue->data;
2763 session->outgoing_message_queue = g_slist_remove(session->outgoing_message_queue, queued_msg);
2764 g_free(queued_msg);
2768 sipe_im_process_queue(sip, session);
2770 g_hash_table_remove(session->unconfirmed_messages, key);
2771 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
2772 key, g_hash_table_size(session->unconfirmed_messages));
2774 g_free(key);
2775 g_free(with);
2776 return TRUE;
2780 static void sipe_invite(struct sipe_account_data *sip, struct sip_im_session *session, const gchar *msg_body)
2782 gchar *hdr;
2783 gchar *to;
2784 gchar *from;
2785 gchar *contact;
2786 gchar *body;
2787 char *msgformat;
2788 char *msgtext;
2789 char *base64_msg;
2790 char *ms_text_format;
2791 gchar *msgr_value;
2792 gchar *msgr;
2793 char *key;
2795 if (session->dialog) {
2796 purple_debug_info("sipe", "session with %s already has a dialog open\n", session->with);
2797 return;
2800 session->dialog = g_new0(struct sip_dialog, 1);
2801 session->dialog->callid = gencallid();
2803 if (!(session->dialog->ourtag)) {
2804 session->dialog->ourtag = gentag();
2808 if (strstr(session->with, "sip:")) {
2809 to = g_strdup(session->with);
2810 } else {
2811 to = g_strdup_printf("sip:%s", session->with);
2814 sipe_parse_html(msg_body, &msgformat, &msgtext);
2815 purple_debug_info("sipe", "sipe_invite: msgformat=%s", msgformat);
2817 msgr_value = sipmsg_get_msgr_string(msgformat);
2818 g_free(msgformat);
2819 msgr = "";
2820 if (msgr_value) {
2821 msgr = g_strdup_printf(";msgr=%s", msgr_value);
2822 g_free(msgr_value);
2825 base64_msg = purple_base64_encode((guchar*) msgtext, strlen(msgtext));
2826 ms_text_format = g_strdup_printf(SIPE_INVITE_TEXT, msgr, base64_msg);
2827 g_free(msgtext);
2828 g_free(msgr);
2829 g_free(base64_msg);
2831 key = g_strdup_printf("<%s><%d><INVITE>", session->dialog->callid, (session->dialog->cseq) + 1);
2832 g_hash_table_insert(session->unconfirmed_messages, g_strdup(key), g_strdup(msg_body));
2833 purple_debug_info("sipe", "sipe_im_send: added message %s to unconfirmed_messages(count=%d)\n",
2834 key, g_hash_table_size(session->unconfirmed_messages));
2835 g_free(key);
2837 contact = get_contact(sip);
2838 /* from = g_strdup_printf("sip:%s", sip->username);*/
2839 hdr = g_strdup_printf(
2840 /*"Supported: ms-delayed-accept\r\n"*/
2841 /*"Roster-Manager: <%s>\r\n"*/
2842 /*"EndPoints: <%s>, <%s>\r\n"*/
2843 /*"Supported: com.microsoft.rtc-multiparty\r\n"*/
2844 "Contact: %s\r\n%s"
2845 "Content-Type: application/sdp\r\n",
2846 contact, ms_text_format);
2847 g_free(ms_text_format);
2849 body = g_strdup_printf(
2850 "v=0\r\n"
2851 "o=- 0 0 IN IP4 %s\r\n"
2852 "s=session\r\n"
2853 "c=IN IP4 %s\r\n"
2854 "t=0 0\r\n"
2855 "m=message %d sip null\r\n"
2856 "a=accept-types:text/plain text/html image/gif "
2857 "multipart/alternative application/im-iscomposing+xml\r\n",
2858 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip->realport);
2860 session->outgoing_invite = send_sip_request(sip->gc, "INVITE",
2861 to, to, hdr, body, session->dialog, process_invite_response);
2863 g_free(to);
2864 /* g_free(from);*/
2865 g_free(body);
2866 g_free(hdr);
2867 g_free(contact);
2870 static void
2871 im_session_close (struct sipe_account_data *sip, struct sip_im_session * session)
2873 if (session) {
2874 send_sip_request(sip->gc, "BYE", session->with, session->with, NULL, NULL, session->dialog, NULL);
2875 im_session_destroy(sip, session);
2879 static void
2880 sipe_convo_closed(PurpleConnection * gc, const char *who)
2882 struct sipe_account_data *sip = gc->proto_data;
2884 purple_debug_info("sipe", "conversation with %s closed\n", who);
2885 im_session_close(sip, find_im_session(sip, who));
2888 static void
2889 im_session_close_all (struct sipe_account_data *sip)
2891 GSList *entry = sip->im_sessions;
2892 while (entry) {
2893 im_session_close (sip, entry->data);
2894 entry = sip->im_sessions;
2898 static int sipe_im_send(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags)
2900 struct sipe_account_data *sip;
2901 gchar *to;
2902 struct sip_im_session *session;
2904 sip = gc->proto_data;
2905 to = g_strdup(who);
2907 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what);
2909 session = find_or_create_im_session(sip, who);
2911 // Queue the message
2912 session->outgoing_message_queue = g_slist_append(session->outgoing_message_queue, g_strdup(what));
2914 if (session->dialog && session->dialog->callid) {
2915 sipe_im_process_queue(sip, session);
2916 } else if (!session->outgoing_invite) {
2917 // Need to send the INVITE to get the outgoing dialog setup
2918 sipe_invite(sip, session, what);
2921 g_free(to);
2922 return 1;
2925 /* End IM Session (INVITE and MESSAGE methods) */
2927 static unsigned int
2928 sipe_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
2930 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
2931 struct sip_im_session *session;
2933 if (state == PURPLE_NOT_TYPING)
2934 return 0;
2936 session = find_im_session(sip, who);
2938 if (session && session->dialog) {
2939 send_sip_request(gc, "INFO", who, who,
2940 "Content-Type: application/xml\r\n",
2941 SIPE_SEND_TYPING, session->dialog, NULL);
2943 return SIPE_TYPING_SEND_TIMEOUT;
2946 static gboolean resend_timeout(struct sipe_account_data *sip)
2948 GSList *tmp = sip->transactions;
2949 time_t currtime = time(NULL);
2950 while (tmp) {
2951 struct transaction *trans = tmp->data;
2952 tmp = tmp->next;
2953 purple_debug_info("sipe", "have open transaction age: %ld\n", currtime-trans->time);
2954 if ((currtime - trans->time > 5) && trans->retries >= 1) {
2955 /* TODO 408 */
2956 } else {
2957 if ((currtime - trans->time > 2) && trans->retries == 0) {
2958 trans->retries++;
2959 sendout_sipmsg(sip, trans->msg);
2963 return TRUE;
2966 static void do_reauthenticate_cb(struct sipe_account_data *sip, void *unused)
2968 /* register again when security token expires */
2969 /* we have to start a new authentication as the security token
2970 * is almost expired by sending a not signed REGISTER message */
2971 purple_debug_info("sipe", "do a full reauthentication\n");
2972 sipe_auth_free(&sip->registrar);
2973 sip->registerstatus = 0;
2974 do_register(sip);
2975 sip->reauthenticate_set = FALSE;
2978 static void process_incoming_message(struct sipe_account_data *sip, struct sipmsg *msg)
2980 gchar *from;
2981 gchar *contenttype;
2982 gboolean found = FALSE;
2984 from = parse_from(sipmsg_find_header(msg, "From"));
2986 if (!from) return;
2988 purple_debug_info("sipe", "got message from %s: %s\n", from, msg->body);
2990 contenttype = sipmsg_find_header(msg, "Content-Type");
2991 if (!strncmp(contenttype, "text/plain", 10) || !strncmp(contenttype, "text/html", 9)) {
2993 gchar *html = get_html_message(contenttype, msg->body);
2994 serv_got_im(sip->gc, from, html, 0, time(NULL));
2995 g_free(html);
2996 send_sip_response(sip->gc, msg, 200, "OK", NULL);
2997 found = TRUE;
2999 } else if (!strncmp(contenttype, "application/im-iscomposing+xml", 30)) {
3000 xmlnode *isc = xmlnode_from_str(msg->body, msg->bodylen);
3001 xmlnode *state;
3002 gchar *statedata;
3004 if (!isc) {
3005 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
3006 return;
3009 state = xmlnode_get_child(isc, "state");
3011 if (!state) {
3012 purple_debug_info("sipe", "process_incoming_message: no state found\n");
3013 xmlnode_free(isc);
3014 return;
3017 statedata = xmlnode_get_data(state);
3018 if (statedata) {
3019 if (strstr(statedata, "active")) serv_got_typing(sip->gc, from, 0, PURPLE_TYPING);
3020 else serv_got_typing_stopped(sip->gc, from);
3022 g_free(statedata);
3024 xmlnode_free(isc);
3025 send_sip_response(sip->gc, msg, 200, "OK", NULL);
3026 found = TRUE;
3028 if (!found) {
3029 purple_debug_info("sipe", "got unknown mime-type");
3030 send_sip_response(sip->gc, msg, 415, "Unsupported media type", NULL);
3032 g_free(from);
3035 static void process_incoming_invite(struct sipe_account_data *sip, struct sipmsg *msg)
3037 gchar *ms_text_format;
3038 gchar *from;
3039 gchar *body;
3040 gchar *newTag = gentag();
3041 struct sip_im_session *session;
3043 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg->body ? msg->body : "");
3045 // Only accept text invitations
3046 if (msg->body && !(strstr(msg->body, "m=message") || strstr(msg->body, "m=x-ms-message"))) {
3047 send_sip_response(sip->gc, msg, 501, "Not implemented", NULL);
3048 return;
3051 // TODO There *must* be a better way to clean up the To header to add a tag...
3052 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
3053 gchar *oldHeader;
3054 gchar *newHeader;
3055 oldHeader = sipmsg_find_header(msg, "To");
3056 newHeader = g_strdup_printf("%s;tag=%s", oldHeader, newTag);
3057 sipmsg_remove_header(msg, "To");
3058 sipmsg_add_header(msg, "To", newHeader);
3059 g_free(newHeader);
3061 from = parse_from(sipmsg_find_header(msg, "From"));
3062 session = find_or_create_im_session (sip, from);
3063 if (session) {
3064 if (session->dialog) {
3065 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
3066 } else {
3067 session->dialog = g_new0(struct sip_dialog, 1);
3069 sipe_parse_dialog(msg, session->dialog, FALSE);
3071 // This stuff is the same stuff that happens in the sipe_parse_dialog...
3072 //session->dialog->callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
3073 //session->dialog->ourtag = find_tag(sipmsg_find_header(msg, "To"));
3074 //session->dialog->theirtag = find_tag(sipmsg_find_header(msg, "From"));
3075 //session->dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, "From"), "epid=", NULL, NULL);
3077 if (!session->dialog->ourtag) {
3078 session->dialog->ourtag = newTag;
3079 newTag = NULL;
3082 } else {
3083 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
3085 g_free(newTag);
3087 //ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk=
3088 ms_text_format = sipmsg_find_header(msg, "ms-text-format");
3089 if (ms_text_format) {
3090 if (!strncmp(ms_text_format, "text/plain", 10) || !strncmp(ms_text_format, "text/html", 9)) {
3092 gchar *html = get_html_message(ms_text_format, NULL);
3093 if (html) {
3094 serv_got_im(sip->gc, from, html, 0, time(NULL));
3095 g_free(html);
3096 sipmsg_add_header(msg, "Supported", "ms-text-format"); // accepts message received
3100 g_free(from);
3102 sipmsg_remove_header(msg, "Ms-Conversation-ID");
3103 sipmsg_remove_header(msg, "Ms-Text-Format");
3104 sipmsg_remove_header(msg, "EndPoints");
3105 sipmsg_remove_header(msg, "User-Agent");
3106 sipmsg_remove_header(msg, "Roster-Manager");
3107 sipmsg_remove_header(msg, "P-Asserted-Identity");
3109 sipmsg_add_header(msg, "User-Agent", purple_account_get_string(sip->account, "useragent", "Purple/" VERSION));
3110 //sipmsg_add_header(msg, "Supported", "ms-renders-gif");
3112 body = g_strdup_printf(
3113 "v=0\r\n"
3114 "o=- 0 0 IN IP4 %s\r\n"
3115 "s=session\r\n"
3116 "c=IN IP4 %s\r\n"
3117 "t=0 0\r\n"
3118 "m=message %d sip sip:%s\r\n"
3119 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
3120 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
3121 sip->realport, sip->username);
3122 send_sip_response(sip->gc, msg, 200, "OK", body);
3123 g_free(body);
3126 static void process_incoming_options(struct sipe_account_data *sip, struct sipmsg *msg)
3128 gchar *body;
3130 sipmsg_remove_header(msg, "Ms-Conversation-ID");
3131 sipmsg_remove_header(msg, "EndPoints");
3132 sipmsg_remove_header(msg, "User-Agent");
3134 sipmsg_add_header(msg, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, BENOTIFY");
3135 sipmsg_add_header(msg, "User-Agent", purple_account_get_string(sip->account, "useragent", "Purple/" VERSION));
3137 body = g_strdup_printf(
3138 "v=0\r\n"
3139 "o=- 0 0 IN IP4 0.0.0.0\r\n"
3140 "s=session\r\n"
3141 "c=IN IP4 0.0.0.0\r\n"
3142 "t=0 0\r\n"
3143 "m=message %d sip sip:%s\r\n"
3144 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
3145 sip->realport, sip->username);
3146 send_sip_response(sip->gc, msg, 200, "OK", body);
3147 g_free(body);
3150 static void sipe_connection_cleanup(struct sipe_account_data *);
3151 static void create_connection(struct sipe_account_data *, gchar *, int);
3153 gboolean process_register_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
3155 gchar *tmp;
3156 const gchar *expires_header;
3157 int expires, i;
3158 GSList *hdr = msg->headers;
3159 GSList *entry;
3160 struct siphdrelement *elem;
3162 expires_header = sipmsg_find_header(msg, "Expires");
3163 expires = expires_header != NULL ? strtol(expires_header, NULL, 10) : 0;
3164 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires);
3166 switch (msg->response) {
3167 case 200:
3168 if (expires == 0) {
3169 sip->registerstatus = 0;
3170 } else {
3171 gchar *contact_hdr = NULL;
3172 gchar *gruu = NULL;
3173 gchar *epid;
3174 gchar *uuid;
3175 gchar *timeout;
3177 if (!sip->reregister_set) {
3178 gchar *action_name = g_strdup_printf("<%s>", "registration");
3179 sipe_schedule_action(action_name, expires, do_register_cb, sip, NULL);
3180 g_free(action_name);
3181 sip->reregister_set = TRUE;
3184 sip->registerstatus = 3;
3186 if (!sip->reauthenticate_set) {
3187 /* we have to reauthenticate as our security token expires
3188 after eight hours (be five minutes early) */
3189 gchar *action_name = g_strdup_printf("<%s>", "+reauthentication");
3190 guint reauth_timeout = (8 * 3600) - 360;
3191 sipe_schedule_action(action_name, reauth_timeout, do_reauthenticate_cb, sip, NULL);
3192 g_free(action_name);
3193 sip->reauthenticate_set = TRUE;
3196 purple_connection_set_state(sip->gc, PURPLE_CONNECTED);
3198 epid = get_epid(sip);
3199 uuid = generateUUIDfromEPID(epid);
3200 g_free(epid);
3202 // There can be multiple Contact headers (one per location where the user is logged in) so
3203 // make sure to only get the one for this uuid
3204 for (i = 0; (contact_hdr = sipmsg_find_header_instance (msg, "Contact", i)); i++) {
3205 gchar * valid_contact = sipmsg_find_part_of_header (contact_hdr, uuid, NULL, NULL);
3206 if (valid_contact) {
3207 gruu = sipmsg_find_part_of_header(contact_hdr, "gruu=\"", "\"", NULL);
3208 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
3209 g_free(valid_contact);
3210 break;
3211 } else {
3212 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
3215 g_free(uuid);
3217 g_free(sip->contact);
3218 if(gruu) {
3219 sip->contact = g_strdup_printf("<%s>", gruu);
3220 g_free(gruu);
3221 } else {
3222 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
3223 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);
3225 sip->msrtc_event_categories = FALSE;
3226 sip->batched_support = FALSE;
3228 while(hdr)
3230 elem = hdr->data;
3231 if (!g_ascii_strcasecmp(elem->name, "Supported")) {
3232 if (!g_ascii_strcasecmp(elem->value, "msrtc-event-categories")) {
3233 sip->msrtc_event_categories = TRUE;
3234 purple_debug(PURPLE_DEBUG_MISC, "sipe", "Supported: %s: %d\n", elem->value,sip->msrtc_event_categories);
3236 if (!g_ascii_strcasecmp(elem->value, "adhoclist")) {
3237 sip->batched_support = TRUE;
3238 purple_debug(PURPLE_DEBUG_MISC, "sipe", "Supported: %s: %d\n", elem->value,sip->batched_support);
3241 if (!g_ascii_strcasecmp(elem->name, "Allow-Events")){
3242 gchar **caps = g_strsplit(elem->value,",",0);
3243 i = 0;
3244 while (caps[i]) {
3245 sip->allow_events = g_slist_append(sip->allow_events, g_strdup(caps[i]));
3246 purple_debug(PURPLE_DEBUG_MISC, "sipe", "Allow-Events: %s\n", caps[i]);
3247 i++;
3249 g_strfreev(caps);
3251 hdr = g_slist_next(hdr);
3254 if (!sip->subscribed) { //do it just once, not every re-register
3255 if(!sip->msrtc_event_categories){ //Only for LCS2005, on OCS2007 always backs the error 504 Server time-out
3256 sipe_options_request(sip, sip->sipdomain);
3258 entry = sip->allow_events;
3259 while (entry) {
3260 tmp = entry->data;
3261 if (tmp && !g_ascii_strcasecmp(tmp, "vnd-microsoft-roaming-contacts")) {
3262 sipe_subscribe_roaming_contacts(sip, msg);
3264 if (tmp && !g_ascii_strcasecmp(tmp,"vnd-microsoft-roaming-ACL")) {
3265 sipe_subscribe_roaming_acl(sip, msg);
3267 if (tmp && !g_ascii_strcasecmp(tmp,"vnd-microsoft-roaming-self")) {
3268 sipe_subscribe_roaming_self(sip, msg);
3270 if (tmp && !g_ascii_strcasecmp(tmp,"vnd-microsoft-provisioning-v2")) {
3271 sipe_subscribe_roaming_provisioning_v2(sip, msg);
3272 } else if (tmp && !g_ascii_strcasecmp(tmp,"vnd-microsoft-provisioning")) { // LSC2005
3273 sipe_subscribe_roaming_provisioning(sip, msg);
3275 if (tmp && !g_ascii_strcasecmp(tmp,"presence.wpending")) {
3276 sipe_subscribe_presence_wpending(sip, msg);
3278 entry = entry->next;
3280 sipe_set_status(sip->account, purple_account_get_active_status(sip->account));
3281 sip->subscribed = TRUE;
3284 timeout = sipmsg_find_part_of_header(sipmsg_find_header(msg, "ms-keep-alive"),
3285 "timeout=", ";", NULL);
3286 if (timeout != NULL) {
3287 sscanf(timeout, "%u", &sip->keepalive_timeout);
3288 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
3289 sip->keepalive_timeout);
3290 g_free(timeout);
3291 } else {
3292 sip->keepalive_timeout = 300;
3295 // Should we remove the transaction here?
3296 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip->cseq);
3297 transactions_remove(sip, tc);
3299 break;
3300 case 301:
3302 gchar *redirect = parse_from(sipmsg_find_header(msg, "Contact"));
3304 if (redirect && (g_strncasecmp("sip:", redirect, 4) == 0)) {
3305 gchar **parts = g_strsplit(redirect + 4, ";", 0);
3306 gchar **tmp;
3307 gchar *hostname;
3308 int port = 0;
3309 sipe_transport_type transport = SIPE_TRANSPORT_TLS;
3310 int i = 1;
3312 tmp = g_strsplit(parts[0], ":", 0);
3313 hostname = g_strdup(tmp[0]);
3314 if (tmp[1]) port = strtoul(tmp[1], NULL, 10);
3315 g_strfreev(tmp);
3317 while (parts[i]) {
3318 tmp = g_strsplit(parts[i], "=", 0);
3319 if (tmp[1]) {
3320 if (g_strcasecmp("transport", tmp[0]) == 0) {
3321 if (g_strcasecmp("tcp", tmp[1]) == 0) {
3322 transport = SIPE_TRANSPORT_TCP;
3323 } else if (g_strcasecmp("udp", tmp[1]) == 0) {
3324 transport = SIPE_TRANSPORT_UDP;
3328 g_strfreev(tmp);
3329 i++;
3331 g_strfreev(parts);
3333 /* Close old connection */
3334 sipe_connection_cleanup(sip);
3336 /* Create new connection */
3337 sip->transport = transport;
3338 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
3339 hostname, port, TRANSPORT_DESCRIPTOR);
3340 create_connection(sip, hostname, port);
3342 g_free(redirect);
3344 break;
3345 case 401:
3346 if (sip->registerstatus != 2) {
3347 purple_debug_info("sipe", "REGISTER retries %d\n", sip->registrar.retries);
3348 if (sip->registrar.retries > 3) {
3349 sip->gc->wants_to_die = TRUE;
3350 purple_connection_error(sip->gc, _("Wrong Password"));
3351 return TRUE;
3353 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
3354 tmp = sipmsg_find_auth_header(msg, "NTLM");
3355 } else {
3356 tmp = sipmsg_find_auth_header(msg, "Kerberos");
3358 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_register_response - Auth header: %s\r\n", tmp);
3359 fill_auth(sip, tmp, &sip->registrar);
3360 sip->registerstatus = 2;
3361 if (sip->account->disconnecting) {
3362 do_register_exp(sip, 0);
3363 } else {
3364 do_register(sip);
3367 break;
3368 case 403:
3370 gchar *warning = sipmsg_find_header(msg, "Warning");
3371 if (warning != NULL) {
3372 /* Example header:
3373 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
3375 gchar **tmp = g_strsplit(warning, "\"", 0);
3376 warning = g_strdup_printf(_("You have been rejected by the server: %s"), tmp[1] ? tmp[1] : _("no reason given"));
3377 g_strfreev(tmp);
3378 } else {
3379 warning = g_strdup(_("You have been rejected by the server"));
3382 sip->gc->wants_to_die = TRUE;
3383 purple_connection_error(sip->gc, warning);
3384 g_free(warning);
3385 return TRUE;
3387 break;
3388 case 404:
3390 gchar *warning = sipmsg_find_header(msg, "ms-diagnostics");
3391 if (warning != NULL) {
3392 gchar *reason = sipmsg_find_part_of_header(warning, "reason=\"", "\"", NULL);
3393 warning = g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason ? reason : _("no reason given"));
3394 g_free(reason);
3395 } else {
3396 warning = g_strdup(_("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator"));
3399 sip->gc->wants_to_die = TRUE;
3400 purple_connection_error(sip->gc, warning);
3401 g_free(warning);
3402 return TRUE;
3404 break;
3405 case 503:
3407 gchar *warning = sipmsg_find_header(msg, "ms-diagnostics");
3408 if (warning != NULL) {
3409 gchar *reason = sipmsg_find_part_of_header(warning, "reason=\"", "\"", NULL);
3410 warning = g_strdup_printf(_("Service unavailable: %s"), reason ? reason : _("no reason given"));
3411 g_free(reason);
3412 } else {
3413 warning = g_strdup(_("Service unavailable: no reason given"));
3416 sip->gc->wants_to_die = TRUE;
3417 purple_connection_error(sip->gc, warning);
3418 g_free(warning);
3419 return TRUE;
3421 break;
3423 return TRUE;
3426 static void process_incoming_notify_rlmi(struct sipe_account_data *sip, const gchar *data, unsigned len)
3428 const char *uri;
3429 xmlnode *xn_categories;
3430 xmlnode *xn_category;
3431 xmlnode *xn_node;
3432 const char *activity = NULL;
3434 xn_categories = xmlnode_from_str(data, len);
3435 uri = xmlnode_get_attrib(xn_categories, "uri");
3437 for (xn_category = xmlnode_get_child(xn_categories, "category");
3438 xn_category ;
3439 xn_category = xmlnode_get_next_twin(xn_category) )
3441 const char *attrVar = xmlnode_get_attrib(xn_category, "name");
3443 if (!strcmp(attrVar, "note"))
3445 char *note;
3446 struct sipe_buddy *sbuddy;
3447 xn_node = xmlnode_get_child(xn_category, "note");
3448 if (!xn_node) continue;
3449 xn_node = xmlnode_get_child(xn_node, "body");
3450 if (!xn_node) continue;
3452 note = xmlnode_get_data(xn_node);
3454 if(uri){
3455 sbuddy = g_hash_table_lookup(sip->buddies, uri);
3457 if (sbuddy && note)
3459 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s),note(%s)\n",uri,note);
3460 if (sbuddy->annotation) { g_free(sbuddy->annotation); }
3461 sbuddy->annotation = g_strdup(note);
3463 if(note)
3464 g_free(note);
3466 else if(!strcmp(attrVar, "state"))
3468 char *data;
3469 int avail;
3470 xn_node = xmlnode_get_child(xn_category, "state");
3471 if (!xn_node) continue;
3472 xn_node = xmlnode_get_child(xn_node, "availability");
3473 if (!xn_node) continue;
3475 data = xmlnode_get_data(xn_node);
3476 avail = atoi(data);
3477 g_free(data);
3479 if (avail < 3000)
3480 activity = SIPE_STATUS_ID_UNKNOWN;
3481 else if (avail < 4500)
3482 activity = SIPE_STATUS_ID_AVAILABLE;
3483 else if (avail < 6000)
3484 activity = SIPE_STATUS_ID_BRB;
3485 else if (avail < 7500)
3486 activity = SIPE_STATUS_ID_ONPHONE;
3487 else if (avail < 9000)
3488 activity = SIPE_STATUS_ID_BUSY;
3489 else if (avail < 12000)
3490 activity = SIPE_STATUS_ID_DND;
3491 else if (avail < 18000)
3492 activity = SIPE_STATUS_ID_AWAY;
3493 else
3494 activity = SIPE_STATUS_ID_OFFLINE;
3497 if(activity) {
3498 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", activity);
3499 purple_prpl_got_user_status(sip->account, uri, activity, NULL);
3502 xmlnode_free(xn_categories);
3505 static void process_incoming_notify_rlmi_resub(struct sipe_account_data *sip, const gchar *data, unsigned len)
3507 xmlnode *xn_list;
3508 xmlnode *xn_resource;
3510 xn_list = xmlnode_from_str(data, len);
3512 for (xn_resource = xmlnode_get_child(xn_list, "resource");
3513 xn_resource;
3514 xn_resource = xmlnode_get_next_twin(xn_resource) )
3516 const char *uri, *state, *poolFqdn, *cid;
3517 xmlnode *xn_instance;
3519 xn_instance = xmlnode_get_child(xn_resource, "instance");
3520 if (!xn_instance) continue;
3522 uri = xmlnode_get_attrib(xn_resource, "uri");
3523 state = xmlnode_get_attrib(xn_instance, "state");
3524 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri, state);
3526 cid = xmlnode_get_attrib(xn_instance, "cid");
3527 if(cid){ //Only OCS2007
3528 uri = g_strdup(cid);
3529 purple_debug_info("sipe", "The user is from a different deployment; cid=%s\n", uri);
3532 poolFqdn = xmlnode_get_attrib(xn_instance, "poolFqdn");
3533 if(poolFqdn){ //[MS-PRES] Section 3.4.5.1.3 Processing Details
3534 uri = g_strdup(poolFqdn);
3535 purple_debug_info("sipe", "The user is from the same domain but is hosted on a different server or server pool; poolFqdn=%s\n", uri);
3539 if (strstr(state, "resubscribe")) {
3540 struct sipe_buddy *sbuddy;
3541 sipe_subscribe_presence_single(sip, (void *) uri);
3542 if(!poolFqdn){
3543 sbuddy = g_hash_table_lookup(sip->buddies, uri);
3544 if (sbuddy) {
3545 sbuddy->resubscribed = TRUE;
3551 xmlnode_free(xn_list);
3554 static void process_incoming_notify_pidf(struct sipe_account_data *sip, const gchar *data, unsigned len)
3556 const gchar *uri;
3557 gchar *getbasic;
3558 gchar *activity = NULL;
3559 xmlnode *pidf;
3560 xmlnode *basicstatus = NULL, *tuple, *status;
3561 gboolean isonline = FALSE;
3562 xmlnode *display_name_node;
3564 pidf = xmlnode_from_str(data, len);
3565 if (!pidf) {
3566 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data);
3567 return;
3570 uri = xmlnode_get_attrib(pidf, "entity");
3572 if ((tuple = xmlnode_get_child(pidf, "tuple")))
3574 if ((status = xmlnode_get_child(tuple, "status"))) {
3575 basicstatus = xmlnode_get_child(status, "basic");
3579 if (!basicstatus) {
3580 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
3581 xmlnode_free(pidf);
3582 return;
3585 getbasic = xmlnode_get_data(basicstatus);
3586 if (!getbasic) {
3587 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
3588 xmlnode_free(pidf);
3589 return;
3592 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic);
3593 if (strstr(getbasic, "open")) {
3594 isonline = TRUE;
3596 g_free(getbasic);
3598 display_name_node = xmlnode_get_child(pidf, "display-name");
3599 // updating display name if alias was just URI
3600 if (display_name_node) {
3601 GSList *buddies = purple_find_buddies(sip->account, uri); //all buddies in different groups
3602 GSList *entry = buddies;
3603 PurpleBuddy *p_buddy;
3604 char * display_name = xmlnode_get_data(display_name_node);
3606 while (entry) {
3607 const char *server_alias;
3608 char *alias;
3610 p_buddy = entry->data;
3612 alias = (char *)purple_buddy_get_alias(p_buddy);
3613 alias = alias ? g_strdup_printf("sip:%s", alias) : NULL;
3614 if (!alias || !g_ascii_strcasecmp(uri, alias)) {
3615 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri, display_name);
3616 purple_blist_alias_buddy(p_buddy, display_name);
3618 g_free(alias);
3620 server_alias = purple_buddy_get_server_alias(p_buddy);
3621 if (display_name &&
3622 ( (server_alias && strcmp(display_name, server_alias))
3623 || !server_alias || strlen(server_alias) == 0 )
3625 purple_blist_server_alias_buddy(p_buddy, display_name);
3628 entry = entry->next;
3630 g_free(display_name);
3633 if ((tuple = xmlnode_get_child(pidf, "tuple"))) {
3634 if ((status = xmlnode_get_child(tuple, "status"))) {
3635 if ((basicstatus = xmlnode_get_child(status, "activities"))) {
3636 if ((basicstatus = xmlnode_get_child(basicstatus, "activity"))) {
3637 activity = xmlnode_get_data(basicstatus);
3638 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity);
3644 if (isonline) {
3645 const gchar * status_id = NULL;
3646 if (activity) {
3647 if (strstr(activity, "busy")) {
3648 status_id = SIPE_STATUS_ID_BUSY;
3649 } else if (strstr(activity, "away")) {
3650 status_id = SIPE_STATUS_ID_AWAY;
3654 if (!status_id) {
3655 status_id = SIPE_STATUS_ID_AVAILABLE;
3658 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id);
3659 purple_prpl_got_user_status(sip->account, uri, status_id, NULL);
3660 } else {
3661 purple_prpl_got_user_status(sip->account, uri, SIPE_STATUS_ID_OFFLINE, NULL);
3664 g_free(activity);
3665 xmlnode_free(pidf);
3668 static void process_incoming_notify_msrtc(struct sipe_account_data *sip, const gchar *data, unsigned len)
3670 const char *availability;
3671 const char *activity;
3672 const char *display_name = NULL;
3673 const char *activity_name = NULL;
3674 const char *name;
3675 char *uri;
3676 int avl;
3677 int act;
3678 struct sipe_buddy *sbuddy;
3680 xmlnode *xn_presentity = xmlnode_from_str(data, len);
3682 xmlnode *xn_availability = xmlnode_get_child(xn_presentity, "availability");
3683 xmlnode *xn_activity = xmlnode_get_child(xn_presentity, "activity");
3684 xmlnode *xn_display_name = xmlnode_get_child(xn_presentity, "displayName");
3685 xmlnode *xn_email = xmlnode_get_child(xn_presentity, "email");
3686 const char *email = xn_email ? xmlnode_get_attrib(xn_email, "email") : NULL;
3687 xmlnode *xn_userinfo = xmlnode_get_child(xn_presentity, "userInfo");
3688 xmlnode *xn_state = xn_userinfo ? xmlnode_get_descendant(xn_userinfo, "states", "state", NULL):NULL;
3689 const char *avail = xn_state ? xmlnode_get_attrib(xn_state, "avail") : NULL;
3691 xmlnode *xn_note = xn_userinfo ? xmlnode_get_child(xn_userinfo, "note") : NULL;
3692 char *note = xn_note ? xmlnode_get_data(xn_note) : NULL;
3693 xmlnode *xn_devices = xmlnode_get_child(xn_presentity, "devices");
3694 xmlnode *xn_device_presence = xn_devices ? xmlnode_get_child(xn_devices, "devicePresence") : NULL;
3695 xmlnode *xn_device_name = xn_device_presence ? xmlnode_get_child(xn_device_presence, "deviceName") : NULL;
3696 const char *device_name = xn_device_name ? xmlnode_get_attrib(xn_device_name, "name") : NULL;
3698 name = xmlnode_get_attrib(xn_presentity, "uri");
3699 uri = g_strdup_printf("sip:%s", name);
3700 availability = xmlnode_get_attrib(xn_availability, "aggregate");
3701 activity = xmlnode_get_attrib(xn_activity, "aggregate");
3703 // updating display name if alias was just URI
3704 if (xn_display_name) {
3705 GSList *buddies = purple_find_buddies(sip->account, uri); //all buddies in different groups
3706 GSList *entry = buddies;
3707 PurpleBuddy *p_buddy;
3708 display_name = xmlnode_get_attrib(xn_display_name, "displayName");
3710 while (entry) {
3711 const char *email_str, *server_alias;
3713 p_buddy = entry->data;
3715 if (!g_ascii_strcasecmp(name, purple_buddy_get_alias(p_buddy))) {
3716 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri, display_name);
3717 purple_blist_alias_buddy(p_buddy, display_name);
3720 server_alias = purple_buddy_get_server_alias(p_buddy);
3721 if (display_name &&
3722 ( (server_alias && strcmp(display_name, server_alias))
3723 || !server_alias || strlen(server_alias) == 0 )
3725 purple_blist_server_alias_buddy(p_buddy, display_name);
3728 if (email) {
3729 email_str = purple_blist_node_get_string((PurpleBlistNode *)p_buddy, "email");
3730 if (!email_str || g_ascii_strcasecmp(email_str, email)) {
3731 purple_blist_node_set_string((PurpleBlistNode *)p_buddy, "email", email);
3735 entry = entry->next;
3739 avl = atoi(availability);
3740 act = atoi(activity);
3742 if(sip->msrtc_event_categories){
3743 if (act == 100 && avl == 0)
3744 activity_name = SIPE_STATUS_ID_OFFLINE;
3745 else if (act == 100 && avl == 300)
3746 activity_name = SIPE_STATUS_ID_AWAY;
3747 else if (act == 300 && avl == 300)
3748 activity_name = SIPE_STATUS_ID_BRB;
3749 else if (act == 400 && avl == 300)
3750 activity_name = SIPE_STATUS_ID_AVAILABLE;
3751 else if (act == 500 && act == 300)
3752 activity_name = SIPE_STATUS_ID_ONPHONE;
3753 else if (act == 600 && avl == 300)
3754 activity_name = SIPE_STATUS_ID_BUSY;
3755 else if (act == 0 && avl == 0){ //MSRTC elements are zero
3756 if(avail){ //Check for LegacyInterop elements
3757 avl = atoi(avail);
3758 if(avl == 18500)
3759 activity_name = SIPE_STATUS_ID_OFFLINE;
3760 else if (avl == 3500)
3761 activity_name = SIPE_STATUS_ID_AVAILABLE;
3762 else if (avl == 15500)
3763 activity_name = SIPE_STATUS_ID_AWAY;
3764 else if (avl == 6500)
3765 activity_name = SIPE_STATUS_ID_BUSY;
3766 else if (avl == 12500)
3767 activity_name = SIPE_STATUS_ID_BRB;
3772 if(activity_name == NULL){
3773 if (act <= 100)
3774 activity_name = SIPE_STATUS_ID_AWAY;
3775 else if (act <= 150)
3776 activity_name = SIPE_STATUS_ID_LUNCH;
3777 else if (act <= 300)
3778 activity_name = SIPE_STATUS_ID_BRB;
3779 else if (act <= 400)
3780 activity_name = SIPE_STATUS_ID_AVAILABLE;
3781 else if (act <= 500)
3782 activity_name = SIPE_STATUS_ID_ONPHONE;
3783 else if (act <= 600)
3784 activity_name = SIPE_STATUS_ID_BUSY;
3785 else
3786 activity_name = SIPE_STATUS_ID_AVAILABLE;
3788 if (avl == 0)
3789 activity_name = SIPE_STATUS_ID_OFFLINE;
3792 sbuddy = g_hash_table_lookup(sip->buddies, uri);
3793 if (sbuddy)
3795 if (sbuddy->annotation) { g_free(sbuddy->annotation); }
3796 sbuddy->annotation = NULL;
3797 if (note) { sbuddy->annotation = g_strdup(note); }
3799 if (sbuddy->device_name) { g_free(sbuddy->device_name); }
3800 sbuddy->device_name = NULL;
3801 if (device_name) { sbuddy->device_name = g_strdup(device_name); }
3804 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name);
3805 purple_prpl_got_user_status(sip->account, uri, activity_name, NULL);
3806 g_free(note);
3807 xmlnode_free(xn_presentity);
3808 g_free(uri);
3811 static void sipe_process_presence(struct sipe_account_data *sip, struct sipmsg *msg)
3813 char *ctype = sipmsg_find_header(msg, "Content-Type");
3815 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype ? ctype : "");
3817 if ( ctype && ( strstr(ctype, "application/rlmi+xml")
3818 || strstr(ctype, "application/msrtc-event-categories+xml") ) )
3820 const char *content = msg->body;
3821 unsigned length = msg->bodylen;
3822 PurpleMimeDocument *mime = NULL;
3824 if (strstr(ctype, "multipart"))
3826 char *doc = g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype, msg->body);
3827 const char *content_type;
3828 GList* parts;
3829 mime = purple_mime_document_parse(doc);
3830 parts = purple_mime_document_get_parts(mime);
3831 while(parts) {
3832 content = purple_mime_part_get_data(parts->data);
3833 length = purple_mime_part_get_length(parts->data);
3834 content_type =purple_mime_part_get_field(parts->data,"Content-Type");
3835 if(content_type && strstr(content_type,"application/rlmi+xml"))
3837 process_incoming_notify_rlmi_resub(sip, content, length);
3839 else if(content_type && strstr(content_type, "text/xml+msrtc.pidf"))
3841 process_incoming_notify_msrtc(sip, content, length);
3843 else
3845 process_incoming_notify_rlmi(sip, content, length);
3847 parts = parts->next;
3849 g_free(doc);
3851 if (mime)
3853 purple_mime_document_free(mime);
3856 else if(strstr(ctype, "application/msrtc-event-categories+xml") )
3858 process_incoming_notify_rlmi(sip, msg->body, msg->bodylen);
3860 else if(strstr(ctype, "application/rlmi+xml"))
3862 process_incoming_notify_rlmi_resub(sip, msg->body, msg->bodylen);
3865 else if(ctype && strstr(ctype, "text/xml+msrtc.pidf"))
3867 process_incoming_notify_msrtc(sip, msg->body, msg->bodylen);
3869 else
3871 process_incoming_notify_pidf(sip, msg->body, msg->bodylen);
3876 * Dispatcher for all incoming subscription information
3877 * whether it comes from NOTIFY, BENOTIFY requests or
3878 * piggy-backed to subscription's OK responce.
3880 * @param request whether initiated from BE/NOTIFY request or OK-response message.
3881 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
3883 static void process_incoming_notify(struct sipe_account_data *sip, struct sipmsg *msg, gboolean request, gboolean benotify)
3885 gchar *event = sipmsg_find_header(msg, "Event");
3886 gchar *subscription_state = sipmsg_find_header(msg, "subscription-state");
3887 int timeout = 0;
3889 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event ? event : "", msg->body);
3890 purple_debug_info("sipe", "process_incoming_notify: subscription_state:%s\n\n", subscription_state);
3892 if (!request)
3894 const gchar *expires_header;
3895 expires_header = sipmsg_find_header(msg, "Expires");
3896 timeout = expires_header ? strtol(expires_header, NULL, 10) : 0;
3897 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n\n", timeout);
3898 timeout = (timeout - 60) > 60 ? (timeout - 60) : timeout; // 1 min ahead of expiration
3901 if (!subscription_state || strstr(subscription_state, "active"))
3903 if (event && !g_ascii_strcasecmp(event, "presence"))
3905 sipe_process_presence(sip, msg);
3907 else if (event && !g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts"))
3909 sipe_process_roaming_contacts(sip, msg, NULL);
3911 else if (event && !g_ascii_strcasecmp(event, "vnd-microsoft-roaming-self") )
3913 sipe_process_roaming_self(sip, msg);
3915 else if (event && !g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL"))
3917 sipe_process_roaming_acl(sip, msg);
3919 else if (event && !g_ascii_strcasecmp(event, "presence.wpending"))
3921 sipe_process_presence_wpending(sip, msg);
3923 else
3925 purple_debug_info("sipe", "Unable to process (BE)NOTIFY. Event is not supported:%s\n", event ? event : "");
3929 //The server sends a (BE)NOTIFY with the status 'terminated'
3930 if (request && subscription_state && strstr(subscription_state, "terminated") ) {
3931 gchar *from = parse_from(sipmsg_find_header(msg, "From"));
3932 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from);
3933 g_free(from);
3936 if (timeout && event) {// For LSC 2005 and OCS 2007
3937 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
3938 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
3940 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
3941 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_contacts, sip, msg);
3942 g_free(action_name);
3944 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
3945 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
3947 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
3948 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_acl, sip, msg);
3949 g_free(action_name);
3951 else*/
3952 if (!g_ascii_strcasecmp(event, "presence.wpending") &&
3953 g_slist_find_custom(sip->allow_events, "presence.wpending", (GCompareFunc)g_ascii_strcasecmp))
3955 gchar *action_name = g_strdup_printf("<%s>", "presence.wpending");
3956 sipe_schedule_action(action_name, timeout, sipe_subscribe_presence_wpending, sip, NULL);
3957 g_free(action_name);
3959 else if (!g_ascii_strcasecmp(event, "presence") &&
3960 g_slist_find_custom(sip->allow_events, "presence", (GCompareFunc)g_ascii_strcasecmp))
3962 gchar *who = parse_from(sipmsg_find_header(msg, request ? "From" : "To"));
3963 gchar *action_name = g_strdup_printf(ACTION_NAME_PRESENCE, who);
3964 if(sip->batched_support) {
3965 gchar *my_self = g_strdup_printf("sip:%s",sip->username);
3966 if(!g_ascii_strcasecmp(who, my_self)){
3967 sipe_schedule_action(action_name, timeout, sipe_subscribe_presence_batched, sip, NULL);
3968 purple_debug_info("sipe", "Resubscription full batched list in %d\n",timeout);
3969 g_free(who); /* unused */
3971 else {
3972 sipe_schedule_action(action_name, timeout, sipe_subscribe_presence_single, sip, who);
3973 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who,timeout);
3975 g_free(my_self);
3977 else {
3978 sipe_schedule_action(action_name, timeout, sipe_subscribe_presence_single, sip, who);
3979 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who,timeout);
3981 g_free(action_name);
3982 /* "who" will be freed by the action we just scheduled */
3986 if (event && !g_ascii_strcasecmp(event, "registration-notify"))
3988 sipe_process_registration_notify(sip, msg);
3991 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
3992 if (request && !benotify)
3994 sipmsg_remove_header(msg, "Expires");
3995 sipmsg_remove_header(msg, "subscription-state");
3996 sipmsg_remove_header(msg, "Event");
3997 sipmsg_remove_header(msg, "Require");
3998 send_sip_response(sip->gc, msg, 200, "OK", NULL);
4003 * unused. Needed?
4005 static gchar* gen_xpidf(struct sipe_account_data *sip)
4007 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4008 "<presence>\r\n"
4009 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
4010 "<display name=\"sip:%s\"/>\r\n"
4011 "<atom id=\"1234\">\r\n"
4012 "<address uri=\"sip:%s\">\r\n"
4013 "<status status=\"%s\"/>\r\n"
4014 "</address>\r\n"
4015 "</atom>\r\n"
4016 "</presence>\r\n",
4017 sip->username,
4018 sip->username,
4019 sip->username,
4020 sip->status);
4021 return doc;
4026 static gchar* gen_pidf(struct sipe_account_data *sip)
4028 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4029 "<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"
4030 "<tuple id=\"0\">\r\n"
4031 "<status>\r\n"
4032 "<basic>open</basic>\r\n"
4033 "<ep:activities>\r\n"
4034 " <ep:activity>%s</ep:activity>\r\n"
4035 "</ep:activities>"
4036 "</status>\r\n"
4037 "</tuple>\r\n"
4038 "<ci:display-name>%s</ci:display-name>\r\n"
4039 "</presence>",
4040 sip->username,
4041 sip->status,
4042 sip->username);
4043 return doc;
4047 static void send_presence_soap(struct sipe_account_data *sip, const char * note)
4049 int availability = 300; // online
4050 int activity = 400; // Available
4051 gchar *name;
4052 gchar *body;
4053 if (!strcmp(sip->status, SIPE_STATUS_ID_AWAY)) {
4054 activity = 100;
4055 } else if (!strcmp(sip->status, SIPE_STATUS_ID_LUNCH)) {
4056 activity = 150;
4057 } else if (!strcmp(sip->status, SIPE_STATUS_ID_BRB)) {
4058 activity = 300;
4059 } else if (!strcmp(sip->status, SIPE_STATUS_ID_AVAILABLE)) {
4060 activity = 400;
4061 } else if (!strcmp(sip->status, SIPE_STATUS_ID_ONPHONE)) {
4062 activity = 500;
4063 } else if (!strcmp(sip->status, SIPE_STATUS_ID_BUSY)) {
4064 activity = 600;
4065 } else if (!strcmp(sip->status, SIPE_STATUS_ID_INVISIBLE) ||
4066 !strcmp(sip->status, SIPE_STATUS_ID_OFFLINE)) {
4067 availability = 0; // offline
4068 activity = 100;
4069 } else {
4070 activity = 400; // available
4073 name = g_strdup_printf("sip: sip:%s", sip->username);
4074 //@TODO: send user data - state; add hostname in upper case
4075 body = g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE, name, availability, activity, note ? note : "");
4076 send_soap_request_with_cb(sip, body, NULL , NULL);
4077 g_free(name);
4078 g_free(body);
4081 static gboolean
4082 process_clear_presence_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
4084 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4085 if (msg->response == 200) {
4086 sip->status_version = 0;
4087 send_presence_status(sip);
4089 return TRUE;
4092 static gboolean
4093 process_send_presence_category_publish_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
4095 if (msg->response == 409) {
4096 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4097 // TODO need to parse the version #'s?
4098 gchar *uri = g_strdup_printf("sip:%s", sip->username);
4099 gchar *doc = g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE, uri);
4100 gchar *tmp;
4101 gchar *hdr;
4103 purple_debug_info("sipe", "process_send_presence_category_publish_response = %s\n", msg->body);
4105 tmp = get_contact(sip);
4106 hdr = g_strdup_printf("Contact: %s\r\n"
4107 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
4109 send_sip_request(sip->gc, "SERVICE", uri, uri, hdr, doc, NULL, process_clear_presence_response);
4111 g_free(tmp);
4112 g_free(hdr);
4113 g_free(uri);
4114 g_free(doc);
4116 return TRUE;
4119 static void send_presence_category_publish(struct sipe_account_data *sip, const char * note)
4121 int code;
4122 gchar *uri;
4123 gchar *doc;
4124 gchar *tmp;
4125 gchar *hdr;
4126 if (!strcmp(sip->status, SIPE_STATUS_ID_AWAY) ||
4127 !strcmp(sip->status, SIPE_STATUS_ID_LUNCH)) {
4128 code = 12000;
4129 } else if (!strcmp(sip->status, SIPE_STATUS_ID_DND)) {
4130 code = 9000;
4131 } else if (!strcmp(sip->status, SIPE_STATUS_ID_BUSY)) {
4132 code = 7500;
4133 } else if (!strcmp(sip->status, SIPE_STATUS_ID_ONPHONE)) {
4134 code = 6000;
4135 } else if (!strcmp(sip->status, SIPE_STATUS_ID_BRB)) {
4136 code = 4500;
4137 } else if (!strcmp(sip->status, SIPE_STATUS_ID_AVAILABLE)) {
4138 code = 3000;
4139 } else if (!strcmp(sip->status, SIPE_STATUS_ID_UNKNOWN)) {
4140 code = 0;
4141 } else {
4142 // Offline or invisible
4143 code = 18000;
4146 uri = g_strdup_printf("sip:%s", sip->username);
4147 doc = g_strdup_printf(SIPE_SEND_PRESENCE, uri,
4148 sip->status_version, code,
4149 sip->status_version, code,
4150 sip->status_version, note ? note : "",
4151 sip->status_version, note ? note : "",
4152 sip->status_version, note ? note : ""
4154 sip->status_version++;
4156 tmp = get_contact(sip);
4157 hdr = g_strdup_printf("Contact: %s\r\n"
4158 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
4160 send_sip_request(sip->gc, "SERVICE", uri, uri, hdr, doc, NULL, process_send_presence_category_publish_response);
4162 g_free(tmp);
4163 g_free(hdr);
4164 g_free(uri);
4165 g_free(doc);
4168 static void send_presence_status(struct sipe_account_data *sip)
4170 PurpleStatus * status = purple_account_get_active_status(sip->account);
4171 const gchar *note;
4172 if (!status) return;
4174 note = purple_status_get_attr_string(status, "message");
4176 if(sip->msrtc_event_categories){
4177 send_presence_category_publish(sip, note);
4178 } else {
4179 send_presence_soap(sip, note);
4183 static void process_input_message(struct sipe_account_data *sip,struct sipmsg *msg)
4185 gboolean found = FALSE;
4186 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg->response,msg->method);
4187 if (msg->response == 0) { /* request */
4188 if (!strcmp(msg->method, "MESSAGE")) {
4189 process_incoming_message(sip, msg);
4190 found = TRUE;
4191 } else if (!strcmp(msg->method, "NOTIFY")) {
4192 purple_debug_info("sipe","send->process_incoming_notify\n");
4193 process_incoming_notify(sip, msg, TRUE, FALSE);
4194 found = TRUE;
4195 } else if (!strcmp(msg->method, "BENOTIFY")) {
4196 purple_debug_info("sipe","send->process_incoming_benotify\n");
4197 process_incoming_notify(sip, msg, TRUE, TRUE);
4198 found = TRUE;
4199 } else if (!strcmp(msg->method, "INVITE")) {
4200 process_incoming_invite(sip, msg);
4201 found = TRUE;
4202 } else if (!strcmp(msg->method, "OPTIONS")) {
4203 process_incoming_options(sip, msg);
4204 found = TRUE;
4205 } else if (!strcmp(msg->method, "INFO")) {
4206 // TODO needs work
4207 gchar *from = parse_from(sipmsg_find_header(msg, "From"));
4208 if (from) {
4209 serv_got_typing(sip->gc, from, SIPE_TYPING_RECV_TIMEOUT, PURPLE_TYPING);
4211 g_free(from);
4212 send_sip_response(sip->gc, msg, 200, "OK", NULL);
4213 found = TRUE;
4214 } else if (!strcmp(msg->method, "ACK")) {
4215 // ACK's don't need any response
4216 found = TRUE;
4217 } else if (!strcmp(msg->method, "SUBSCRIBE")) {
4218 // LCS 2005 sends us these - just respond 200 OK
4219 found = TRUE;
4220 send_sip_response(sip->gc, msg, 200, "OK", NULL);
4221 } else if (!strcmp(msg->method, "BYE")) {
4222 struct sip_im_session *session;
4223 gchar *from;
4224 send_sip_response(sip->gc, msg, 200, "OK", NULL);
4226 from = parse_from(sipmsg_find_header(msg, "From"));
4227 session = find_im_session (sip, from);
4228 g_free(from);
4230 if (session) {
4231 // TODO Let the user know the other user left the conversation?
4232 im_session_destroy(sip, session);
4235 found = TRUE;
4236 } else {
4237 send_sip_response(sip->gc, msg, 501, "Not implemented", NULL);
4239 } else { /* response */
4240 struct transaction *trans = transactions_find(sip, msg);
4241 if (trans) {
4242 if (msg->response == 407) {
4243 gchar *resend, *auth, *ptmp;
4245 if (sip->proxy.retries > 30) return;
4246 sip->proxy.retries++;
4247 /* do proxy authentication */
4249 ptmp = sipmsg_find_header(msg, "Proxy-Authenticate");
4251 fill_auth(sip, ptmp, &sip->proxy);
4252 auth = auth_header(sip, &sip->proxy, trans->msg);
4253 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
4254 sipmsg_add_header_pos(trans->msg, "Proxy-Authorization", auth, 5);
4255 g_free(auth);
4256 resend = sipmsg_to_string(trans->msg);
4257 /* resend request */
4258 sendout_pkt(sip->gc, resend);
4259 g_free(resend);
4260 } else {
4261 if (msg->response == 100 || msg->response == 180) {
4262 /* ignore provisional response */
4263 purple_debug_info("sipe", "got trying (%d) response\n", msg->response);
4264 } else {
4265 sip->proxy.retries = 0;
4266 if (!strcmp(trans->msg->method, "REGISTER")) {
4267 if (msg->response == 401)
4269 sip->registrar.retries++;
4270 sip->registrar.expires = 0;
4272 else
4274 sip->registrar.retries = 0;
4276 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip->cseq);
4277 } else {
4278 if (msg->response == 401) {
4279 gchar *resend, *auth, *ptmp;
4281 if (sip->registrar.retries > 4) return;
4282 sip->registrar.retries++;
4284 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
4285 ptmp = sipmsg_find_auth_header(msg, "NTLM");
4286 } else {
4287 ptmp = sipmsg_find_auth_header(msg, "Kerberos");
4290 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - Auth header: %s\r\n", ptmp);
4292 fill_auth(sip, ptmp, &sip->registrar);
4293 auth = auth_header(sip, &sip->registrar, trans->msg);
4294 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
4295 sipmsg_add_header(trans->msg, "Proxy-Authorization", auth);
4297 //sipmsg_remove_header(trans->msg, "Authorization");
4298 //sipmsg_add_header(trans->msg, "Authorization", auth);
4299 g_free(auth);
4300 resend = sipmsg_to_string(trans->msg);
4301 /* resend request */
4302 sendout_pkt(sip->gc, resend);
4303 g_free(resend);
4307 if (trans->callback) {
4308 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - we have a transaction callback\r\n");
4309 /* call the callback to process response*/
4310 (trans->callback)(sip, msg, trans);
4312 /* Not sure if this is needed or what needs to be done
4313 but transactions seem to be removed prematurely so
4314 this only removes them if the response is 200 OK */
4315 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - removing CSeq %d\r\n", sip->cseq);
4316 /*Has a bug and it's unneccesary*/
4317 /*transactions_remove(sip, trans);*/
4321 found = TRUE;
4322 } else {
4323 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received response to unknown transaction\n");
4326 if (!found) {
4327 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received a unknown sip message with method %s and response %d\n", msg->method, msg->response);
4331 static void process_input(struct sipe_account_data *sip, struct sip_connection *conn)
4333 char *cur;
4334 char *dummy;
4335 struct sipmsg *msg;
4336 int restlen;
4337 cur = conn->inbuf;
4339 /* according to the RFC remove CRLF at the beginning */
4340 while (*cur == '\r' || *cur == '\n') {
4341 cur++;
4343 if (cur != conn->inbuf) {
4344 memmove(conn->inbuf, cur, conn->inbufused - (cur - conn->inbuf));
4345 conn->inbufused = strlen(conn->inbuf);
4348 /* Received a full Header? */
4349 sip->processing_input = TRUE;
4350 while (sip->processing_input &&
4351 ((cur = strstr(conn->inbuf, "\r\n\r\n")) != NULL)) {
4352 time_t currtime = time(NULL);
4353 cur += 2;
4354 cur[0] = '\0';
4355 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), conn->inbuf);
4356 msg = sipmsg_parse_header(conn->inbuf);
4357 cur[0] = '\r';
4358 cur += 2;
4359 restlen = conn->inbufused - (cur - conn->inbuf);
4360 if (restlen >= msg->bodylen) {
4361 dummy = g_malloc(msg->bodylen + 1);
4362 memcpy(dummy, cur, msg->bodylen);
4363 dummy[msg->bodylen] = '\0';
4364 msg->body = dummy;
4365 cur += msg->bodylen;
4366 memmove(conn->inbuf, cur, conn->inbuflen - (cur - conn->inbuf));
4367 conn->inbufused = strlen(conn->inbuf);
4368 } else {
4369 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
4370 restlen, msg->bodylen, (int)strlen(conn->inbuf));
4371 sipmsg_free(msg);
4372 return;
4375 /*if (msg->body) {
4376 purple_debug_info("sipe", "body:\n%s", msg->body);
4379 // Verify the signature before processing it
4380 if (sip->registrar.ntlm_key) {
4381 struct sipmsg_breakdown msgbd;
4382 gchar *signature_input_str;
4383 gchar *signature = NULL;
4384 gchar *rspauth;
4385 msgbd.msg = msg;
4386 sipmsg_breakdown_parse(&msgbd, sip->registrar.realm, sip->registrar.target);
4387 signature_input_str = sipmsg_breakdown_get_string(&msgbd);
4388 if (signature_input_str != NULL) {
4389 signature = purple_ntlm_sipe_signature_make (signature_input_str, sip->registrar.ntlm_key);
4391 g_free(signature_input_str);
4393 rspauth = sipmsg_find_part_of_header(sipmsg_find_header(msg, "Authentication-Info"), "rspauth=\"", "\"", NULL);
4395 if (signature != NULL) {
4396 if (rspauth != NULL) {
4397 if (purple_ntlm_verify_signature (signature, rspauth)) {
4398 purple_debug(PURPLE_DEBUG_MISC, "sipe", "incoming message's signature validated\n");
4399 process_input_message(sip, msg);
4400 } else {
4401 purple_debug(PURPLE_DEBUG_MISC, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth, signature);
4402 purple_connection_error(sip->gc, _("Invalid message signature received"));
4403 sip->gc->wants_to_die = TRUE;
4405 } else if (msg->response == 401) {
4406 purple_connection_error(sip->gc, _("Wrong Password"));
4407 sip->gc->wants_to_die = TRUE;
4409 g_free(signature);
4412 g_free(rspauth);
4413 sipmsg_breakdown_free(&msgbd);
4414 } else {
4415 process_input_message(sip, msg);
4418 sipmsg_free(msg);
4422 static void sipe_udp_process(gpointer data, gint source, PurpleInputCondition con)
4424 PurpleConnection *gc = data;
4425 struct sipe_account_data *sip = gc->proto_data;
4426 struct sipmsg *msg;
4427 int len;
4428 time_t currtime;
4430 static char buffer[65536];
4431 if ((len = recv(source, buffer, sizeof(buffer) - 1, 0)) > 0) {
4432 buffer[len] = '\0';
4433 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), buffer);
4434 msg = sipmsg_parse_msg(buffer);
4435 if (msg) process_input_message(sip, msg);
4439 static void sipe_invalidate_ssl_connection(PurpleConnection *gc, const char *msg, const char *debug)
4441 struct sipe_account_data *sip = gc->proto_data;
4442 PurpleSslConnection *gsc = sip->gsc;
4444 purple_debug_error("sipe", "%s",debug);
4445 purple_connection_error(gc, msg);
4447 /* Invalidate this connection. Next send will open a new one */
4448 if (gsc) {
4449 connection_remove(sip, gsc->fd);
4450 purple_ssl_close(gsc);
4452 sip->gsc = NULL;
4453 sip->fd = -1;
4456 static void sipe_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
4458 PurpleConnection *gc = data;
4459 struct sipe_account_data *sip;
4460 struct sip_connection *conn;
4461 int readlen, len;
4462 gboolean firstread = TRUE;
4464 /* NOTE: This check *IS* necessary */
4465 if (!PURPLE_CONNECTION_IS_VALID(gc)) {
4466 purple_ssl_close(gsc);
4467 return;
4470 sip = gc->proto_data;
4471 conn = connection_find(sip, gsc->fd);
4472 if (conn == NULL) {
4473 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
4474 gc->wants_to_die = TRUE;
4475 purple_connection_error(gc, _("Connection not found; Please try to connect again.\n"));
4476 return;
4479 /* Read all available data from the SSL connection */
4480 do {
4481 /* Increase input buffer size as needed */
4482 if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
4483 conn->inbuflen += SIMPLE_BUF_INC;
4484 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
4485 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn->inbuflen);
4488 /* Try to read as much as there is space left in the buffer */
4489 readlen = conn->inbuflen - conn->inbufused - 1;
4490 len = purple_ssl_read(gsc, conn->inbuf + conn->inbufused, readlen);
4492 if (len < 0 && errno == EAGAIN) {
4493 /* Try again later */
4494 return;
4495 } else if (len < 0) {
4496 sipe_invalidate_ssl_connection(gc, _("SSL read error"), "SSL read error\n");
4497 return;
4498 } else if (firstread && (len == 0)) {
4499 sipe_invalidate_ssl_connection(gc, _("Server has disconnected"), "Server has disconnected\n");
4500 return;
4503 conn->inbufused += len;
4504 firstread = FALSE;
4506 /* Equivalence indicates that there is possibly more data to read */
4507 } while (len == readlen);
4509 conn->inbuf[conn->inbufused] = '\0';
4510 process_input(sip, conn);
4514 static void sipe_input_cb(gpointer data, gint source, PurpleInputCondition cond)
4516 PurpleConnection *gc = data;
4517 struct sipe_account_data *sip = gc->proto_data;
4518 int len;
4519 struct sip_connection *conn = connection_find(sip, source);
4520 if (!conn) {
4521 purple_debug_error("sipe", "Connection not found!\n");
4522 return;
4525 if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
4526 conn->inbuflen += SIMPLE_BUF_INC;
4527 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
4530 len = read(source, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1);
4532 if (len < 0 && errno == EAGAIN)
4533 return;
4534 else if (len <= 0) {
4535 purple_debug_info("sipe", "sipe_input_cb: read error\n");
4536 connection_remove(sip, source);
4537 if (sip->fd == source) sip->fd = -1;
4538 return;
4541 conn->inbufused += len;
4542 conn->inbuf[conn->inbufused] = '\0';
4544 process_input(sip, conn);
4547 /* Callback for new connections on incoming TCP port */
4548 static void sipe_newconn_cb(gpointer data, gint source, PurpleInputCondition cond)
4550 PurpleConnection *gc = data;
4551 struct sipe_account_data *sip = gc->proto_data;
4552 struct sip_connection *conn;
4554 int newfd = accept(source, NULL, NULL);
4556 conn = connection_create(sip, newfd);
4558 conn->inputhandler = purple_input_add(newfd, PURPLE_INPUT_READ, sipe_input_cb, gc);
4561 static void login_cb(gpointer data, gint source, const gchar *error_message)
4563 PurpleConnection *gc = data;
4564 struct sipe_account_data *sip;
4565 struct sip_connection *conn;
4567 if (!PURPLE_CONNECTION_IS_VALID(gc))
4569 if (source >= 0)
4570 close(source);
4571 return;
4574 if (source < 0) {
4575 purple_connection_error(gc, _("Could not connect"));
4576 return;
4579 sip = gc->proto_data;
4580 sip->fd = source;
4581 sip->last_keepalive = time(NULL);
4583 conn = connection_create(sip, source);
4585 do_register(sip);
4587 conn->inputhandler = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_input_cb, gc);
4590 static void login_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
4592 struct sipe_account_data *sip = sipe_setup_ssl(data, gsc);
4593 if (sip == NULL) return;
4595 do_register(sip);
4598 static guint sipe_ht_hash_nick(const char *nick)
4600 char *lc = g_utf8_strdown(nick, -1);
4601 guint bucket = g_str_hash(lc);
4602 g_free(lc);
4604 return bucket;
4607 static gboolean sipe_ht_equals_nick(const char *nick1, const char *nick2)
4609 return (purple_utf8_strcasecmp(nick1, nick2) == 0);
4612 static void sipe_udp_host_resolved_listen_cb(int listenfd, gpointer data)
4614 struct sipe_account_data *sip = (struct sipe_account_data*) data;
4616 sip->listen_data = NULL;
4618 if (listenfd == -1) {
4619 purple_connection_error(sip->gc, _("Could not create listen socket"));
4620 return;
4623 sip->fd = listenfd;
4625 sip->listenport = purple_network_get_port_from_fd(sip->fd);
4626 sip->listenfd = sip->fd;
4628 sip->listenpa = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_udp_process, sip->gc);
4630 sip->resendtimeout = purple_timeout_add(2500, (GSourceFunc) resend_timeout, sip);
4631 do_register(sip);
4634 static void sipe_udp_host_resolved(GSList *hosts, gpointer data, const char *error_message)
4636 struct sipe_account_data *sip = (struct sipe_account_data*) data;
4637 int addr_size;
4639 sip->query_data = NULL;
4641 if (!hosts || !hosts->data) {
4642 purple_connection_error(sip->gc, _("Couldn't resolve host"));
4643 return;
4646 addr_size = GPOINTER_TO_INT(hosts->data);
4647 hosts = g_slist_remove(hosts, hosts->data);
4648 memcpy(&(sip->serveraddr), hosts->data, addr_size);
4649 g_free(hosts->data);
4650 hosts = g_slist_remove(hosts, hosts->data);
4651 while (hosts) {
4652 hosts = g_slist_remove(hosts, hosts->data);
4653 g_free(hosts->data);
4654 hosts = g_slist_remove(hosts, hosts->data);
4657 /* create socket for incoming connections */
4658 sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_DGRAM,
4659 sipe_udp_host_resolved_listen_cb, sip);
4660 if (sip->listen_data == NULL) {
4661 purple_connection_error(sip->gc, _("Could not create listen socket"));
4662 return;
4666 static void sipe_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
4667 gpointer data)
4669 PurpleConnection *gc = data;
4670 struct sipe_account_data *sip;
4672 /* If the connection is already disconnected, we don't need to do anything else */
4673 if (!PURPLE_CONNECTION_IS_VALID(gc))
4674 return;
4676 sip = gc->proto_data;
4677 sip->fd = -1;
4678 sip->gsc = NULL;
4680 switch(error) {
4681 case PURPLE_SSL_CONNECT_FAILED:
4682 purple_connection_error(gc, _("Connection Failed"));
4683 break;
4684 case PURPLE_SSL_HANDSHAKE_FAILED:
4685 purple_connection_error(gc, _("SSL Handshake Failed"));
4686 break;
4687 case PURPLE_SSL_CERTIFICATE_INVALID:
4688 purple_connection_error(gc, _("SSL Certificate Invalid"));
4689 break;
4693 static void
4694 sipe_tcp_connect_listen_cb(int listenfd, gpointer data)
4696 struct sipe_account_data *sip = (struct sipe_account_data*) data;
4697 PurpleProxyConnectData *connect_data;
4699 sip->listen_data = NULL;
4701 sip->listenfd = listenfd;
4702 if (sip->listenfd == -1) {
4703 purple_connection_error(sip->gc, _("Could not create listen socket"));
4704 return;
4707 purple_debug_info("sipe", "listenfd: %d\n", sip->listenfd);
4708 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
4709 sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
4710 sip->listenpa = purple_input_add(sip->listenfd, PURPLE_INPUT_READ,
4711 sipe_newconn_cb, sip->gc);
4712 purple_debug_info("sipe", "connecting to %s port %d\n",
4713 sip->realhostname, sip->realport);
4714 /* open tcp connection to the server */
4715 connect_data = purple_proxy_connect(sip->gc, sip->account, sip->realhostname,
4716 sip->realport, login_cb, sip->gc);
4718 if (connect_data == NULL) {
4719 purple_connection_error(sip->gc, _("Couldn't create socket"));
4724 static void create_connection(struct sipe_account_data *sip, gchar *hostname, int port)
4726 PurpleAccount *account = sip->account;
4727 PurpleConnection *gc = sip->gc;
4729 if (purple_account_get_bool(account, "useport", FALSE)) {
4730 purple_debug(PURPLE_DEBUG_MISC, "sipe", "create_connection - using specified SIP port\n");
4731 port = purple_account_get_int(account, "port", 0);
4732 } else {
4733 port = port ? port : (sip->transport == SIPE_TRANSPORT_TLS) ? 5061 : 5060;
4736 sip->realhostname = hostname;
4737 sip->realport = port;
4739 purple_debug(PURPLE_DEBUG_MISC, "sipe", "create_connection - hostname: %s port: %d\n",
4740 hostname, port);
4742 /* TODO: is there a good default grow size? */
4743 if (sip->transport != SIPE_TRANSPORT_UDP)
4744 sip->txbuf = purple_circ_buffer_new(0);
4746 if (sip->transport == SIPE_TRANSPORT_TLS) {
4747 /* SSL case */
4748 if (!purple_ssl_is_supported()) {
4749 gc->wants_to_die = TRUE;
4750 purple_connection_error(gc, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
4751 return;
4754 purple_debug_info("sipe", "using SSL\n");
4756 sip->gsc = purple_ssl_connect(account, hostname, port,
4757 login_cb_ssl, sipe_ssl_connect_failure, gc);
4758 if (sip->gsc == NULL) {
4759 purple_connection_error(gc, _("Could not create SSL context"));
4760 return;
4762 } else if (sip->transport == SIPE_TRANSPORT_UDP) {
4763 /* UDP case */
4764 purple_debug_info("sipe", "using UDP\n");
4766 sip->query_data = purple_dnsquery_a(hostname, port, sipe_udp_host_resolved, sip);
4767 if (sip->query_data == NULL) {
4768 purple_connection_error(gc, _("Could not resolve hostname"));
4770 } else {
4771 /* TCP case */
4772 purple_debug_info("sipe", "using TCP\n");
4773 /* create socket for incoming connections */
4774 sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_STREAM,
4775 sipe_tcp_connect_listen_cb, sip);
4776 if (sip->listen_data == NULL) {
4777 purple_connection_error(gc, _("Could not create listen socket"));
4778 return;
4783 /* Service list for autodection */
4784 static const struct sipe_service_data service_autodetect[] = {
4785 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS }, /* for internal TLS connections */
4786 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP }, /* for internal TCP connections */
4787 { "sip", "tls", SIPE_TRANSPORT_TLS }, /* for external TLS connections */
4788 { "sip", "tcp", SIPE_TRANSPORT_TCP }, /*.for external TCP connections */
4789 { NULL, NULL, 0 }
4792 /* Service list for SSL/TLS */
4793 static const struct sipe_service_data service_tls[] = {
4794 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS }, /* for internal TLS connections */
4795 { "sip", "tls", SIPE_TRANSPORT_TLS }, /* for external TLS connections */
4796 { NULL, NULL, 0 }
4799 /* Service list for TCP */
4800 static const struct sipe_service_data service_tcp[] = {
4801 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP }, /* for internal TCP connections */
4802 { "sip", "tcp", SIPE_TRANSPORT_TCP }, /*.for external TCP connections */
4803 { NULL, NULL, 0 }
4806 /* Service list for UDP */
4807 static const struct sipe_service_data service_udp[] = {
4808 { "sip", "udp", SIPE_TRANSPORT_UDP },
4809 { NULL, NULL, 0 }
4812 static void srvresolved(PurpleSrvResponse *, int, gpointer);
4813 static void resolve_next_service(struct sipe_account_data *sip,
4814 const struct sipe_service_data *start)
4816 if (start) {
4817 sip->service_data = start;
4818 } else {
4819 sip->service_data++;
4820 if (sip->service_data->service == NULL) {
4821 gchar *hostname;
4822 /* Try connecting to the SIP hostname directly */
4823 purple_debug(PURPLE_DEBUG_MISC, "sipe", "no SRV records found; using SIP domain as fallback\n");
4824 if (sip->auto_transport) {
4825 // If SSL is supported, default to using it; OCS servers aren't configured
4826 // by default to accept TCP
4827 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
4828 sip->transport = purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS : SIPE_TRANSPORT_TCP;
4829 purple_debug(PURPLE_DEBUG_MISC, "sipe", "set transport type..\n");
4832 hostname = g_strdup(sip->sipdomain);
4833 create_connection(sip, hostname, 0);
4834 return;
4838 /* Try to resolve next service */
4839 sip->srv_query_data = purple_srv_resolve(sip->service_data->service,
4840 sip->service_data->transport,
4841 sip->sipdomain,
4842 srvresolved, sip);
4845 static void srvresolved(PurpleSrvResponse *resp, int results, gpointer data)
4847 struct sipe_account_data *sip = data;
4849 sip->srv_query_data = NULL;
4851 /* find the host to connect to */
4852 if (results) {
4853 gchar *hostname = g_strdup(resp->hostname);
4854 int port = resp->port;
4855 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
4856 hostname, port);
4857 g_free(resp);
4859 sip->transport = sip->service_data->type;
4861 create_connection(sip, hostname, port);
4862 } else {
4863 resolve_next_service(sip, NULL);
4867 static void sipe_login(PurpleAccount *account)
4869 PurpleConnection *gc;
4870 struct sipe_account_data *sip;
4871 gchar **signinname_login, **userserver, **domain_user;
4872 const char *transport;
4874 const char *username = purple_account_get_username(account);
4875 gc = purple_account_get_connection(account);
4877 if (strpbrk(username, "\t\v\r\n") != NULL) {
4878 gc->wants_to_die = TRUE;
4879 purple_connection_error(gc, _("SIP Exchange username contains invalid characters"));
4880 return;
4883 gc->proto_data = sip = g_new0(struct sipe_account_data, 1);
4884 gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO | PURPLE_CONNECTION_NO_BGCOLOR |
4885 PURPLE_CONNECTION_NO_FONTSIZE | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY;
4886 sip->gc = gc;
4887 sip->account = account;
4888 sip->reregister_set = FALSE;
4889 sip->reauthenticate_set = FALSE;
4890 sip->subscribed = FALSE;
4891 sip->subscribed_buddies = FALSE;
4893 signinname_login = g_strsplit(username, ",", 2);
4895 userserver = g_strsplit(signinname_login[0], "@", 2);
4896 purple_connection_set_display_name(gc, userserver[0]);
4897 sip->username = g_strjoin("@", userserver[0], userserver[1], NULL);
4898 sip->sipdomain = g_strdup(userserver[1]);
4900 if (strpbrk(sip->username, " \t\v\r\n") != NULL) {
4901 gc->wants_to_die = TRUE;
4902 purple_connection_error(gc, _("SIP Exchange usernames may not contain whitespaces"));
4903 return;
4906 domain_user = g_strsplit(signinname_login[1], "\\", 2);
4907 sip->authdomain = (domain_user && domain_user[1]) ? g_strdup(domain_user[0]) : NULL;
4908 sip->authuser = (domain_user && domain_user[1]) ? g_strdup(domain_user[1]) : (signinname_login ? g_strdup(signinname_login[1]) : NULL);
4910 sip->password = g_strdup(purple_connection_get_password(gc));
4912 g_strfreev(userserver);
4913 g_strfreev(domain_user);
4914 g_strfreev(signinname_login);
4916 sip->buddies = g_hash_table_new((GHashFunc)sipe_ht_hash_nick, (GEqualFunc)sipe_ht_equals_nick);
4918 purple_connection_update_progress(gc, _("Connecting"), 1, 2);
4920 /* TODO: Set the status correctly. */
4921 sip->status = g_strdup(SIPE_STATUS_ID_AVAILABLE);
4923 transport = purple_account_get_string(account, "transport", "auto");
4924 sip->transport = (strcmp(transport, "tls") == 0) ? SIPE_TRANSPORT_TLS :
4925 (strcmp(transport, "tcp") == 0) ? SIPE_TRANSPORT_TCP :
4926 SIPE_TRANSPORT_UDP;
4928 if (purple_account_get_bool(account, "useproxy", FALSE)) {
4929 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_login - using specified SIP proxy\n");
4930 create_connection(sip, g_strdup(purple_account_get_string(account, "proxy", sip->sipdomain)), 0);
4931 } else if (strcmp(transport, "auto") == 0) {
4932 sip->auto_transport = TRUE;
4933 resolve_next_service(sip, purple_ssl_is_supported() ? service_autodetect : service_tcp);
4934 } else if (strcmp(transport, "tls") == 0) {
4935 resolve_next_service(sip, service_tls);
4936 } else if (strcmp(transport, "tcp") == 0) {
4937 resolve_next_service(sip, service_tcp);
4938 } else {
4939 resolve_next_service(sip, service_udp);
4943 static void sipe_connection_cleanup(struct sipe_account_data *sip)
4945 connection_free_all(sip);
4947 g_free(sip->epid);
4948 sip->epid = NULL;
4950 if (sip->query_data != NULL)
4951 purple_dnsquery_destroy(sip->query_data);
4952 sip->query_data = NULL;
4954 if (sip->srv_query_data != NULL)
4955 purple_srv_cancel(sip->srv_query_data);
4956 sip->srv_query_data = NULL;
4958 if (sip->listen_data != NULL)
4959 purple_network_listen_cancel(sip->listen_data);
4960 sip->listen_data = NULL;
4962 if (sip->gsc != NULL)
4963 purple_ssl_close(sip->gsc);
4964 sip->gsc = NULL;
4966 sipe_auth_free(&sip->registrar);
4967 sipe_auth_free(&sip->proxy);
4969 if (sip->txbuf)
4970 purple_circ_buffer_destroy(sip->txbuf);
4971 sip->txbuf = NULL;
4973 g_free(sip->realhostname);
4974 sip->realhostname = NULL;
4976 if (sip->listenpa)
4977 purple_input_remove(sip->listenpa);
4978 sip->listenpa = 0;
4979 if (sip->tx_handler)
4980 purple_input_remove(sip->tx_handler);
4981 sip->tx_handler = 0;
4982 if (sip->resendtimeout)
4983 purple_timeout_remove(sip->resendtimeout);
4984 sip->resendtimeout = 0;
4985 if (sip->timeouts) {
4986 GSList *entry = sip->timeouts;
4987 while (entry) {
4988 struct scheduled_action *sched_action = entry->data;
4989 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action->name);
4990 purple_timeout_remove(sched_action->timeout_handler);
4991 g_free(sched_action->payload);
4992 g_free(sched_action->name);
4993 g_free(sched_action);
4994 entry = entry->next;
4997 g_slist_free(sip->timeouts);
4999 if (sip->allow_events) {
5000 GSList *entry = sip->allow_events;
5001 while (entry) {
5002 g_free(entry->data);
5003 entry = entry->next;
5006 g_slist_free(sip->allow_events);
5008 if (sip->contact)
5009 g_free(sip->contact);
5010 sip->contact = NULL;
5011 if (sip->regcallid)
5012 g_free(sip->regcallid);
5013 sip->regcallid = NULL;
5015 sip->fd = -1;
5016 sip->processing_input = FALSE;
5020 * A callback for g_hash_table_foreach_remove
5022 static gboolean sipe_buddy_remove(gpointer key, struct sipe_buddy *buddy, gpointer user_data)
5024 sipe_free_buddy(buddy);
5027 static void sipe_close(PurpleConnection *gc)
5029 struct sipe_account_data *sip = gc->proto_data;
5031 if (sip) {
5032 /* leave all conversations */
5033 im_session_close_all(sip);
5035 /* unregister */
5036 do_register_exp(sip, 0);
5038 sipe_connection_cleanup(sip);
5039 g_free(sip->sipdomain);
5040 g_free(sip->username);
5041 g_free(sip->password);
5042 g_free(sip->authdomain);
5043 g_free(sip->authuser);
5044 g_free(sip->status);
5046 g_hash_table_foreach_remove(sip->buddies, (GHRFunc) sipe_buddy_remove, NULL);
5047 g_hash_table_destroy(sip->buddies);
5049 g_free(gc->proto_data);
5050 gc->proto_data = NULL;
5053 static void sipe_searchresults_im_buddy(PurpleConnection *gc, GList *row, void *user_data)
5055 PurpleAccount *acct = purple_connection_get_account(gc);
5056 char *id = g_strdup_printf("sip:%s", (char *)g_list_nth_data(row, 0));
5057 PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, id, acct);
5058 if (conv == NULL)
5059 conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, id);
5060 purple_conversation_present(conv);
5061 g_free(id);
5064 static void sipe_searchresults_add_buddy(PurpleConnection *gc, GList *row, void *user_data)
5067 purple_blist_request_add_buddy(purple_connection_get_account(gc),
5068 g_list_nth_data(row, 0), NULL, g_list_nth_data(row, 1));
5071 static gboolean process_search_contact_response(struct sipe_account_data *sip, struct sipmsg *msg,struct transaction *tc)
5073 PurpleNotifySearchResults *results;
5074 PurpleNotifySearchColumn *column;
5075 xmlnode *searchResults;
5076 xmlnode *mrow;
5077 int match_count = 0;
5078 gboolean more = FALSE;
5079 gchar *secondary;
5081 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg->body ? msg->body : "");
5083 searchResults = xmlnode_from_str(msg->body, msg->bodylen);
5084 if (!searchResults) {
5085 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
5086 return FALSE;
5089 results = purple_notify_searchresults_new();
5091 if (results == NULL) {
5092 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
5093 purple_notify_error(sip->gc, NULL, _("Unable to display the search results."), NULL);
5095 xmlnode_free(searchResults);
5096 return FALSE;
5099 column = purple_notify_searchresults_column_new(_("User Name"));
5100 purple_notify_searchresults_column_add(results, column);
5102 column = purple_notify_searchresults_column_new(_("Name"));
5103 purple_notify_searchresults_column_add(results, column);
5105 column = purple_notify_searchresults_column_new(_("Company"));
5106 purple_notify_searchresults_column_add(results, column);
5108 column = purple_notify_searchresults_column_new(_("Country"));
5109 purple_notify_searchresults_column_add(results, column);
5111 column = purple_notify_searchresults_column_new(_("Email"));
5112 purple_notify_searchresults_column_add(results, column);
5114 for (mrow = xmlnode_get_descendant(searchResults, "Body", "Array", "row", NULL); mrow; mrow = xmlnode_get_next_twin(mrow)) {
5115 GList *row = NULL;
5117 gchar **uri_parts = g_strsplit(xmlnode_get_attrib(mrow, "uri"), ":", 2);
5118 row = g_list_append(row, g_strdup(uri_parts[1]));
5119 g_strfreev(uri_parts);
5121 row = g_list_append(row, g_strdup(xmlnode_get_attrib(mrow, "displayName")));
5122 row = g_list_append(row, g_strdup(xmlnode_get_attrib(mrow, "company")));
5123 row = g_list_append(row, g_strdup(xmlnode_get_attrib(mrow, "country")));
5124 row = g_list_append(row, g_strdup(xmlnode_get_attrib(mrow, "email")));
5126 purple_notify_searchresults_row_add(results, row);
5127 match_count++;
5130 if ((mrow = xmlnode_get_descendant(searchResults, "Body", "directorySearch", "moreAvailable", NULL)) != NULL) {
5131 char *data = xmlnode_get_data_unescaped(mrow);
5132 more = (g_strcasecmp(data, "true") == 0);
5133 g_free(data);
5136 secondary = g_strdup_printf(
5137 dngettext(GETTEXT_PACKAGE,
5138 "Found %d contact%s:",
5139 "Found %d contacts%s:", match_count),
5140 match_count, more ? _(" (more matched your query)") : "");
5142 purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_IM, sipe_searchresults_im_buddy);
5143 purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_ADD, sipe_searchresults_add_buddy);
5144 purple_notify_searchresults(sip->gc, NULL, NULL, secondary, results, NULL, NULL);
5146 g_free(secondary);
5147 xmlnode_free(searchResults);
5148 return TRUE;
5151 static void sipe_search_contact_with_cb(PurpleConnection *gc, PurpleRequestFields *fields)
5153 GList *entries = purple_request_field_group_get_fields(purple_request_fields_get_groups(fields)->data);
5154 gchar **attrs = g_new(gchar *, g_list_length(entries) + 1);
5155 unsigned i = 0;
5157 do {
5158 PurpleRequestField *field = entries->data;
5159 const char *id = purple_request_field_get_id(field);
5160 const char *value = purple_request_field_string_get_value(field);
5162 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id, value ? value : "");
5164 if (value != NULL) attrs[i++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW, id, value);
5165 } while ((entries = g_list_next(entries)) != NULL);
5166 attrs[i] = NULL;
5168 if (i > 0) {
5169 gchar *query = g_strjoinv(NULL, attrs);
5170 gchar *body = g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT, 100, query);
5171 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body ? body : "");
5172 send_soap_request_with_cb(gc->proto_data, body,
5173 (TransCallback) process_search_contact_response, NULL);
5174 g_free(body);
5175 g_free(query);
5178 g_strfreev(attrs);
5181 static void sipe_show_find_contact(PurplePluginAction *action)
5183 PurpleConnection *gc = (PurpleConnection *) action->context;
5184 PurpleRequestFields *fields;
5185 PurpleRequestFieldGroup *group;
5186 PurpleRequestField *field;
5188 fields = purple_request_fields_new();
5189 group = purple_request_field_group_new(NULL);
5190 purple_request_fields_add_group(fields, group);
5192 field = purple_request_field_string_new("givenName", _("First Name"), NULL, FALSE);
5193 purple_request_field_group_add_field(group, field);
5194 field = purple_request_field_string_new("sn", _("Last Name"), NULL, FALSE);
5195 purple_request_field_group_add_field(group, field);
5196 field = purple_request_field_string_new("company", _("Company"), NULL, FALSE);
5197 purple_request_field_group_add_field(group, field);
5198 field = purple_request_field_string_new("c", _("Country"), NULL, FALSE);
5199 purple_request_field_group_add_field(group, field);
5201 purple_request_fields(gc,
5202 _("Search"),
5203 _("Search for a Contact"),
5204 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
5205 fields,
5206 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb),
5207 _("_Cancel"), NULL,
5208 purple_connection_get_account(gc), NULL, NULL, gc);
5211 GList *sipe_actions(PurplePlugin *plugin, gpointer context)
5213 GList *menu = NULL;
5214 PurplePluginAction *act;
5216 act = purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact);
5217 menu = g_list_prepend(menu, act);
5219 menu = g_list_reverse(menu);
5221 return menu;
5224 static void dummy_permit_deny(PurpleConnection *gc)
5228 static gboolean sipe_plugin_load(PurplePlugin *plugin)
5230 return TRUE;
5234 static gboolean sipe_plugin_unload(PurplePlugin *plugin)
5236 return TRUE;
5240 static char *sipe_status_text(PurpleBuddy *buddy)
5242 struct sipe_account_data *sip;
5243 struct sipe_buddy *sbuddy;
5244 char *text = NULL;
5246 sip = (struct sipe_account_data *) buddy->account->gc->proto_data;
5247 if (sip) //happens on pidgin exit
5249 sbuddy = g_hash_table_lookup(sip->buddies, buddy->name);
5250 if (sbuddy && sbuddy->annotation)
5252 text = g_strdup(sbuddy->annotation);
5256 return text;
5259 static void sipe_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full)
5261 const PurplePresence *presence = purple_buddy_get_presence(buddy);
5262 const PurpleStatus *status = purple_presence_get_active_status(presence);
5263 struct sipe_account_data *sip;
5264 struct sipe_buddy *sbuddy;
5265 char *annotation = NULL;
5267 sip = (struct sipe_account_data *) buddy->account->gc->proto_data;
5268 if (sip) //happens on pidgin exit
5270 sbuddy = g_hash_table_lookup(sip->buddies, buddy->name);
5271 if (sbuddy)
5273 annotation = sbuddy->annotation ? g_strdup(sbuddy->annotation) : NULL;
5277 //Layout
5278 if (purple_presence_is_online(presence))
5280 purple_notify_user_info_add_pair(user_info, _("Status"), purple_status_get_name(status));
5283 if (annotation)
5285 purple_notify_user_info_add_pair( user_info, _("Note"), annotation );
5286 g_free(annotation);
5291 static GHashTable *
5292 sipe_get_account_text_table(PurpleAccount *account)
5294 GHashTable *table;
5295 table = g_hash_table_new(g_str_hash, g_str_equal);
5296 g_hash_table_insert(table, "login_label", (gpointer)_("Sign-In Name..."));
5297 return table;
5300 static PurpleBuddy *
5301 purple_blist_add_buddy_clone(PurpleGroup * group, PurpleBuddy * buddy)
5303 PurpleBuddy *clone;
5304 const gchar *server_alias, *email;
5305 const PurpleStatus *status = purple_presence_get_active_status(purple_buddy_get_presence(buddy));
5307 clone = purple_buddy_new(buddy->account, buddy->name, buddy->alias);
5309 purple_blist_add_buddy(clone, NULL, group, NULL);
5311 server_alias = g_strdup(purple_buddy_get_server_alias(buddy));
5312 if (server_alias) {
5313 purple_blist_server_alias_buddy(clone, server_alias);
5316 email = purple_blist_node_get_string((PurpleBlistNode *)buddy, "email");
5317 if (email) {
5318 purple_blist_node_set_string((PurpleBlistNode *)clone, "email", email);
5321 purple_presence_set_status_active(purple_buddy_get_presence(clone), purple_status_get_id(status), TRUE);
5322 //for UI to update;
5323 purple_prpl_got_user_status(clone->account, clone->name, purple_status_get_id(status), NULL);
5324 return clone;
5327 static void
5328 sipe_buddy_menu_copy_to_cb(PurpleBlistNode *node, const char *group_name)
5330 PurpleBuddy *buddy, *b;
5331 PurpleConnection *gc;
5332 PurpleGroup * group = purple_find_group(group_name);
5334 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
5336 buddy = (PurpleBuddy *)node;
5338 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy->name, group_name);
5339 gc = purple_account_get_connection(buddy->account);
5341 b = purple_find_buddy_in_group(buddy->account, buddy->name, group);
5342 if (!b){
5343 b = purple_blist_add_buddy_clone(group, buddy);
5346 sipe_group_buddy(gc, buddy->name, NULL, group_name);
5349 static void
5350 sipe_buddy_menu_send_email_cb(PurpleBuddy *buddy)
5352 const gchar *email;
5353 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy->name);
5355 email = purple_blist_node_get_string((PurpleBlistNode *)buddy, "email");
5356 if (email)
5358 char *mailto = g_strdup_printf("mailto:%s", email);
5359 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email);
5360 #ifndef _WIN32
5362 pid_t pid;
5363 char *const parmList[] = {mailto, NULL};
5364 if ((pid = fork()) == -1)
5366 purple_debug_info("sipe", "fork() error\n");
5368 else if (pid == 0)
5370 execvp("xdg-email", parmList);
5371 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
5374 #else
5376 BOOL ret;
5377 _flushall();
5378 errno = 0;
5379 //@TODO resolve env variable %WINDIR% first
5380 ret = spawnl(_P_NOWAIT, "c:/WINDOWS/system32/cmd", "/c", "start", mailto, NULL);
5381 if (errno)
5383 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno));
5386 #endif
5388 g_free(mailto);
5390 else
5392 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy->name);
5397 * A menu which appear when right-clicking on buddy in contact list.
5399 static GList *
5400 sipe_buddy_menu(PurpleBuddy *buddy)
5402 PurpleBlistNode *g_node;
5403 PurpleGroup *group, *gr_parent;
5404 PurpleMenuAction *act;
5405 GList *menu = NULL;
5406 GList *menu_groups = NULL;
5408 act = purple_menu_action_new(_("Send Email..."),
5409 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb),
5410 NULL, NULL);
5411 menu = g_list_prepend(menu, act);
5413 gr_parent = purple_buddy_get_group(buddy);
5414 for (g_node = purple_blist_get_root(); g_node; g_node = g_node->next) {
5415 if (g_node->type != PURPLE_BLIST_GROUP_NODE)
5416 continue;
5418 group = (PurpleGroup *)g_node;
5419 if (group == gr_parent)
5420 continue;
5422 if (purple_find_buddy_in_group(buddy->account, buddy->name, group))
5423 continue;
5425 act = purple_menu_action_new(purple_group_get_name(group),
5426 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb),
5427 group->name, NULL);
5428 menu_groups = g_list_prepend(menu_groups, act);
5430 menu_groups = g_list_reverse(menu_groups);
5432 act = purple_menu_action_new(_("Copy to"),
5433 NULL,
5434 NULL, menu_groups);
5435 menu = g_list_prepend(menu, act);
5436 menu = g_list_reverse(menu);
5438 return menu;
5441 GList *sipe_blist_node_menu(PurpleBlistNode *node) {
5442 if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
5443 return sipe_buddy_menu((PurpleBuddy *) node);
5444 } else {
5445 return NULL;
5449 static gboolean
5450 process_get_info_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *trans)
5452 gboolean ret = TRUE;
5453 char *username = (char *)trans->payload;
5455 PurpleNotifyUserInfo *info = purple_notify_user_info_new();
5456 PurpleBuddy *pbuddy;
5457 struct sipe_buddy *sbuddy;
5458 const char *alias;
5459 char *server_alias = NULL;
5460 char *email = NULL;
5461 const char *device_name = NULL;
5463 purple_debug_info("sipe", "Fetching %s's user info for %s\n", username, sip->username);
5465 pbuddy = purple_find_buddy((PurpleAccount *)sip->account, username);
5466 alias = purple_buddy_get_local_alias(pbuddy);
5468 if (sip)
5470 //will query buddy UA's capabilities and send answer to log
5471 sipe_options_request(sip, username);
5473 sbuddy = g_hash_table_lookup(sip->buddies, username);
5474 if (sbuddy)
5476 device_name = sbuddy->device_name ? g_strdup(sbuddy->device_name) : NULL;
5480 if (msg->response != 200) {
5481 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg->response);
5482 } else {
5483 xmlnode *searchResults;
5484 xmlnode *mrow;
5486 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg->body ? msg->body : "");
5487 searchResults = xmlnode_from_str(msg->body, msg->bodylen);
5488 if (!searchResults) {
5489 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
5490 } else if ((mrow = xmlnode_get_descendant(searchResults, "Body", "Array", "row", NULL))) {
5491 server_alias = g_strdup(xmlnode_get_attrib(mrow, "displayName"));
5492 purple_notify_user_info_add_pair(info, _("Display Name"), server_alias);
5493 purple_notify_user_info_add_pair(info, _("Job Title"), g_strdup(xmlnode_get_attrib(mrow, "title")));
5494 purple_notify_user_info_add_pair(info, _("Office"), g_strdup(xmlnode_get_attrib(mrow, "office")));
5495 purple_notify_user_info_add_pair(info, _("Business Phone"), g_strdup(xmlnode_get_attrib(mrow, "phone")));
5496 purple_notify_user_info_add_pair(info, _("Company"), g_strdup(xmlnode_get_attrib(mrow, "company")));
5497 purple_notify_user_info_add_pair(info, _("City"), g_strdup(xmlnode_get_attrib(mrow, "city")));
5498 purple_notify_user_info_add_pair(info, _("State"), g_strdup(xmlnode_get_attrib(mrow, "state")));
5499 purple_notify_user_info_add_pair(info, _("Country"), g_strdup(xmlnode_get_attrib(mrow, "country")));
5500 email = g_strdup(xmlnode_get_attrib(mrow, "email"));
5501 purple_notify_user_info_add_pair(info, _("E-Mail Address"), email);
5502 if (!email || strcmp("", email)) {
5503 if (!purple_blist_node_get_string((PurpleBlistNode *)pbuddy, "email")) {
5504 purple_blist_node_set_string((PurpleBlistNode *)pbuddy, "email", email);
5508 xmlnode_free(searchResults);
5511 purple_notify_user_info_add_section_break(info);
5513 if (!server_alias || !strcmp("", server_alias)) {
5514 g_free(server_alias);
5515 server_alias = g_strdup(purple_buddy_get_server_alias(pbuddy));
5516 if (server_alias) {
5517 purple_notify_user_info_add_pair(info, _("Display Name"), server_alias);
5521 // same as server alias, do not present
5522 alias = (alias && server_alias && !strcmp(alias, server_alias)) ? NULL : alias;
5523 if (alias)
5525 purple_notify_user_info_add_pair(info, _("Alias"), alias);
5528 if (!email || !strcmp("", email)) {
5529 g_free(email);
5530 email = g_strdup(purple_blist_node_get_string((PurpleBlistNode *)pbuddy, "email"));
5531 if (email) {
5532 purple_notify_user_info_add_pair(info, _("E-Mail Address"), email);
5536 if (device_name)
5538 purple_notify_user_info_add_pair(info, _("Device"), device_name);
5541 /* show a buddy's user info in a nice dialog box */
5542 purple_notify_userinfo(sip->gc, /* connection the buddy info came through */
5543 username, /* buddy's username */
5544 info, /* body */
5545 NULL, /* callback called when dialog closed */
5546 NULL); /* userdata for callback */
5548 return ret;
5552 * AD search first, LDAP based
5554 static void sipe_get_info(PurpleConnection *gc, const char *username)
5556 char *row = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW, "msRTCSIP-PrimaryUserAddress", username);
5557 gchar *body = g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT, 1, row);
5559 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body ? body : "");
5560 send_soap_request_with_cb((struct sipe_account_data *)gc->proto_data, body,
5561 (TransCallback) process_get_info_response, (gpointer)g_strdup(username));
5562 g_free(body);
5563 g_free(row);
5566 static PurplePlugin *my_protocol = NULL;
5568 static PurplePluginProtocolInfo prpl_info =
5571 NULL, /* user_splits */
5572 NULL, /* protocol_options */
5573 NO_BUDDY_ICONS, /* icon_spec */
5574 sipe_list_icon, /* list_icon */
5575 NULL, /* list_emblems */
5576 sipe_status_text, /* status_text */
5577 sipe_tooltip_text, /* tooltip_text */ // add custom info to contact tooltip
5578 sipe_status_types, /* away_states */
5579 sipe_blist_node_menu, /* blist_node_menu */
5580 NULL, /* chat_info */
5581 NULL, /* chat_info_defaults */
5582 sipe_login, /* login */
5583 sipe_close, /* close */
5584 sipe_im_send, /* send_im */
5585 NULL, /* set_info */ // TODO maybe
5586 sipe_send_typing, /* send_typing */
5587 sipe_get_info, /* get_info */
5588 sipe_set_status, /* set_status */
5589 NULL, /* set_idle */
5590 NULL, /* change_passwd */
5591 sipe_add_buddy, /* add_buddy */
5592 NULL, /* add_buddies */
5593 sipe_remove_buddy, /* remove_buddy */
5594 NULL, /* remove_buddies */
5595 sipe_add_permit, /* add_permit */
5596 sipe_add_deny, /* add_deny */
5597 sipe_add_deny, /* rem_permit */
5598 sipe_add_permit, /* rem_deny */
5599 dummy_permit_deny, /* set_permit_deny */
5600 NULL, /* join_chat */
5601 NULL, /* reject_chat */
5602 NULL, /* get_chat_name */
5603 NULL, /* chat_invite */
5604 NULL, /* chat_leave */
5605 NULL, /* chat_whisper */
5606 NULL, /* chat_send */
5607 sipe_keep_alive, /* keepalive */
5608 NULL, /* register_user */
5609 NULL, /* get_cb_info */ // deprecated
5610 NULL, /* get_cb_away */ // deprecated
5611 sipe_alias_buddy, /* alias_buddy */
5612 sipe_group_buddy, /* group_buddy */
5613 sipe_rename_group, /* rename_group */
5614 NULL, /* buddy_free */
5615 sipe_convo_closed, /* convo_closed */
5616 purple_normalize_nocase, /* normalize */
5617 NULL, /* set_buddy_icon */
5618 sipe_remove_group, /* remove_group */
5619 NULL, /* get_cb_real_name */ // TODO?
5620 NULL, /* set_chat_topic */
5621 NULL, /* find_blist_chat */
5622 NULL, /* roomlist_get_list */
5623 NULL, /* roomlist_cancel */
5624 NULL, /* roomlist_expand_category */
5625 NULL, /* can_receive_file */
5626 NULL, /* send_file */
5627 NULL, /* new_xfer */
5628 NULL, /* offline_message */
5629 NULL, /* whiteboard_prpl_ops */
5630 sipe_send_raw, /* send_raw */
5631 NULL, /* roomlist_room_serialize */
5632 NULL, /* unregister_user */
5633 NULL, /* send_attention */
5634 NULL, /* get_attention_types */
5636 sizeof(PurplePluginProtocolInfo), /* struct_size */
5637 sipe_get_account_text_table, /* get_account_text_table */
5641 static PurplePluginInfo info = {
5642 PURPLE_PLUGIN_MAGIC,
5643 PURPLE_MAJOR_VERSION,
5644 PURPLE_MINOR_VERSION,
5645 PURPLE_PLUGIN_PROTOCOL, /**< type */
5646 NULL, /**< ui_requirement */
5647 0, /**< flags */
5648 NULL, /**< dependencies */
5649 PURPLE_PRIORITY_DEFAULT, /**< priority */
5650 "prpl-sipe", /**< id */
5651 "Microsoft LCS/OCS", /**< name */
5652 VERSION, /**< version */
5653 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
5654 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
5655 "Anibal Avelar <avelar@gmail.com>, " /**< author */
5656 "Gabriel Burt <gburt@novell.com>", /**< author */
5657 PURPLE_WEBSITE, /**< homepage */
5658 sipe_plugin_load, /**< load */
5659 sipe_plugin_unload, /**< unload */
5660 sipe_plugin_destroy, /**< destroy */
5661 NULL, /**< ui_info */
5662 &prpl_info, /**< extra_info */
5663 NULL,
5664 sipe_actions,
5665 NULL,
5666 NULL,
5667 NULL,
5668 NULL
5671 static void sipe_plugin_destroy(PurplePlugin *plugin)
5673 GList *entry;
5675 entry = prpl_info.protocol_options;
5676 while (entry) {
5677 purple_account_option_destroy(entry->data);
5678 entry = g_list_delete_link(entry, entry);
5680 prpl_info.protocol_options = NULL;
5682 entry = prpl_info.user_splits;
5683 while (entry) {
5684 purple_account_user_split_destroy(entry->data);
5685 entry = g_list_delete_link(entry, entry);
5687 prpl_info.user_splits = NULL;
5690 static void init_plugin(PurplePlugin *plugin)
5692 PurpleAccountUserSplit *split;
5693 PurpleAccountOption *option;
5695 #ifdef ENABLE_NLS
5696 purple_debug_info(PACKAGE, "bindtextdomain = %s", bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR));
5697 purple_debug_info(PACKAGE, "bind_textdomain_codeset = %s",
5698 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"));
5699 #endif
5701 purple_plugin_register(plugin);
5703 split = purple_account_user_split_new(_("Login \n domain\\user or\n someone@linux.com "), NULL, ',');
5704 purple_account_user_split_set_reverse(split, FALSE);
5705 prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
5707 option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
5708 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5709 option = purple_account_option_string_new(_("Proxy Server"), "proxy", "");
5710 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5712 option = purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE);
5713 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5714 // Translators: noun (networking port)
5715 option = purple_account_option_int_new(_("Port"), "port", 5061);
5716 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5718 option = purple_account_option_list_new(_("Connection Type"), "transport", NULL);
5719 purple_account_option_add_list_item(option, _("Auto"), "auto");
5720 purple_account_option_add_list_item(option, _("SSL/TLS"), "tls");
5721 purple_account_option_add_list_item(option, _("TCP"), "tcp");
5722 purple_account_option_add_list_item(option, _("UDP"), "udp");
5723 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5725 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
5726 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
5728 option = purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION);
5729 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5731 // TODO commented out so won't show in the preferences until we fix krb message signing
5732 /*option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
5733 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5735 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
5736 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
5737 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5740 my_protocol = plugin;
5743 /* I had to redefined the function for it load, but works */
5744 gboolean purple_init_plugin(PurplePlugin *plugin){
5745 plugin->info = &(info);
5746 init_plugin((plugin));
5747 sipe_plugin_load((plugin));
5748 return purple_plugin_register(plugin);
5752 Local Variables:
5753 mode: c
5754 c-file-style: "bsd"
5755 indent-tabs-mode: t
5756 tab-width: 8
5757 End: