Set translation domain so translations actually used, improve the wording
[siplcs.git] / src / sipe.c
blobd223a4c94431b4075f7f59439bf7997a73344c7a
1 /**
2 * @file sipe.c
4 * pidgin-sipe
6 * Copyright (C) 2008 Novell, Inc.
7 * Copyright (C) 2007 Anibal Avelar "Fixxxer"<avelar@gmail.com>
8 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
10 * ***
11 * Thanks to Google's Summer of Code Program and the helpful mentors
12 * ***
14 * Session-based SIP MESSAGE documentation:
15 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #ifndef _WIN32
33 #include "sip-internal.h"
34 #else /* _WIN32 */
35 #ifdef _DLL
36 #define _WS2TCPIP_H_
37 #define _WINSOCK2API_
38 #define _LIBC_INTERNAL_
39 #endif /* _DLL */
41 #include "internal.h"
42 #endif /* _WIN32 */
44 #include <glib.h>
46 #ifdef ENABLE_NLS
47 # include <glib/gi18n-lib.h>
48 #else
49 # define _(String) ((const char *) (String))
50 #endif /* ENABLE_NLS */
52 #include "accountopt.h"
53 #include "blist.h"
54 #include "conversation.h"
55 #include "dnsquery.h"
56 #include "debug.h"
57 #include "notify.h"
58 #include "privacy.h"
59 #include "prpl.h"
60 #include "plugin.h"
61 #include "util.h"
62 #include "version.h"
63 #include "network.h"
64 #include "xmlnode.h"
66 #include "sipe.h"
67 #include "sip-ntlm.h"
68 #ifdef USE_KERBEROS
69 #include "sipkrb5.h"
70 #endif /*USE_KERBEROS*/
72 #include "sipmsg.h"
73 #include "sipe-sign.h"
74 #include "dnssrv.h"
76 /* Keep in sync with sipe_transport_type! */
77 static const char *transport_descriptor[] = { "tls", "tcp", "udp" };
78 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
80 static char *gentag()
82 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
85 static gchar *get_epid()
87 return sipe_uuid_get_macaddr();
90 static char *genbranch()
92 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
93 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
94 rand() & 0xFFFF, rand() & 0xFFFF);
97 static char *gencallid()
99 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
100 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
101 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
102 rand() & 0xFFFF, rand() & 0xFFFF);
105 static gchar *find_tag(const gchar *hdr)
107 gchar * tag = sipmsg_find_part_of_header (hdr, "tag=", ";", NULL);
108 if (!tag) {
109 // In case it's at the end and there's no trailing ;
110 tag = sipmsg_find_part_of_header (hdr, "tag=", NULL, NULL);
112 return tag;
116 static const char *sipe_list_icon(PurpleAccount *a, PurpleBuddy *b)
118 return "sipe";
121 static void sipe_keep_alive(PurpleConnection *gc)
123 struct sipe_account_data *sip = gc->proto_data;
124 if (sip->transport == SIPE_TRANSPORT_UDP) {
125 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
126 gchar buf[2] = {0, 0};
127 purple_debug_info("sipe", "sending keep alive\n");
128 sendto(sip->fd, buf, 1, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in));
132 static gboolean process_register_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc);
134 static void sipe_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
135 static void sipe_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
136 gpointer data);
138 static void send_notify(struct sipe_account_data *sip, struct sipe_watcher *);
139 static void sipe_close(PurpleConnection *gc);
141 static void send_service(struct sipe_account_data *sip);
142 static void sipe_subscribe_to_name(struct sipe_account_data *sip, const char * buddy_name);
143 static void send_presence_info(struct sipe_account_data *sip);
145 static void do_notifies(struct sipe_account_data *sip)
147 GSList *tmp = sip->watcher;
148 purple_debug_info("sipe", "do_notifies()\n");
150 while (tmp) {
151 purple_debug_info("sipe", "notifying %s\n", ((struct sipe_watcher*)tmp->data)->name);
152 send_notify(sip, tmp->data);
153 tmp = tmp->next;
157 static struct sip_connection *connection_find(struct sipe_account_data *sip, int fd)
159 struct sip_connection *ret = NULL;
160 GSList *entry = sip->openconns;
161 while (entry) {
162 ret = entry->data;
163 if (ret->fd == fd) return ret;
164 entry = entry->next;
166 return NULL;
169 static struct sipe_watcher *watcher_find(struct sipe_account_data *sip,
170 const gchar *name)
172 struct sipe_watcher *watcher;
173 GSList *entry = sip->watcher;
174 while (entry) {
175 watcher = entry->data;
176 if (!strcmp(name, watcher->name)) return watcher;
177 entry = entry->next;
179 return NULL;
182 static struct sipe_watcher *watcher_create(struct sipe_account_data *sip,
183 const gchar *name, const gchar *callid, const gchar *ourtag,
184 const gchar *theirtag, gboolean needsxpidf)
186 struct sipe_watcher *watcher = g_new0(struct sipe_watcher, 1);
187 watcher->name = g_strdup(name);
188 watcher->dialog.callid = g_strdup(callid);
189 watcher->dialog.ourtag = g_strdup(ourtag);
190 watcher->dialog.theirtag = g_strdup(theirtag);
191 watcher->needsxpidf = needsxpidf;
192 sip->watcher = g_slist_append(sip->watcher, watcher);
193 return watcher;
196 static void watcher_remove(struct sipe_account_data *sip, const gchar *name)
198 struct sipe_watcher *watcher = watcher_find(sip, name);
199 sip->watcher = g_slist_remove(sip->watcher, watcher);
200 g_free(watcher->name);
201 g_free(watcher->dialog.callid);
202 g_free(watcher->dialog.ourtag);
203 g_free(watcher->dialog.theirtag);
204 g_free(watcher);
207 static struct sip_connection *connection_create(struct sipe_account_data *sip, int fd)
209 struct sip_connection *ret = g_new0(struct sip_connection, 1);
210 ret->fd = fd;
211 sip->openconns = g_slist_append(sip->openconns, ret);
212 return ret;
215 static void connection_remove(struct sipe_account_data *sip, int fd)
217 struct sip_connection *conn = connection_find(sip, fd);
218 sip->openconns = g_slist_remove(sip->openconns, conn);
219 if (conn->inputhandler) purple_input_remove(conn->inputhandler);
220 g_free(conn->inbuf);
221 g_free(conn);
224 static void connection_free_all(struct sipe_account_data *sip)
226 struct sip_connection *ret = NULL;
227 GSList *entry = sip->openconns;
228 while (entry) {
229 ret = entry->data;
230 connection_remove(sip, ret->fd);
231 entry = sip->openconns;
235 static gchar *auth_header_without_newline(struct sipe_account_data *sip, struct sip_auth *auth, struct sipmsg * msg, gboolean force_reauth)
237 const gchar *method = msg->method;
238 const gchar *target = msg->target;
239 gchar noncecount[9];
240 gchar *response;
241 gchar *ret;
242 gchar *tmp;
243 const char *authdomain;
244 const char *authuser;
245 const char *krb5_realm;
246 const char *host;
247 gchar *krb5_token = NULL;
249 authdomain = purple_account_get_string(sip->account, "authdomain", "");
250 authuser = purple_account_get_string(sip->account, "authuser", sip->username);
252 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
253 // and do error checking
255 // KRB realm should always be uppercase
256 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
258 if (sip->realhostname) {
259 host = sip->realhostname;
260 } else if (purple_account_get_bool(sip->account, "useproxy", TRUE)) {
261 host = purple_account_get_string(sip->account, "proxy", "");
262 } else {
263 host = sip->sipdomain;
266 /*gboolean new_auth = krb5_auth.gss_context == NULL;
267 if (new_auth) {
268 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
271 if (new_auth || force_reauth) {
272 krb5_token = krb5_auth.base64_token;
275 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
276 krb5_token = krb5_auth.base64_token;*/
278 if (!authuser || strlen(authuser) < 1) {
279 authuser = sip->username;
282 if (auth->type == 1) { /* Digest */
283 sprintf(noncecount, "%08d", auth->nc++);
284 response = purple_cipher_http_digest_calculate_response(
285 "md5", method, target, NULL, NULL,
286 auth->nonce, noncecount, NULL, auth->digest_session_key);
287 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response %s\n", response);
289 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);
290 g_free(response);
291 return ret;
292 } else if (auth->type == 2) { /* NTLM */
293 // If we have a signature for the message, include that
294 if (msg->signature) {
295 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);
296 return tmp;
299 if (auth->nc == 3 && auth->nonce && auth->ntlm_key == NULL) {
300 /* TODO: Don't hardcode "purple" as the hostname */
301 const gchar * ntlm_key;
302 gchar * gssapi_data = purple_ntlm_gen_authenticate(&ntlm_key, authuser, sip->password, "purple", authdomain, (const guint8 *)auth->nonce, &auth->flags);
303 auth->ntlm_key = (gchar *)ntlm_key;
304 tmp = g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth->opaque, auth->realm, auth->target, gssapi_data);
305 g_free(gssapi_data);
306 return tmp;
309 tmp = g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth->realm, auth->target);
310 return tmp;
311 } else if (auth->type == 3) {
312 /* Kerberos */
313 if (auth->nc == 3) {
314 /*if (new_auth || force_reauth) {
315 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
316 if (auth->opaque) {
317 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);
318 } else {
319 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
321 } else {
322 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
323 gchar * mic = "MICTODO";
324 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
325 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
326 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
327 //auth->opaque ? auth->opaque : "", auth->target);
328 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
329 //g_free(mic);
331 return tmp;
333 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth->target);
336 sprintf(noncecount, "%08d", auth->nc++);
337 response = purple_cipher_http_digest_calculate_response(
338 "md5", method, target, NULL, NULL,
339 auth->nonce, noncecount, NULL, auth->digest_session_key);
340 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response %s\n", response);
342 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);
343 g_free(response);
344 return ret;
347 static gchar *auth_header(struct sipe_account_data *sip, struct sip_auth *auth, struct sipmsg * msg, gboolean force_reauth)
349 gchar *with, *without;
351 without = auth_header_without_newline(sip, auth, msg, force_reauth);
352 with = g_strdup_printf("%s\r\n", without);
353 g_free (without);
354 return with;
358 static char *parse_attribute(const char *attrname, const char *source)
360 const char *tmp, *tmp2;
361 char *retval = NULL;
362 int len = strlen(attrname);
364 if (!strncmp(source, attrname, len)) {
365 tmp = source + len;
366 tmp2 = g_strstr_len(tmp, strlen(tmp), "\"");
367 if (tmp2)
368 retval = g_strndup(tmp, tmp2 - tmp);
369 else
370 retval = g_strdup(tmp);
373 return retval;
376 static void fill_auth(struct sipe_account_data *sip, gchar *hdr, struct sip_auth *auth)
378 int i = 0;
379 const char *authuser;
380 char *tmp;
381 gchar **parts;
382 const char *krb5_realm;
383 const char *host;
385 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
386 // and do error checking
388 // KRB realm should always be uppercase
389 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
391 if (sip->realhostname) {
392 host = sip->realhostname;
393 } else if (purple_account_get_bool(sip->account, "useproxy", TRUE)) {
394 host = purple_account_get_string(sip->account, "proxy", "");
395 } else {
396 host = sip->sipdomain;
399 authuser = purple_account_get_string(sip->account, "authuser", sip->username);
401 if (!authuser || strlen(authuser) < 1) {
402 authuser = sip->username;
405 if (!hdr) {
406 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
407 return;
410 if (!g_strncasecmp(hdr, "NTLM", 4)) {
411 auth->type = 2;
412 parts = g_strsplit(hdr+5, "\", ", 0);
413 i = 0;
414 while (parts[i]) {
415 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
416 if ((tmp = parse_attribute("gssapi-data=\"", parts[i]))) {
417 auth->nonce = g_memdup(purple_ntlm_parse_challenge(tmp, &auth->flags), 8);
418 g_free(tmp);
420 if ((tmp = parse_attribute("targetname=\"",
421 parts[i]))) {
422 auth->target = tmp;
424 else if ((tmp = parse_attribute("realm=\"",
425 parts[i]))) {
426 auth->realm = tmp;
428 else if ((tmp = parse_attribute("opaque=\"", parts[i]))) {
429 auth->opaque = tmp;
431 i++;
433 g_strfreev(parts);
434 auth->nc = 1;
435 if (!strstr(hdr, "gssapi-data")) {
436 auth->nc = 1;
437 } else {
438 auth->nc = 3;
440 return;
443 if (!g_strncasecmp(hdr, "Kerberos", 8)) {
444 purple_debug(PURPLE_DEBUG_MISC, "sipe", "setting auth type to Kerberos (3)\r\n");
445 auth->type = 3;
446 purple_debug(PURPLE_DEBUG_MISC, "sipe", "fill_auth - header: %s\r\n", hdr);
447 parts = g_strsplit(hdr+9, "\", ", 0);
448 i = 0;
449 while (parts[i]) {
450 purple_debug_info("sipe", "krb - parts[i] %s\n", parts[i]);
451 if ((tmp = parse_attribute("gssapi-data=\"", parts[i]))) {
452 /*if (krb5_auth.gss_context == NULL) {
453 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
455 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
456 g_free(tmp);
458 if ((tmp = parse_attribute("targetname=\"", parts[i]))) {
459 auth->target = tmp;
460 } else if ((tmp = parse_attribute("realm=\"", parts[i]))) {
461 auth->realm = tmp;
462 } else if ((tmp = parse_attribute("opaque=\"", parts[i]))) {
463 auth->opaque = tmp;
465 i++;
467 g_strfreev(parts);
468 auth->nc = 3;
469 //if (!strstr(hdr, "gssapi-data")) {
470 // auth->nc = 1;
471 //} else {
472 // auth->nc = 3;
474 return;
477 auth->type = 1;
478 parts = g_strsplit(hdr, " ", 0);
479 while (parts[i]) {
480 if ((tmp = parse_attribute("nonce=\"", parts[i]))) {
481 auth->nonce = tmp;
483 else if ((tmp = parse_attribute("realm=\"", parts[i]))) {
484 auth->realm = tmp;
486 i++;
488 g_strfreev(parts);
490 purple_debug(PURPLE_DEBUG_MISC, "sipe", "nonce: %s realm: %s\n", auth->nonce ? auth->nonce : "(null)", auth->realm ? auth->realm : "(null)");
491 if (auth->realm) {
492 auth->digest_session_key = purple_cipher_http_digest_calculate_session_key(
493 "md5", authuser, auth->realm, sip->password, auth->nonce, NULL);
495 auth->nc = 1;
499 static void sipe_canwrite_cb(gpointer data, gint source, PurpleInputCondition cond)
501 PurpleConnection *gc = data;
502 struct sipe_account_data *sip = gc->proto_data;
503 gsize max_write;
504 gssize written;
506 max_write = purple_circ_buffer_get_max_read(sip->txbuf);
508 if (max_write == 0) {
509 if (sip->tx_handler != 0){
510 purple_input_remove(sip->tx_handler);
511 sip->tx_handler = 0;
513 return;
516 written = write(sip->fd, sip->txbuf->outptr, max_write);
518 if (written < 0 && errno == EAGAIN)
519 written = 0;
520 else if (written <= 0) {
521 /*TODO: do we really want to disconnect on a failure to write?*/
522 purple_connection_error(gc, _("Could not write"));
523 return;
526 purple_circ_buffer_mark_read(sip->txbuf, written);
529 static void sipe_canwrite_cb_ssl(gpointer data, gint src, PurpleInputCondition cond)
531 PurpleConnection *gc = data;
532 struct sipe_account_data *sip = gc->proto_data;
533 gsize max_write;
534 gssize written;
536 max_write = purple_circ_buffer_get_max_read(sip->txbuf);
538 if (max_write == 0) {
539 if (sip->tx_handler != 0) {
540 purple_input_remove(sip->tx_handler);
541 sip->tx_handler = 0;
542 return;
546 written = purple_ssl_write(sip->gsc, sip->txbuf->outptr, max_write);
548 if (written < 0 && errno == EAGAIN)
549 written = 0;
550 else if (written <= 0) {
551 /*TODO: do we really want to disconnect on a failure to write?*/
552 purple_connection_error(gc, _("Could not write"));
553 return;
556 purple_circ_buffer_mark_read(sip->txbuf, written);
559 static void sipe_input_cb(gpointer data, gint source, PurpleInputCondition cond);
561 static void send_later_cb(gpointer data, gint source, const gchar *error)
563 PurpleConnection *gc = data;
564 struct sipe_account_data *sip;
565 struct sip_connection *conn;
567 if (!PURPLE_CONNECTION_IS_VALID(gc))
569 if (source >= 0)
570 close(source);
571 return;
574 if (source < 0) {
575 purple_connection_error(gc, _("Could not connect"));
576 return;
579 sip = gc->proto_data;
580 sip->fd = source;
581 sip->connecting = FALSE;
583 sipe_canwrite_cb(gc, sip->fd, PURPLE_INPUT_WRITE);
585 /* If there is more to write now, we need to register a handler */
586 if (sip->txbuf->bufused > 0)
587 sip->tx_handler = purple_input_add(sip->fd, PURPLE_INPUT_WRITE, sipe_canwrite_cb, gc);
589 conn = connection_create(sip, source);
590 conn->inputhandler = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_input_cb, gc);
593 static void send_later_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
595 PurpleConnection *gc = data;
596 struct sipe_account_data *sip;
597 struct sip_connection *conn;
599 if (!PURPLE_CONNECTION_IS_VALID(gc))
601 if(gsc) purple_ssl_close(gsc);
602 return;
605 sip = gc->proto_data;
606 sip->gsc = gsc;
607 sip->fd = gsc->fd;
608 sip->connecting = FALSE;
610 sipe_canwrite_cb_ssl(gc, sip->gsc->fd, PURPLE_INPUT_WRITE);
612 /* If there is more to write now */
613 if (sip->txbuf->bufused > 0)
615 sip->tx_handler = purple_input_add(sip->gsc->fd, PURPLE_INPUT_WRITE, sipe_canwrite_cb_ssl, gc);
618 conn = connection_create(sip, sip->gsc->fd);
619 purple_ssl_input_add(sip->gsc, sipe_input_cb_ssl, gc);
623 static void sendlater(PurpleConnection *gc, const char *buf)
625 struct sipe_account_data *sip = gc->proto_data;
627 if (!sip->connecting) {
628 purple_debug_info("sipe", "connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport);
629 if (sip->transport == SIPE_TRANSPORT_TLS){
630 sip->gsc = purple_ssl_connect(sip->account,sip->realhostname, sip->realport, send_later_cb_ssl, sipe_ssl_connect_failure, sip->gc);
631 } else {
632 if (purple_proxy_connect(gc, sip->account, sip->realhostname, sip->realport, send_later_cb, gc) == NULL) {
633 purple_connection_error(gc, _("Couldn't create socket"));
636 sip->connecting = TRUE;
639 if (purple_circ_buffer_get_max_read(sip->txbuf) > 0)
640 purple_circ_buffer_append(sip->txbuf, "\r\n", 2);
642 purple_circ_buffer_append(sip->txbuf, buf, strlen(buf));
645 static void sendout_pkt(PurpleConnection *gc, const char *buf)
647 struct sipe_account_data *sip = gc->proto_data;
648 time_t currtime = time(NULL);
649 int writelen = strlen(buf);
651 purple_debug(PURPLE_DEBUG_MISC, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime), buf);
652 if (sip->transport == SIPE_TRANSPORT_UDP) {
653 if (sendto(sip->fd, buf, writelen, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)) < writelen) {
654 purple_debug_info("sipe", "could not send packet\n");
656 } else {
657 int ret;
658 if (sip->fd < 0) {
659 sendlater(gc, buf);
660 return;
663 if (sip->tx_handler) {
664 ret = -1;
665 errno = EAGAIN;
666 } else{
667 if (sip->gsc){
668 ret = purple_ssl_write(sip->gsc, buf, writelen);
669 }else{
670 ret = write(sip->fd, buf, writelen);
674 if (ret < 0 && errno == EAGAIN)
675 ret = 0;
676 else if (ret <= 0) { /* XXX: When does this happen legitimately? */
677 sendlater(gc, buf);
678 return;
681 if (ret < writelen) {
682 if (!sip->tx_handler){
683 if (sip->gsc){
684 sip->tx_handler = purple_input_add(sip->gsc->fd, PURPLE_INPUT_WRITE, sipe_canwrite_cb_ssl, gc);
686 else{
687 sip->tx_handler = purple_input_add(sip->fd,
688 PURPLE_INPUT_WRITE, sipe_canwrite_cb,
689 gc);
693 /* XXX: is it OK to do this? You might get part of a request sent
694 with part of another. */
695 if (sip->txbuf->bufused > 0)
696 purple_circ_buffer_append(sip->txbuf, "\r\n", 2);
698 purple_circ_buffer_append(sip->txbuf, buf + ret,
699 writelen - ret);
704 static int sipe_send_raw(PurpleConnection *gc, const char *buf, int len)
706 sendout_pkt(gc, buf);
707 return len;
710 static void sendout_sipmsg(struct sipe_account_data *sip, struct sipmsg *msg)
712 GSList *tmp = msg->headers;
713 gchar *name;
714 gchar *value;
715 GString *outstr = g_string_new("");
716 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n", msg->method, msg->target);
717 while (tmp) {
718 name = ((struct siphdrelement*) (tmp->data))->name;
719 value = ((struct siphdrelement*) (tmp->data))->value;
720 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
721 tmp = g_slist_next(tmp);
723 g_string_append_printf(outstr, "\r\n%s", msg->body ? msg->body : "");
724 sendout_pkt(sip->gc, outstr->str);
725 g_string_free(outstr, TRUE);
728 static void sign_outgoing_message (struct sipmsg * msg, struct sipe_account_data *sip, const gchar *method)
730 gchar * buf;
731 if (sip->registrar.ntlm_key) {
732 struct sipmsg_breakdown msgbd;
733 msgbd.msg = msg;
734 sipmsg_breakdown_parse(&msgbd, sip->registrar.realm, sip->registrar.target);
735 // TODO generate this
736 msgbd.rand = g_strdup("0878F41B");
737 sip->registrar.ntlm_num++;
738 msgbd.num = g_strdup_printf("%d", sip->registrar.ntlm_num);
739 gchar * signature_input_str = sipmsg_breakdown_get_string(&msgbd);
740 if (signature_input_str != NULL) {
741 msg->signature = purple_ntlm_sipe_signature_make (signature_input_str, sip->registrar.ntlm_key);
742 msg->rand = g_strdup(msgbd.rand);
743 msg->num = g_strdup(msgbd.num);
745 sipmsg_breakdown_free(&msgbd);
748 if (sip->registrar.type && !strcmp(method, "REGISTER")) {
749 buf = auth_header_without_newline(sip, &sip->registrar, msg, FALSE);
750 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
751 sipmsg_add_header(msg, "Authorization", buf);
752 } else {
753 sipmsg_add_header_pos(msg, "Proxy-Authorization", buf, 5);
754 //sipmsg_add_header_pos(msg, "Authorization", buf, 5);
756 g_free(buf);
757 } 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")) {
758 sip->registrar.nc=3;
759 sip->registrar.type=2;
761 buf = auth_header_without_newline(sip, &sip->registrar, msg, FALSE);
762 //buf = auth_header(sip, &sip->proxy, msg, FALSE);
763 sipmsg_add_header_pos(msg, "Proxy-Authorization", buf, 5);
764 //sipmsg_add_header(msg, "Authorization", buf);
765 g_free(buf);
766 } else {
767 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method);
771 static char *get_contact(struct sipe_account_data *sip)
773 return g_strdup(sip->contact);
777 static char *get_contact_service(struct sipe_account_data *sip)
779 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()));
780 //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);
783 static void send_sip_response(PurpleConnection *gc, struct sipmsg *msg, int code,
784 const char *text, const char *body)
786 gchar *name;
787 gchar *value;
788 GString *outstr = g_string_new("");
789 struct sipe_account_data *sip = gc->proto_data;
791 gchar *contact;
792 contact = get_contact(sip);
793 sipmsg_remove_header(msg, "Contact");
794 sipmsg_add_header(msg, "Contact", contact);
795 g_free(contact);
797 /* When sending the acknowlegements and errors, the content length from the original
798 message is still here, but there is no body; we need to make sure we're sending the
799 correct content length */
800 sipmsg_remove_header(msg, "Content-Length");
801 if (body) {
802 gchar len[12];
803 sprintf(len, "%" G_GSIZE_FORMAT , strlen(body));
804 sipmsg_add_header(msg, "Content-Length", len);
805 } else {
806 sipmsg_add_header(msg, "Content-Length", "0");
809 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
810 //gchar * mic = "MICTODO";
811 msg->response = code;
813 sipmsg_remove_header(msg, "Authentication-Info");
814 sign_outgoing_message(msg, sip, msg->method);
816 g_string_append_printf(outstr, "SIP/2.0 %d %s\r\n", code, text);
817 GSList *tmp = msg->headers;
818 while (tmp) {
819 name = ((struct siphdrelement*) (tmp->data))->name;
820 value = ((struct siphdrelement*) (tmp->data))->value;
822 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
823 tmp = g_slist_next(tmp);
825 g_string_append_printf(outstr, "\r\n%s", body ? body : "");
826 sendout_pkt(gc, outstr->str);
827 g_string_free(outstr, TRUE);
830 static void transactions_remove(struct sipe_account_data *sip, struct transaction *trans)
832 if (trans->msg) sipmsg_free(trans->msg);
833 sip->transactions = g_slist_remove(sip->transactions, trans);
834 g_free(trans);
837 static struct transaction *
838 transactions_add_buf(struct sipe_account_data *sip, const struct sipmsg *msg, void *callback)
840 struct transaction *trans = g_new0(struct transaction, 1);
841 trans->time = time(NULL);
842 trans->msg = (struct sipmsg *)msg;
843 trans->cseq = sipmsg_find_header(trans->msg, "CSeq");
844 trans->callback = callback;
845 sip->transactions = g_slist_append(sip->transactions, trans);
846 return trans;
849 static struct transaction *transactions_find(struct sipe_account_data *sip, struct sipmsg *msg)
851 struct transaction *trans;
852 GSList *transactions = sip->transactions;
853 gchar *cseq = sipmsg_find_header(msg, "CSeq");
855 while (transactions) {
856 trans = transactions->data;
857 if (!strcmp(trans->cseq, cseq)) {
858 return trans;
860 transactions = transactions->next;
863 return NULL;
866 static struct transaction *
867 send_sip_request(PurpleConnection *gc, const gchar *method,
868 const gchar *url, const gchar *to, const gchar *addheaders,
869 const gchar *body, struct sip_dialog *dialog, TransCallback tc)
871 struct sipe_account_data *sip = gc->proto_data;
872 const char *addh = "";
873 char *buf;
874 struct sipmsg *msg;
875 gchar *ptmp;
876 gchar *ourtag = dialog && dialog->ourtag ? g_strdup(dialog->ourtag) : NULL;
877 gchar *theirtag = dialog && dialog->theirtag ? g_strdup(dialog->theirtag) : NULL;
878 gchar *theirepid = dialog && dialog->theirepid ? g_strdup(dialog->theirepid) : NULL;
879 gchar *callid = dialog && dialog->callid ? g_strdup(dialog->callid) : gencallid();
880 gchar *branch = dialog && dialog->callid ? NULL : genbranch();
881 gchar *useragent = (gchar *)purple_account_get_string(sip->account, "useragent", "Purple/" VERSION);
883 if (!ourtag && !dialog) {
884 ourtag = gentag();
887 if (!strcmp(method, "REGISTER")) {
888 if (sip->regcallid) {
889 g_free(callid);
890 callid = g_strdup(sip->regcallid);
891 } else {
892 sip->regcallid = g_strdup(callid);
896 if (addheaders) addh = addheaders;
898 buf = g_strdup_printf("%s %s SIP/2.0\r\n"
899 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
900 "From: <sip:%s>%s%s;epid=%s\r\n"
901 "To: <%s>%s%s%s%s\r\n"
902 "Max-Forwards: 70\r\n"
903 "CSeq: %d %s\r\n"
904 "User-Agent: %s\r\n"
905 "Call-ID: %s\r\n"
906 "%s%s"
907 "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n%s",
908 method,
909 dialog && dialog->request ? dialog->request : url,
910 TRANSPORT_DESCRIPTOR,
911 purple_network_get_my_ip(-1),
912 sip->listenport,
913 branch ? ";branch=" : "",
914 branch ? branch : "",
915 sip->username,
916 ourtag ? ";tag=" : "",
917 ourtag ? ourtag : "",
918 get_epid(), // TODO generate one per account/login
920 theirtag ? ";tag=" : "",
921 theirtag ? theirtag : "",
922 theirepid ? ";epid=" : "",
923 theirepid ? theirepid : "",
924 dialog ? ++dialog->cseq : ++sip->cseq,
925 method,
926 useragent,
927 callid,
928 dialog && dialog->route ? dialog->route : "",
929 addh,
930 body ? strlen(body) : 0,
931 body ? body : "");
934 //printf ("parsing msg buf:\n%s\n\n", buf);
935 msg = sipmsg_parse_msg(buf);
937 g_free(buf);
938 g_free(ourtag);
939 g_free(theirtag);
940 g_free(theirepid);
941 g_free(branch);
942 g_free(callid);
944 sign_outgoing_message (msg, sip, method);
946 buf = sipmsg_to_string (msg);
948 /* add to ongoing transactions */
949 struct transaction * trans = transactions_add_buf(sip, msg, tc);
950 sendout_pkt(gc, buf);
952 return trans;
955 static void send_soap_request_with_cb(struct sipe_account_data *sip, gchar *body, TransCallback callback, void * payload)
957 gchar *from = g_strdup_printf("sip:%s", sip->username);
958 gchar *hdr = g_strdup("Content-Type: application/SOAP+xml\r\n");
960 struct transaction * tr = send_sip_request(sip->gc, "SERVICE", from, from, hdr, body, NULL, callback);
961 tr->payload = payload;
963 g_free(from);
964 g_free(hdr);
967 static void send_soap_request(struct sipe_account_data *sip, gchar *body)
969 send_soap_request_with_cb(sip, body, NULL, NULL);
972 static char *get_contact_register(struct sipe_account_data *sip)
974 return g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, BYE, CANCEL, NOTIFY, ACK, BENOTIFY\";proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip->listenport, TRANSPORT_DESCRIPTOR, generateUUIDfromEPID(get_epid()));
977 static void do_register_exp(struct sipe_account_data *sip, int expire)
979 char *uri = g_strdup_printf("sip:%s", sip->sipdomain);
980 char *to = g_strdup_printf("sip:%s", sip->username);
981 char *contact = get_contact_register(sip);
982 //char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire);
983 // char *hdr = g_strdup_printf("Contact: %s\r\nEvent: registration\r\nAllow-Events: presence\r\nms-keep-alive: UAC;hop-hop=yes\r\nExpires: %d\r\n", contact,expire);
984 //char *hdr = g_strdup_printf("Contact: %s\r\nSupported: com.microsoft.msrtc.presence, adhoclist\r\nms-keep-alive: UAC;hop-hop=yes\r\nEvent: registration\r\nAllow-Events: presence\r\n", contact);
985 //char *hdr = g_strdup_printf("Contact: %s\r\nSupported: com.microsoft.msrtc.presence, gruu-10, adhoclist\r\nEvent: registration\r\nAllow-Events: presence\r\nms-keep-alive: UAC;hop-hop=yes\r\nExpires: %d\r\n", contact,expire);
986 char *hdr = g_strdup_printf("Contact: %s\r\n"
987 "Supported: gruu-10, adhoclist\r\n"
988 "Event: registration\r\n"
989 "Allow-Events: presence\r\n"
990 "ms-keep-alive: UAC;hop-hop=yes\r\n"
991 "Expires: %d\r\n", contact,expire);
992 g_free(contact);
994 sip->registerstatus = 1;
996 if (expire) {
997 sip->reregister = time(NULL) + expire - 50;
998 } else {
999 sip->reregister = time(NULL) + 600;
1002 send_sip_request(sip->gc, "REGISTER", uri, to, hdr, "", NULL,
1003 process_register_response);
1005 g_free(hdr);
1006 g_free(uri);
1007 g_free(to);
1010 static void do_register(struct sipe_account_data *sip)
1012 do_register_exp(sip, sip->registerexpire);
1015 static gchar *parse_from(const gchar *hdr)
1017 gchar *from;
1018 const gchar *tmp, *tmp2 = hdr;
1020 if (!hdr) return NULL;
1021 purple_debug_info("sipe", "parsing address out of %s\n", hdr);
1022 tmp = strchr(hdr, '<');
1024 /* i hate the different SIP UA behaviours... */
1025 if (tmp) { /* sip address in <...> */
1026 tmp2 = tmp + 1;
1027 tmp = strchr(tmp2, '>');
1028 if (tmp) {
1029 from = g_strndup(tmp2, tmp - tmp2);
1030 } else {
1031 purple_debug_info("sipe", "found < without > in From\n");
1032 return NULL;
1034 } else {
1035 tmp = strchr(tmp2, ';');
1036 if (tmp) {
1037 from = g_strndup(tmp2, tmp - tmp2);
1038 } else {
1039 from = g_strdup(tmp2);
1042 purple_debug_info("sipe", "got %s\n", from);
1043 return from;
1046 static xmlnode * xmlnode_get_descendant(xmlnode * parent, ...)
1048 va_list args;
1049 xmlnode * node;
1050 const gchar * name;
1052 va_start(args, parent);
1053 while ((name = va_arg(args, const char *)) != NULL) {
1054 node = xmlnode_get_child(parent, name);
1055 if (node == NULL) return NULL;
1056 parent = node;
1058 va_end(args);
1060 return node;
1063 static void
1064 sipe_group_add (struct sipe_account_data *sip, struct sipe_group * group)
1066 PurpleGroup * purple_group = purple_find_group(group->name);
1067 if (!purple_group) {
1068 purple_group = purple_group_new(group->name);
1069 purple_blist_add_group(purple_group, NULL);
1072 if (purple_group) {
1073 group->purple_group = purple_group;
1074 sip->groups = g_slist_append(sip->groups, group);
1075 purple_debug_info("sipe", "added group %s (id %d)\n", group->name, group->id);
1076 } else {
1077 purple_debug_info("sipe", "did not add group %s\n", group->name);
1081 static struct sipe_group * sipe_group_find_by_id (struct sipe_account_data *sip, int id)
1083 if (sip == NULL) {
1084 return NULL;
1087 struct sipe_group *group;
1088 GSList *entry = sip->groups;
1089 while (entry) {
1090 group = entry->data;
1091 if (group->id == id) {
1092 return group;
1094 entry = entry->next;
1096 return NULL;
1099 static struct sipe_group * sipe_group_find_by_name (struct sipe_account_data *sip, gchar * name)
1101 if (sip == NULL) {
1102 return NULL;
1105 struct sipe_group *group;
1106 GSList *entry = sip->groups;
1107 while (entry) {
1108 group = entry->data;
1109 if (!strcmp(group->name, name)) {
1110 return group;
1112 entry = entry->next;
1114 return NULL;
1117 static void
1118 sipe_group_rename (struct sipe_account_data *sip, struct sipe_group * group, gchar * name)
1120 purple_debug_info("sipe", "Renaming group %s to %s\n", group->name, name);
1121 gchar * body = g_strdup_printf(SIPE_SOAP_MOD_GROUP, group->id, name, sip->delta_num++);
1122 send_soap_request(sip, body);
1123 g_free(body);
1124 g_free(group->name);
1125 group->name = g_strdup(name);
1128 static void
1129 sipe_group_set_user (struct sipe_account_data *sip, struct sipe_group * group, const gchar * who)
1131 struct sipe_buddy *buddy = g_hash_table_lookup(sip->buddies, who);
1132 PurpleBuddy * purple_buddy = purple_find_buddy (sip->account, who);
1134 if (!group) {
1135 group = sipe_group_find_by_id (sip, buddy->group_id);
1137 buddy->group_id = group ? group->id : 1;
1139 if (buddy && purple_buddy) {
1140 gchar * alias = purple_buddy_get_alias(purple_buddy);
1141 purple_debug_info("sipe", "Saving buddy %s with alias %s and group_id %d\n", who, alias, buddy->group_id);
1142 gchar * body = g_strdup_printf(SIPE_SOAP_SET_CONTACT,
1143 alias, buddy->group_id, "true", buddy->name, sip->delta_num++
1145 send_soap_request(sip, body);
1146 g_free(body);
1150 static gboolean process_add_group_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1152 if (msg->response == 200) {
1153 struct sipe_group * group = g_new0(struct sipe_group, 1);
1155 struct group_user_context * ctx = (struct group_user_context*)tc->payload;
1156 group->name = ctx->group_name;
1158 xmlnode * xml = xmlnode_from_str(msg->body, msg->bodylen);
1159 if (!xml) return FALSE;
1161 xmlnode * node = xmlnode_get_descendant(xml, "Body", "addGroup", "groupID", NULL);
1162 if (!node) return FALSE;
1164 char * group_id = xmlnode_get_data(node);
1165 if (!group_id) return FALSE;
1167 group->id = (int)g_ascii_strtod(group_id, NULL);
1169 sipe_group_add(sip, group);
1170 sipe_group_set_user(sip, group, ctx->user_name);
1172 g_free(ctx);
1173 xmlnode_free(xml);
1174 return TRUE;
1176 return FALSE;
1179 static void sipe_group_create (struct sipe_account_data *sip, gchar *name, gchar * who)
1181 struct group_user_context * ctx = g_new0(struct group_user_context, 1);
1182 ctx->group_name = g_strdup(name);
1183 ctx->user_name = g_strdup(who);
1185 gchar * body = g_strdup_printf(SIPE_SOAP_ADD_GROUP, name, sip->delta_num++);
1186 send_soap_request_with_cb(sip, body, process_add_group_response, ctx);
1187 g_free(body);
1190 static gboolean process_subscribe_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1192 gchar *to;
1194 if (msg->response == 200 || msg->response == 202) {
1195 return TRUE;
1198 to = parse_from(sipmsg_find_header(tc->msg, "To")); /* cant be NULL since it is our own msg */
1200 /* we can not subscribe -> user is offline (TODO unknown status?) */
1202 purple_prpl_got_user_status(sip->account, to, "offline", NULL);
1203 g_free(to);
1204 return TRUE;
1207 static void sipe_subscribe_to_name(struct sipe_account_data *sip, const char * buddy_name)
1209 gchar *to = strstr(buddy_name, "sip:") ? g_strdup(buddy_name) : g_strdup_printf("sip:%s", buddy_name);
1210 gchar *tmp = get_contact(sip);
1211 gchar *contact = g_strdup_printf(
1212 "Accept: application/pidf+xml, application/xpidf+xml\r\n"
1213 "Event: presence\r\n"
1214 "Contact: %s\r\n", tmp);
1215 g_free(tmp);
1217 /* subscribe to buddy presence */
1218 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL, process_subscribe_response);
1220 g_free(to);
1221 g_free(contact);
1224 static void sipe_subscribe(struct sipe_account_data *sip, struct sipe_buddy *buddy)
1226 sipe_subscribe_to_name(sip, buddy->name);
1228 /* resubscribe before subscription expires */
1229 /* add some jitter */
1230 buddy->resubscribe = time(NULL)+1140+(rand()%50);
1233 static void sipe_set_status(PurpleAccount *account, PurpleStatus *status)
1235 PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_type(status));
1236 struct sipe_account_data *sip = NULL;
1238 if (!purple_status_is_active(status))
1239 return;
1241 if (account->gc)
1242 sip = account->gc->proto_data;
1244 if (sip) {
1245 g_free(sip->status);
1247 if (primitive == PURPLE_STATUS_AWAY) {
1248 sip->status = g_strdup("away");
1249 } else if (primitive == PURPLE_STATUS_AVAILABLE) {
1250 sip->status = g_strdup("available");
1251 } else if (primitive == PURPLE_STATUS_UNAVAILABLE) {
1252 sip->status = g_strdup("busy");
1255 send_presence_info(sip);
1259 static void
1260 sipe_alias_buddy(PurpleConnection *gc, const char *name, const char *alias)
1262 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1263 sipe_group_set_user(sip, NULL, name);
1266 static void
1267 sipe_group_buddy(PurpleConnection *gc,
1268 const char *who,
1269 const char *old_group_name,
1270 const char *new_group_name)
1272 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1273 struct sipe_group * group = sipe_group_find_by_name(sip, new_group_name);
1274 if (!group) {
1275 sipe_group_create(sip, new_group_name, who);
1276 } else {
1277 sipe_group_set_user(sip, group, who);
1281 static void sipe_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
1283 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1284 struct sipe_buddy *b;
1286 // Prepend sip: if needed
1287 if (strncmp("sip:", buddy->name, 4)) {
1288 gchar *buf = g_strdup_printf("sip:%s", buddy->name);
1289 purple_blist_rename_buddy(buddy, buf);
1290 g_free(buf);
1293 if (!g_hash_table_lookup(sip->buddies, buddy->name)) {
1294 b = g_new0(struct sipe_buddy, 1);
1295 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy->name);
1296 b->name = g_strdup(buddy->name);
1297 g_hash_table_insert(sip->buddies, b->name, b);
1299 sipe_group_buddy(gc, b->name, NULL, group->name);
1300 } else {
1301 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy->name);
1305 // Not Used; Remove?
1306 /*static void sipe_get_buddies(PurpleConnection *gc)
1308 PurpleBlistNode *gnode, *cnode, *bnode;
1310 purple_debug_info("sipe", "sipe_get_buddies\n");
1312 for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
1313 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue;
1314 for (cnode = gnode->child; cnode; cnode = cnode->next) {
1315 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue;
1316 for (bnode = cnode->child; bnode; bnode = bnode->next) {
1317 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue;
1318 if (((PurpleBuddy*)bnode)->account == gc->account)
1319 sipe_add_buddy(gc, (PurpleBuddy*)bnode, (PurpleGroup *)gnode);
1325 static void sipe_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
1327 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1328 struct sipe_buddy *b = g_hash_table_lookup(sip->buddies, buddy->name);
1330 if (!b) return;
1331 g_hash_table_remove(sip->buddies, buddy->name);
1333 if (b->name) {
1334 gchar * body = g_strdup_printf(SIPE_SOAP_DEL_CONTACT, b->name, sip->delta_num++);
1335 send_soap_request(sip, body);
1336 g_free(body);
1339 g_free(b->name);
1340 g_free(b);
1343 static void
1344 sipe_rename_group(PurpleConnection *gc,
1345 const char *old_name,
1346 PurpleGroup *group,
1347 GList *moved_buddies)
1349 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1350 struct sipe_group * s_group = sipe_group_find_by_name(sip, old_name);
1351 if (group) {
1352 sipe_group_rename(sip, s_group, group->name);
1353 } else {
1354 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name);
1358 static void
1359 sipe_remove_group(PurpleConnection *gc, PurpleGroup *group)
1361 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1362 struct sipe_group * s_group = sipe_group_find_by_name(sip, group->name);
1363 if (s_group) {
1364 purple_debug_info("sipe", "Deleting group %s\n", group->name);
1365 gchar * body = g_strdup_printf(SIPE_SOAP_DEL_GROUP, s_group->id, sip->delta_num++);
1366 send_soap_request(sip, body);
1367 g_free(body);
1369 sip->groups = g_slist_remove(sip->groups, s_group);
1370 g_free(s_group->name);
1371 } else {
1372 purple_debug_info("sipe", "Cannot find group %s to delete\n", group->name);
1376 static GList *sipe_status_types(PurpleAccount *acc)
1378 PurpleStatusType *type;
1379 GList *types = NULL;
1381 // Available
1382 type = purple_status_type_new_with_attrs(
1383 PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
1384 // Translators: noun
1385 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
1386 NULL);
1387 types = g_list_append(types, type);
1389 // Away
1390 type = purple_status_type_new_with_attrs(
1391 PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
1392 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
1393 NULL);
1394 types = g_list_append(types, type);
1396 // Busy
1397 type = purple_status_type_new_with_attrs(
1398 PURPLE_STATUS_UNAVAILABLE, "busy", _("Busy"), TRUE, TRUE, FALSE,
1399 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
1400 NULL);
1401 types = g_list_append(types, type);
1403 // Offline
1404 type = purple_status_type_new_full(
1405 PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE);
1406 types = g_list_append(types, type);
1408 return types;
1411 static gboolean sipe_add_lcs_contacts(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1413 int len = msg->bodylen;
1415 gchar *tmp = sipmsg_find_header(msg, "Event");
1416 if (!tmp || strncmp(tmp, "vnd-microsoft-roaming-contacts", 30)) {
1417 return FALSE;
1420 /* Convert the contact from XML to Purple Buddies */
1421 xmlnode * isc = xmlnode_from_str(msg->body, len);
1422 if (!isc) {
1423 return FALSE;
1426 char * delta_num = xmlnode_get_attrib(isc, "deltaNum");
1427 if (delta_num) {
1428 sip->delta_num = (int)g_ascii_strtod(delta_num, NULL);
1431 /* Parse groups */
1432 xmlnode *group_node;
1433 for (group_node = xmlnode_get_child(isc, "group"); group_node; group_node = xmlnode_get_next_twin(group_node)) {
1434 struct sipe_group * group = g_new0(struct sipe_group, 1);
1436 group->name = xmlnode_get_attrib(group_node, "name");
1437 if (!strncmp(group->name, "~", 1)){
1438 // TODO translate
1439 group->name = "General";
1441 group->name = g_strdup(group->name);
1442 group->id = (int)g_ascii_strtod(xmlnode_get_attrib(group_node, "id"), NULL);
1444 sipe_group_add(sip, group);
1447 // Make sure we have at least one group
1448 if (g_slist_length(sip->groups) == 0) {
1449 struct sipe_group * group = g_new0(struct sipe_group, 1);
1450 // TODO translate
1451 group->name = g_strdup("General");
1452 group->id = 1;
1453 PurpleGroup * purple_group = purple_group_new(group->name);
1454 purple_blist_add_group(purple_group, NULL);
1455 sip->groups = g_slist_append(sip->groups, group);
1458 /* Parse contacts */
1459 xmlnode *item;
1460 for (item = xmlnode_get_child(isc, "contact"); item; item = xmlnode_get_next_twin(item)) {
1461 char * uri = xmlnode_get_attrib(item, "uri");
1462 char * name = xmlnode_get_attrib(item, "name");
1463 gchar **item_groups = g_strsplit(xmlnode_get_attrib(item, "groups"), " ", 0);
1465 struct sipe_group * group = NULL;
1467 // Find the first group this contact belongs to; that's where we'll place it in the buddy list
1468 if (item_groups[0]) {
1469 group = sipe_group_find_by_id(sip, g_ascii_strtod(item_groups[0], NULL));
1472 // If couldn't find the right group for this contact, just put them in the first group we have
1473 if (group == NULL && g_slist_length(sip->groups) > 0) {
1474 group = sip->groups->data;
1477 if (group != NULL) {
1478 char * buddy_name = g_strdup_printf("sip:%s", uri);
1480 //b = purple_find_buddy(sip->account, buddy_name);
1481 PurpleBuddy *b = purple_find_buddy_in_group(sip->account, buddy_name, group->purple_group);
1482 if (!b){
1483 b = purple_buddy_new(sip->account, buddy_name, uri);
1485 g_free(buddy_name);
1487 purple_blist_add_buddy(b, NULL, group->purple_group, NULL);
1489 if (name != NULL && strlen(name) != 0) {
1490 purple_blist_alias_buddy(b, name);
1491 } else {
1492 purple_blist_alias_buddy(b, uri);
1495 struct sipe_buddy * buddy = g_new0(struct sipe_buddy, 1);
1496 buddy->name = g_strdup(b->name);
1497 buddy->group_id = group->id;
1498 g_hash_table_insert(sip->buddies, buddy->name, buddy);
1500 purple_debug_info("sipe", "Added buddy %s to group %s\n", buddy->name, group->name);
1501 } else {
1502 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
1503 name);
1507 xmlnode_free(isc);
1509 return 0;
1512 static void sipe_subscribe_buddylist(struct sipe_account_data *sip,struct sipmsg *msg)
1514 gchar *to = g_strdup_printf("sip:%s", sip->username);
1515 gchar *tmp = get_contact(sip);
1516 gchar *hdr = g_strdup_printf("Event: vnd-microsoft-roaming-contacts\r\n"
1517 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
1518 "Supported: com.microsoft.autoextend\r\n"
1519 "Supported: ms-benotify\r\n"
1520 "Proxy-Require: ms-benotify\r\n"
1521 "Supported: ms-piggyback-first-notify\r\n"
1522 "Contact: %s\r\n", tmp);
1523 g_free(tmp);
1525 send_sip_request(sip->gc, "SUBSCRIBE", to, to, hdr, "", NULL, sipe_add_lcs_contacts);
1526 g_free(to);
1527 g_free(hdr);
1530 static void sipe_subscribe_roaming_self(struct sipe_account_data *sip,struct sipmsg *msg)
1532 gchar *to = g_strdup_printf("sip:%s", sip->username);
1533 gchar *tmp = get_contact(sip);
1534 gchar *hdr = g_strdup_printf("Event: vnd-microsoft-roaming-self\r\n"
1535 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
1536 "Supported: com.microsoft.autoextend\r\n"
1537 "Supported: ms-benotify\r\n"
1538 "Proxy-Require: ms-benotify\r\n"
1539 "Supported: ms-piggyback-first-notify\r\n"
1540 "Contact: %s\r\n"
1541 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n"
1542 ,tmp);
1544 g_free(tmp);
1546 gchar *body=g_strdup("<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\"><roaming type=\"categories\"/><roaming type=\"containers\"/><roaming type=\"subscribers\"/></roamingList>");
1548 send_sip_request(sip->gc, "SUBSCRIBE", to, to, hdr, body, NULL, NULL);
1549 g_free(body);
1550 g_free(to);
1551 g_free(hdr);
1554 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data *sip,struct sipmsg *msg)
1556 gchar *to = g_strdup_printf("sip:%s", sip->username);
1557 gchar *tmp = get_contact(sip);
1558 gchar *hdr = g_strdup_printf("Event: vnd-microsoft-provisioning-v2\r\n"
1559 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
1560 "Supported: com.microsoft.autoextend\r\n"
1561 "Supported: ms-benotify\r\n"
1562 "Proxy-Require: ms-benotify\r\n"
1563 "Supported: ms-piggyback-first-notify\r\n"
1564 "Expires: 0\r\n"
1565 "Contact: %s\r\n"
1566 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
1567 ,tmp);
1569 g_free(tmp);
1571 gchar *body=g_strdup("<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\"><provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/><provisioningGroup name=\"ucPolicy\"/></provisioningGroupList>");
1572 send_sip_request(sip->gc, "SUBSCRIBE", to, to, hdr, body, NULL, NULL);
1573 g_free(body);
1574 g_free(to);
1575 g_free(hdr);
1578 /* IM Session (INVITE and MESSAGE methods) */
1580 static struct sip_im_session * find_im_session (struct sipe_account_data *sip, const char *who)
1582 if (sip == NULL || who == NULL) {
1583 return NULL;
1586 struct sip_im_session *session;
1587 GSList *entry = sip->im_sessions;
1588 while (entry) {
1589 session = entry->data;
1590 if ((who != NULL && !strcmp(who, session->with))) {
1591 return session;
1593 entry = entry->next;
1595 return NULL;
1598 static struct sip_im_session * find_or_create_im_session (struct sipe_account_data *sip, const char *who)
1600 struct sip_im_session *session = find_im_session(sip, who);
1601 if (!session) {
1602 session = g_new0(struct sip_im_session, 1);
1603 session->with = g_strdup(who);
1604 sip->im_sessions = g_slist_append(sip->im_sessions, session);
1606 return session;
1609 static void im_session_destroy(struct sipe_account_data *sip, struct sip_im_session * session)
1611 sip->im_sessions = g_slist_remove(sip->im_sessions, session);
1612 // TODO free session resources
1615 static void sipe_send_message(struct sipe_account_data *sip, struct sip_im_session * session, const char *msg)
1617 gchar *hdr;
1618 gchar *fullto;
1619 gchar *tmp;
1621 if (strncmp("sip:", session->with, 4)) {
1622 fullto = g_strdup_printf("sip:%s", session->with);
1623 } else {
1624 fullto = g_strdup(session->with);
1627 hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1628 //hdr = g_strdup("Content-Type: text/rtf\r\n");
1629 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
1631 tmp = get_contact(sip);
1632 hdr = g_strdup_printf("Contact: %s\r\n%s", tmp, hdr);
1633 g_free(tmp);
1635 send_sip_request(sip->gc, "MESSAGE", fullto, fullto, hdr, msg, session->dialog, NULL);
1637 g_free(hdr);
1638 g_free(fullto);
1642 static void
1643 sipe_im_process_queue (struct sipe_account_data * sip, struct sip_im_session * session)
1645 GSList *entry = session->outgoing_message_queue;
1646 while (entry) {
1647 char *queued_msg = entry->data;
1648 sipe_send_message(sip, session, queued_msg);
1650 // Remove from the queue and free the string
1651 entry = session->outgoing_message_queue = g_slist_remove(session->outgoing_message_queue, queued_msg);
1652 g_free(queued_msg);
1656 static void
1657 sipe_parse_dialog(struct sipmsg * msg, struct sip_dialog * dialog, gboolean outgoing)
1659 gchar *us = outgoing ? "From" : "To";
1660 gchar *them = outgoing ? "To" : "From";
1662 dialog->callid = sipmsg_find_header(msg, "Call-ID");
1663 dialog->ourtag = find_tag(sipmsg_find_header(msg, us));
1664 dialog->theirtag = find_tag(sipmsg_find_header(msg, them));
1665 if (!dialog->theirepid) {
1666 dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, them), "epid=", ";", NULL);
1668 if (!dialog->theirepid) {
1669 dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, them), "epid=", NULL, NULL);
1672 dialog->request = sipmsg_find_part_of_header(sipmsg_find_header(msg, "Record-Route"), "<", ">", NULL);
1673 dialog->route = g_strdup_printf("Route: %s\r\n", sipmsg_find_header(msg, "Contact"));
1677 static gboolean
1678 process_invite_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *trans)
1680 gchar * with = parse_from(sipmsg_find_header(msg, "To"));
1681 struct sip_im_session * session = find_im_session(sip, with);
1682 g_free(with);
1684 if (!session) {
1685 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
1686 return FALSE;
1689 if (msg->response != 200) {
1690 purple_debug_info("sipe", "process_invite_response: INVITE response not 200, ignoring\n");
1691 im_session_destroy(sip, session);
1692 return FALSE;
1695 struct sip_dialog * dialog = session->dialog;
1696 if (!dialog) {
1697 purple_debug_info("sipe", "process_invite_response: session outgoign dialog is NULL\n");
1698 return FALSE;
1701 sipe_parse_dialog(msg, dialog, TRUE);
1702 dialog->cseq = 0;
1704 send_sip_request(sip->gc, "ACK", session->with, session->with, NULL, NULL, dialog, NULL);
1705 session->outgoing_invite = NULL;
1706 sipe_im_process_queue(sip, session);
1708 return TRUE;
1712 static void sipe_invite(struct sipe_account_data *sip, struct sip_im_session * session, gchar * msg_body)
1714 gchar *hdr;
1715 gchar *to;
1716 gchar *contact;
1717 gchar *body;
1719 if (session->dialog) {
1720 purple_debug_info("sipe", "session with %s already has a dialog open\n", session->with);
1721 return;
1724 session->dialog = g_new0(struct sip_dialog, 1);
1726 if (strstr(session->with, "sip:")) {
1727 to = g_strdup(session->with);
1728 } else {
1729 to = g_strdup_printf("sip:%s", session->with);
1732 char * base64_msg = purple_base64_encode((guchar*) msg_body, strlen(msg_body));
1733 char * ms_text_format = g_strdup_printf(SIPE_INVITE_TEXT, base64_msg);
1734 g_free(base64_msg);
1736 contact = get_contact(sip);
1737 hdr = g_strdup_printf(
1738 "Contact: %s\r\n%s"
1739 "Content-Type: application/sdp\r\n",
1740 contact, ms_text_format, sip->username, sip->username, to);
1741 g_free(ms_text_format);
1743 body = g_strdup_printf(
1744 "v=0\r\n"
1745 "o=- 0 0 IN IP4 %s\r\n"
1746 "s=session\r\n"
1747 "c=IN IP4 %s\r\n"
1748 "t=0 0\r\n"
1749 "m=message %d sip null\r\n"
1750 "a=accept-types:text/plain text/html image/gif "
1751 "multipart/alternative application/im-iscomposing+xml\r\n",
1752 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip->realport);
1754 session->outgoing_invite = send_sip_request(sip->gc, "INVITE",
1755 to, to, hdr, body, session->dialog, process_invite_response);
1757 g_free(to);
1758 g_free(body);
1759 g_free(hdr);
1760 g_free(contact);
1763 static void
1764 im_session_close (struct sipe_account_data *sip, struct sip_im_session * session)
1766 if (session) {
1767 send_sip_request(sip->gc, "BYE", session->with, session->with, NULL, NULL, session->dialog, NULL);
1768 im_session_destroy(sip, session);
1772 static void
1773 sipe_convo_closed(PurpleConnection * gc, const char *who)
1775 struct sipe_account_data *sip = gc->proto_data;
1777 purple_debug_info("sipe", "conversation with %s closed\n", who);
1778 im_session_close(sip, find_im_session(sip, who));
1781 static void
1782 im_session_close_all (struct sipe_account_data *sip)
1784 GSList *entry = sip->im_sessions;
1785 while (entry) {
1786 im_session_close (sip, entry->data);
1787 entry = sip->im_sessions;
1791 static int sipe_im_send(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags)
1793 struct sipe_account_data *sip = gc->proto_data;
1794 char *to = g_strdup(who);
1795 char *text = purple_unescape_html(what);
1797 struct sip_im_session * session = find_or_create_im_session(sip, who);
1799 // Queue the message
1800 session->outgoing_message_queue = g_slist_append(session->outgoing_message_queue, text);
1802 if (session->dialog && session->dialog->callid) {
1803 sipe_im_process_queue(sip, session);
1804 } else if (!session->outgoing_invite) {
1805 // Need to send the INVITE to get the outgoing dialog setup
1806 sipe_invite(sip, session, text);
1809 g_free(to);
1810 return 1;
1814 /* End IM Session (INVITE and MESSAGE methods) */
1816 static unsigned int
1817 sipe_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
1819 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1821 if (state == PURPLE_NOT_TYPING)
1822 return 0;
1824 struct sip_im_session * session = find_im_session(sip, who);
1826 if (session && session->dialog) {
1827 send_sip_request(gc, "INFO", who, who,
1828 "Content-Type: application/xml\r\n",
1829 SIPE_SEND_TYPING, session->dialog, NULL);
1832 return SIPE_TYPING_SEND_TIMEOUT;
1836 static void sipe_buddy_resub(char *name, struct sipe_buddy *buddy, struct sipe_account_data *sip)
1838 time_t curtime = time(NULL);
1839 if (buddy->resubscribe < curtime) {
1840 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_buddy_resub %s\n", name);
1841 sipe_subscribe(sip, buddy);
1845 static gboolean resend_timeout(struct sipe_account_data *sip)
1847 GSList *tmp = sip->transactions;
1848 time_t currtime = time(NULL);
1849 while (tmp) {
1850 struct transaction *trans = tmp->data;
1851 tmp = tmp->next;
1852 purple_debug_info("sipe", "have open transaction age: %d\n", currtime- trans->time);
1853 if ((currtime - trans->time > 5) && trans->retries >= 1) {
1854 /* TODO 408 */
1855 } else {
1856 if ((currtime - trans->time > 2) && trans->retries == 0) {
1857 trans->retries++;
1858 sendout_sipmsg(sip, trans->msg);
1862 return TRUE;
1865 static gboolean subscribe_timeout(struct sipe_account_data *sip)
1867 GSList *tmp;
1868 time_t curtime = time(NULL);
1869 /* register again if first registration expires */
1870 if (sip->reregister < curtime) {
1871 do_register(sip);
1873 /* check for every subscription if we need to resubscribe */
1874 //Fixxxer we need resub?
1875 g_hash_table_foreach(sip->buddies, (GHFunc)sipe_buddy_resub, (gpointer)sip);
1877 /* remove a timed out suscriber */
1879 tmp = sip->watcher;
1880 while (tmp) {
1881 struct sipe_watcher *watcher = tmp->data;
1882 if (watcher->expire < curtime) {
1883 watcher_remove(sip, watcher->name);
1884 tmp = sip->watcher;
1886 if (tmp) tmp = tmp->next;
1889 return TRUE;
1892 static void process_incoming_message(struct sipe_account_data *sip, struct sipmsg *msg)
1894 gchar *from;
1895 gchar *contenttype;
1896 gboolean found = FALSE;
1898 from = parse_from(sipmsg_find_header(msg, "From"));
1900 if (!from) return;
1902 purple_debug_info("sipe", "got message from %s: %s\n", from, msg->body);
1904 contenttype = sipmsg_find_header(msg, "Content-Type");
1905 if (!contenttype || !strncmp(contenttype, "text/plain", 10) || !strncmp(contenttype, "text/html", 9)) {
1906 serv_got_im(sip->gc, from, msg->body, 0, time(NULL));
1907 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1908 found = TRUE;
1910 if (!strncmp(contenttype, "application/im-iscomposing+xml", 30)) {
1911 xmlnode *isc = xmlnode_from_str(msg->body, msg->bodylen);
1912 xmlnode *state;
1913 gchar *statedata;
1915 if (!isc) {
1916 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1917 return;
1920 state = xmlnode_get_child(isc, "state");
1922 if (!state) {
1923 purple_debug_info("sipe", "process_incoming_message: no state found\n");
1924 xmlnode_free(isc);
1925 return;
1928 statedata = xmlnode_get_data(state);
1929 if (statedata) {
1930 if (strstr(statedata, "active")) serv_got_typing(sip->gc, from, 0, PURPLE_TYPING);
1931 else serv_got_typing_stopped(sip->gc, from);
1933 g_free(statedata);
1935 xmlnode_free(isc);
1936 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1937 found = TRUE;
1939 if (!found) {
1940 purple_debug_info("sipe", "got unknown mime-type");
1941 send_sip_response(sip->gc, msg, 415, "Unsupported media type", NULL);
1943 g_free(from);
1946 static void process_incoming_invite(struct sipe_account_data *sip, struct sipmsg *msg)
1948 // Only accept text invitations
1949 if (msg->body && !strstr(msg->body, "m=message")) {
1950 send_sip_response(sip->gc, msg, 501, "Not implemented", NULL);
1951 return;
1954 gchar * from = sipmsg_find_part_of_header(sipmsg_find_header(msg, "From"), "<", ">", NULL);
1955 struct sip_im_session * session = find_or_create_im_session (sip, from);
1956 if (session) {
1957 if (session->dialog) {
1958 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
1959 } else {
1960 session->dialog = g_new0(struct sip_dialog, 1);
1962 sipe_parse_dialog(msg, session->dialog, FALSE);
1964 session->dialog->callid = sipmsg_find_header(msg, "Call-ID");
1965 session->dialog->ourtag = find_tag(sipmsg_find_header(msg, "To"));
1966 session->dialog->theirtag = find_tag(sipmsg_find_header(msg, "From"));
1967 session->dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, "From"), "epid=", NULL, NULL);
1969 } else {
1970 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
1972 g_free(from);
1974 send_sip_response(sip->gc, msg, 200, "OK", g_strdup_printf(
1975 "v=0\r\n"
1976 "o=- 0 0 IN IP4 %s\r\n"
1977 "s=session\r\n"
1978 "c=IN IP4 %s\r\n"
1979 "t=0 0\r\n"
1980 "m=message %d sip sip:%s\r\n"
1981 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1982 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
1983 sip->realport, sip->username));
1986 static void sipe_connection_cleanup(struct sipe_account_data *);
1987 static void create_connection(struct sipe_account_data *, gchar *, int);
1989 gboolean process_register_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1991 gchar *tmp, krb5_token;
1992 const gchar *expires_header;
1993 int expires;
1995 expires_header = sipmsg_find_header(msg, "Expires");
1996 expires = expires_header != NULL ? strtol(expires_header, NULL, 10) : 0;
1997 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires);
1999 switch (msg->response) {
2000 case 200:
2001 if (expires == 0) {
2002 sip->registerstatus = 0;
2003 } else {
2004 sip->registerstatus = 3;
2005 purple_connection_set_state(sip->gc, PURPLE_CONNECTED);
2007 gchar *gruu = sipmsg_find_part_of_header(sipmsg_find_header(msg, "Contact"), "gruu=\"", "\"", NULL);
2008 if(gruu) {
2009 sip->contact = g_strdup_printf("<%s>", gruu);
2010 } else {
2011 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);
2014 /* get buddies from blist; Has a bug */
2015 subscribe_timeout(sip);
2017 tmp = sipmsg_find_header(msg, "Allow-Events");
2018 if (tmp && strstr(tmp, "vnd-microsoft-provisioning")){
2019 sipe_subscribe_buddylist(sip, msg);
2022 sipe_subscribe_roaming_self(sip, msg);
2023 sipe_subscribe_roaming_provisioning(sip, msg);
2024 sipe_set_status(sip->account, purple_account_get_active_status(sip->account));
2026 // Should we remove the transaction here?
2027 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip->cseq);
2028 transactions_remove(sip, tc);
2030 break;
2031 case 301:
2033 gchar *redirect = parse_from(sipmsg_find_header(msg, "Contact"));
2035 if (redirect && (g_strncasecmp("sip:", redirect, 4) == 0)) {
2036 gchar **parts = g_strsplit(redirect + 4, ";", 0);
2037 gchar **tmp;
2038 gchar *hostname;
2039 int port = 0;
2040 sipe_transport_type transport = SIPE_TRANSPORT_TLS;
2041 int i = 1;
2043 tmp = g_strsplit(parts[0], ":", 0);
2044 hostname = g_strdup(tmp[0]);
2045 if (tmp[1]) port = strtoul(tmp[1], NULL, 10);
2046 g_strfreev(tmp);
2048 while (parts[i]) {
2049 tmp = g_strsplit(parts[i], "=", 0);
2050 if (tmp[1]) {
2051 if (g_strcasecmp("transport", tmp[0]) == 0) {
2052 if (g_strcasecmp("tcp", tmp[1]) == 0) {
2053 transport = SIPE_TRANSPORT_TCP;
2054 } else if (g_strcasecmp("udp", tmp[1]) == 0) {
2055 transport = SIPE_TRANSPORT_UDP;
2059 g_strfreev(tmp);
2060 i++;
2062 g_strfreev(parts);
2064 /* Close old connection */
2065 sipe_connection_cleanup(sip);
2067 /* Create new connection */
2068 sip->transport = transport;
2069 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
2070 hostname, port, TRANSPORT_DESCRIPTOR);
2071 create_connection(sip, hostname, port);
2074 break;
2075 case 401:
2076 if (sip->registerstatus != 2) {
2077 purple_debug_info("sipe", "REGISTER retries %d\n", sip->registrar.retries);
2078 if (sip->registrar.retries > 3) {
2079 sip->gc->wants_to_die = TRUE;
2080 purple_connection_error(sip->gc, _("Wrong Password"));
2081 return TRUE;
2083 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
2084 tmp = sipmsg_find_auth_header(msg, "NTLM");
2085 } else {
2086 tmp = sipmsg_find_auth_header(msg, "Kerberos");
2088 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_register_response - Auth header: %s\r\n", tmp);
2089 fill_auth(sip, tmp, &sip->registrar);
2090 sip->registerstatus = 2;
2091 if (sip->account->disconnecting) {
2092 do_register_exp(sip, 0);
2093 } else {
2094 do_register(sip);
2097 break;
2098 case 403:
2100 const gchar *warning = sipmsg_find_header(msg, "Warning");
2101 if (warning != NULL) {
2102 /* Example header:
2103 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
2105 gchar **tmp = g_strsplit(warning, "\"", 0);
2106 warning = g_strdup_printf("You have been rejected by the server: %s", tmp[1] ? tmp[1] : "no reason given");
2107 g_strfreev(tmp);
2108 } else {
2109 warning = _("You have been rejected by the server");
2112 sip->gc->wants_to_die = TRUE;
2113 purple_connection_error(sip->gc, warning);
2114 return TRUE;
2116 break;
2118 return TRUE;
2121 static void process_incoming_notify(struct sipe_account_data *sip, struct sipmsg *msg)
2123 gchar *from;
2124 gchar *fromhdr;
2125 gchar *tmp2;
2126 gchar *activity = g_strdup("available");
2127 xmlnode *pidf;
2128 xmlnode *basicstatus = NULL, *tuple, *status;
2129 gboolean isonline = FALSE;
2131 fromhdr = sipmsg_find_header(msg, "From");
2132 from = parse_from(fromhdr);
2134 if (!from) {
2135 return;
2138 pidf = xmlnode_from_str(msg->body, msg->bodylen);
2139 if (!pidf) {
2140 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
2141 return;
2144 if ((tuple = xmlnode_get_child(pidf, "tuple"))) {
2145 if ((status = xmlnode_get_child(tuple, "status"))) {
2146 basicstatus = xmlnode_get_child(status, "basic");
2150 if (!basicstatus) {
2151 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
2152 xmlnode_free(pidf);
2153 return;
2156 tmp2 = xmlnode_get_data(basicstatus);
2157 if (!tmp2) {
2158 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
2159 xmlnode_free(pidf);
2160 return;
2163 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", tmp2);
2164 if (strstr(tmp2, "open")) {
2165 isonline = TRUE;
2168 xmlnode *display_name_node = xmlnode_get_child(pidf, "display-name");
2169 if (display_name_node) {
2170 PurpleBuddy * buddy = purple_find_buddy (sip->account, from);
2171 char * display_name = xmlnode_get_data(display_name_node);
2172 if (buddy && display_name) {
2173 purple_blist_server_alias_buddy (buddy, g_strdup(display_name));
2177 if ((tuple = xmlnode_get_child(pidf, "tuple"))) {
2178 if ((status = xmlnode_get_child(tuple, "status"))) {
2179 if (basicstatus = xmlnode_get_child(status, "activities")) {
2180 if (basicstatus = xmlnode_get_child(basicstatus, "activity")) {
2181 activity = xmlnode_get_data(basicstatus);
2186 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity);
2188 if (isonline) {
2189 gchar * status_id = NULL;
2190 if (activity) {
2191 if (strstr(activity, "busy")) {
2192 status_id = "busy";
2193 } else if (strstr(activity, "away")) {
2194 status_id = "away";
2198 if (!status_id) {
2199 status_id = "available";
2202 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id);
2203 purple_prpl_got_user_status(sip->account, from, status_id, NULL);
2204 } else {
2205 purple_prpl_got_user_status(sip->account, from, "offline", NULL);
2208 xmlnode_free(pidf);
2209 g_free(from);
2210 g_free(tmp2);
2211 g_free(activity);
2213 send_sip_response(sip->gc, msg, 200, "OK", NULL);
2216 static void process_incoming_benotify(struct sipe_account_data *sip, struct sipmsg *msg)
2218 xmlnode *xml = xmlnode_from_str(msg->body, msg->bodylen);
2219 if (!xml) return;
2221 char * delta_num = xmlnode_get_attrib(xml, "deltaNum");
2222 if (delta_num) {
2223 sip->delta_num = (int)g_ascii_strtod(delta_num, NULL);
2226 xmlnode_free(xml);
2230 static gchar* gen_xpidf(struct sipe_account_data *sip)
2232 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
2233 "<presence>\r\n"
2234 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
2235 "<display name=\"sip:%s\"/>\r\n"
2236 "<atom id=\"1234\">\r\n"
2237 "<address uri=\"sip:%s\">\r\n"
2238 "<status status=\"%s\"/>\r\n"
2239 "</address>\r\n"
2240 "</atom>\r\n"
2241 "</presence>\r\n",
2242 sip->username,
2243 sip->username,
2244 sip->username,
2245 sip->status);
2246 return doc;
2251 static gchar* gen_pidf(struct sipe_account_data *sip)
2253 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
2254 "<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"
2255 "<tuple id=\"0\">\r\n"
2256 "<status>\r\n"
2257 "<basic>open</basic>\r\n"
2258 "<ep:activities>\r\n"
2259 " <ep:activity>%s</ep:activity>\r\n"
2260 "</ep:activities>"
2261 "</status>\r\n"
2262 "</tuple>\r\n"
2263 "<ci:display-name>%s</ci:display-name>\r\n"
2264 "</presence>",
2265 sip->username,
2266 sip->status,
2267 sip->username);
2268 return doc;
2271 static void send_notify(struct sipe_account_data *sip, struct sipe_watcher *watcher)
2273 gchar *doc = watcher->needsxpidf ? gen_xpidf(sip) : gen_pidf(sip);
2274 gchar *hdr = watcher->needsxpidf ? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
2275 send_sip_request(sip->gc, "NOTIFY", watcher->name, watcher->name, hdr, doc, &watcher->dialog, NULL);
2276 g_free(doc);
2279 static gboolean process_service_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
2282 if (msg->response != 200 && msg->response != 408) {
2283 /* never send again */
2284 sip->republish = -1;
2286 return TRUE;
2289 static void send_clear_notes(struct sipe_account_data *sip)
2293 static gboolean
2294 process_send_presence_info_v0_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
2296 if (msg->response == 488) {
2297 sip->presence_method_version = 1;
2298 send_presence_info(sip);
2300 return TRUE;
2303 static void send_presence_info_v0(struct sipe_account_data *sip, char * note)
2305 int code;
2306 if (!strcmp(sip->status, "away")) {
2307 code = 100;
2308 } else if (!strcmp(sip->status, "busy")) {
2309 code = 600;
2310 } else {
2311 // Available
2312 code = 400;
2315 gchar *name = g_strdup_printf("sip: sip:%s", sip->username);
2316 gchar * body = g_strdup_printf(SIPE_SOAP_SET_PRESENCE, name, 200, code, note);
2317 send_soap_request_with_cb(sip, body, process_send_presence_info_v0_response, NULL);
2318 g_free(name);
2319 g_free(body);
2322 static gboolean
2323 process_clear_presence_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
2325 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
2326 if (msg->response == 200) {
2327 sip->status_version = 0;
2328 send_presence_info(sip);
2330 return TRUE;
2333 static gboolean
2334 process_send_presence_info_v1_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
2336 if (msg->response == 409) {
2337 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
2338 // TODO need to parse the version #'s?
2339 gchar *uri = g_strdup_printf("sip:%s", sip->username);
2340 gchar *doc = g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE, uri);
2342 gchar *tmp = get_contact(sip);
2343 gchar *hdr = g_strdup_printf("Contact: %s\r\n"
2344 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
2346 send_sip_request(sip->gc, "SERVICE", uri, uri, hdr, doc, NULL, process_clear_presence_response);
2348 g_free(tmp);
2349 g_free(hdr);
2350 g_free(uri);
2351 g_free(doc);
2353 return TRUE;
2356 static void send_presence_info_v1(struct sipe_account_data *sip, char * note)
2358 int code;
2359 if (!strcmp(sip->status, "away")) {
2360 code = 12000;
2361 } else if (!strcmp(sip->status, "busy")) {
2362 code = 6000;
2363 } else {
2364 // Available
2365 code = 3000;
2368 gchar *uri = g_strdup_printf("sip:%s", sip->username);
2369 gchar *doc = g_strdup_printf(SIPE_SEND_PRESENCE, uri,
2370 sip->status_version, code,
2371 sip->status_version, code,
2372 sip->status_version, note ? note : "",
2373 sip->status_version, note ? note : "",
2374 sip->status_version, note ? note : ""
2376 sip->status_version++;
2378 gchar *tmp = get_contact(sip);
2379 gchar *hdr = g_strdup_printf("Contact: %s\r\n"
2380 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
2382 send_sip_request(sip->gc, "SERVICE", uri, uri, hdr, doc, NULL, process_send_presence_info_v1_response);
2384 g_free(tmp);
2385 g_free(hdr);
2386 g_free(uri);
2387 g_free(doc);
2390 static void send_presence_info(struct sipe_account_data *sip)
2392 PurpleStatus * status = purple_account_get_active_status(sip->account);
2393 if (!status) return;
2395 char *note = purple_status_get_attr_string(status, "message");
2397 purple_debug_info("sipe", "sending presence info, version = %d\n", sip->presence_method_version);
2398 if (sip->presence_method_version != 1) {
2399 send_presence_info_v0(sip, note);
2400 } else {
2401 send_presence_info_v1(sip, note);
2405 static void send_service(struct sipe_account_data *sip)
2407 //gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->sipdomain);
2408 gchar *uri = g_strdup_printf("sip:%s", sip->username);
2409 //gchar *doc = gen_pidf(sip);
2411 gchar *doc = gen_pidf(sip);
2412 gchar *hdr = g_strdup("Event: presence\r\nContent-Type: application/pidf+xml\r\n");
2414 //gchar *hdr = g_strdup("Content-Type: application/SOAP+xml\r\n");
2415 gchar *tmp = get_contact(sip);
2416 hdr = g_strdup_printf("Contact: %s\r\n%s; +sip.instance=\"<urn:uuid:%s>\"", tmp, hdr,generateUUIDfromEPID(get_epid()));
2417 g_free(tmp);
2418 send_sip_request(sip->gc, "SERVICE", uri, uri,
2419 hdr,
2420 doc, NULL, process_service_response);
2421 sip->republish = time(NULL) + 500;
2422 g_free(hdr);
2423 g_free(uri);
2424 g_free(doc);
2427 static void process_incoming_subscribe(struct sipe_account_data *sip, struct sipmsg *msg)
2429 const char *from_hdr = sipmsg_find_header(msg, "From");
2430 gchar *from = parse_from(from_hdr);
2431 gchar *theirtag = find_tag(from_hdr);
2432 gchar *ourtag = find_tag(sipmsg_find_header(msg, "To"));
2433 gboolean tagadded = FALSE;
2434 gchar *callid = sipmsg_find_header(msg, "Call-ID");
2435 gchar *expire = sipmsg_find_header(msg, "Expire");
2436 gchar *tmp;
2437 struct sipe_watcher *watcher = watcher_find(sip, from);
2438 if (!ourtag) {
2439 tagadded = TRUE;
2440 ourtag = gentag();
2442 if (!watcher) { /* new subscription */
2443 gchar *acceptheader = sipmsg_find_header(msg, "Accept");
2444 gboolean needsxpidf = FALSE;
2445 if (!purple_privacy_check(sip->account, from)) {
2446 send_sip_response(sip->gc, msg, 202, "Ok", NULL);
2447 goto privend;
2449 if (acceptheader) {
2450 gchar *tmp = acceptheader;
2451 gboolean foundpidf = FALSE;
2452 gboolean foundxpidf = FALSE;
2453 while (tmp && tmp < acceptheader + strlen(acceptheader)) {
2454 gchar *tmp2 = strchr(tmp, ',');
2455 if (tmp2) *tmp2 = '\0';
2456 if (!strcmp("application/pidf+xml", tmp))
2457 foundpidf = TRUE;
2458 if (!strcmp("application/xpidf+xml", tmp))
2459 foundxpidf = TRUE;
2460 if (tmp2) {
2461 *tmp2 = ',';
2462 tmp = tmp2;
2463 while (*tmp == ' ') tmp++;
2464 } else
2465 tmp = 0;
2467 if (!foundpidf && foundxpidf) needsxpidf = TRUE;
2468 g_free(acceptheader);
2470 watcher = watcher_create(sip, from, callid, ourtag, theirtag, needsxpidf);
2472 if (tagadded) {
2473 gchar *to = g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg, "To"), ourtag);
2474 sipmsg_remove_header(msg, "To");
2475 sipmsg_add_header(msg, "To", to);
2476 g_free(to);
2478 if (expire)
2479 watcher->expire = time(NULL) + strtol(expire, NULL, 10);
2480 else
2481 watcher->expire = time(NULL) + 600;
2482 //Fixxxer
2483 sipmsg_remove_header(msg, "Contact");
2484 tmp = get_contact(sip);
2485 sipmsg_add_header(msg, "Contact", tmp);
2486 g_free(tmp);
2487 purple_debug_info("sipe", "got subscribe: name %s ourtag %s theirtag %s callid %s\n", watcher->name, watcher->dialog.ourtag, watcher->dialog.theirtag, watcher->dialog.callid);
2488 send_sip_response(sip->gc, msg, 200, "Ok", NULL);
2489 send_notify(sip, watcher);
2490 privend:
2491 g_free(from);
2492 g_free(theirtag);
2493 g_free(ourtag);
2494 g_free(callid);
2495 g_free(expire);
2498 static void process_input_message(struct sipe_account_data *sip,struct sipmsg *msg)
2500 gboolean found = FALSE;
2501 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg->response,msg->method);
2502 if (msg->response == 0) { /* request */
2503 if (!strcmp(msg->method, "MESSAGE")) {
2504 process_incoming_message(sip, msg);
2505 found = TRUE;
2506 } else if (!strcmp(msg->method, "NOTIFY")) {
2507 purple_debug_info("sipe","send->process_incoming_notify\n");
2508 process_incoming_notify(sip, msg);
2509 found = TRUE;
2510 } else if (!strcmp(msg->method, "BENOTIFY")) {
2511 purple_debug_info("sipe","send->process_incoming_benotify\n");
2512 process_incoming_benotify(sip, msg);
2513 found = TRUE;
2514 } else if (!strcmp(msg->method, "SUBSCRIBE")) {
2515 purple_debug_info("sipe","send->process_incoming_subscribe\n");
2516 process_incoming_subscribe(sip, msg);
2517 found = TRUE;
2518 } else if (!strcmp(msg->method, "INVITE")) {
2519 process_incoming_invite(sip, msg);
2520 found = TRUE;
2521 } else if (!strcmp(msg->method, "INFO")) {
2522 // TODO needs work
2523 gchar * from = parse_from(sipmsg_find_header(msg, "From"));
2524 if (from) {
2525 serv_got_typing(sip->gc, from, SIPE_TYPING_RECV_TIMEOUT, PURPLE_TYPING);
2527 send_sip_response(sip->gc, msg, 200, "OK", NULL);
2528 found = TRUE;
2529 } else if (!strcmp(msg->method, "ACK")) {
2530 // ACK's don't need any response
2531 found = TRUE;
2532 } else if (!strcmp(msg->method, "BYE")) {
2533 send_sip_response(sip->gc, msg, 200, "OK", NULL);
2535 gchar * from = parse_from(sipmsg_find_header(msg, "From"));
2536 struct sip_im_session * session = find_im_session (sip, from);
2537 g_free(from);
2539 if (session) {
2540 // TODO Let the user know the other user left the conversation?
2541 im_session_destroy(sip, session);
2544 found = TRUE;
2545 } else {
2546 send_sip_response(sip->gc, msg, 501, "Not implemented", NULL);
2548 } else { /* response */
2549 struct transaction *trans = transactions_find(sip, msg);
2550 if (trans) {
2551 if (msg->response == 407) {
2552 gchar *resend, *auth, *ptmp;
2554 if (sip->proxy.retries > 30) return;
2555 sip->proxy.retries++;
2556 /* do proxy authentication */
2558 ptmp = sipmsg_find_header(msg, "Proxy-Authenticate");
2560 fill_auth(sip, ptmp, &sip->proxy);
2561 auth = auth_header(sip, &sip->proxy, trans->msg, TRUE);
2562 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
2563 sipmsg_add_header_pos(trans->msg, "Proxy-Authorization", auth, 5);
2564 g_free(auth);
2565 resend = sipmsg_to_string(trans->msg);
2566 /* resend request */
2567 sendout_pkt(sip->gc, resend);
2568 g_free(resend);
2569 } else {
2570 if (msg->response == 100 || msg->response == 180) {
2571 /* ignore provisional response */
2572 purple_debug_info("sipe", "got trying (%d) response\n", msg->response);
2573 } else {
2574 sip->proxy.retries = 0;
2575 if (!strcmp(trans->msg->method, "REGISTER")) {
2576 if (msg->response == 401) sip->registrar.retries++;
2577 else sip->registrar.retries = 0;
2578 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip->cseq);
2579 } else {
2580 if (msg->response == 401) {
2581 gchar *resend, *auth, *ptmp;
2583 if (sip->registrar.retries > 4) return;
2584 sip->registrar.retries++;
2586 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
2587 ptmp = sipmsg_find_auth_header(msg, "NTLM");
2588 } else {
2589 ptmp = sipmsg_find_auth_header(msg, "Kerberos");
2592 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - Auth header: %s\r\n", ptmp);
2594 fill_auth(sip, ptmp, &sip->registrar);
2595 auth = auth_header(sip, &sip->registrar, trans->msg, TRUE);
2596 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
2597 sipmsg_add_header(trans->msg, "Proxy-Authorization", auth);
2599 //sipmsg_remove_header(trans->msg, "Authorization");
2600 //sipmsg_add_header(trans->msg, "Authorization", auth);
2601 g_free(auth);
2602 resend = sipmsg_to_string(trans->msg);
2603 /* resend request */
2604 sendout_pkt(sip->gc, resend);
2605 g_free(resend);
2609 if (trans->callback) {
2610 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - we have a transaction callback\r\n");
2611 /* call the callback to process response*/
2612 (trans->callback)(sip, msg, trans);
2614 /* Not sure if this is needed or what needs to be done
2615 but transactions seem to be removed prematurely so
2616 this only removes them if the response is 200 OK */
2617 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - removing CSeq %d\r\n", sip->cseq);
2618 /*Has a bug and it's unneccesary*/
2619 /*transactions_remove(sip, trans);*/
2623 found = TRUE;
2624 } else {
2625 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received response to unknown transaction");
2628 if (!found) {
2629 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received a unknown sip message with method %s and response %d\n", msg->method, msg->response);
2633 static void process_input(struct sipe_account_data *sip, struct sip_connection *conn)
2635 char *cur;
2636 char *dummy;
2637 struct sipmsg *msg;
2638 int restlen;
2639 cur = conn->inbuf;
2641 /* according to the RFC remove CRLF at the beginning */
2642 while (*cur == '\r' || *cur == '\n') {
2643 cur++;
2645 if (cur != conn->inbuf) {
2646 memmove(conn->inbuf, cur, conn->inbufused - (cur - conn->inbuf));
2647 conn->inbufused = strlen(conn->inbuf);
2650 /* Received a full Header? */
2651 while ((cur = strstr(conn->inbuf, "\r\n\r\n")) != NULL) {
2652 time_t currtime = time(NULL);
2653 cur += 2;
2654 cur[0] = '\0';
2655 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), conn->inbuf);
2656 msg = sipmsg_parse_header(conn->inbuf);
2657 cur[0] = '\r';
2658 cur += 2;
2659 restlen = conn->inbufused - (cur - conn->inbuf);
2660 if (restlen >= msg->bodylen) {
2661 dummy = g_malloc(msg->bodylen + 1);
2662 memcpy(dummy, cur, msg->bodylen);
2663 dummy[msg->bodylen] = '\0';
2664 msg->body = dummy;
2665 cur += msg->bodylen;
2666 memmove(conn->inbuf, cur, conn->inbuflen - (cur - conn->inbuf));
2667 conn->inbufused = strlen(conn->inbuf);
2668 } else {
2669 sipmsg_free(msg);
2670 return;
2673 if (msg->body) {
2674 purple_debug_info("sipe", "body:\n%s", msg->body);
2677 // Verify the signature before processing it
2678 if (sip->registrar.ntlm_key) {
2679 struct sipmsg_breakdown msgbd;
2680 msgbd.msg = msg;
2681 sipmsg_breakdown_parse(&msgbd, sip->registrar.realm, sip->registrar.target);
2682 gchar * signature_input_str = sipmsg_breakdown_get_string(&msgbd);
2683 gchar * signature;
2684 if (signature_input_str != NULL) {
2685 signature = purple_ntlm_sipe_signature_make (signature_input_str, sip->registrar.ntlm_key);
2688 gchar * rspauth = sipmsg_find_part_of_header(sipmsg_find_header(msg, "Authentication-Info"), "rspauth=\"", "\"", NULL);
2689 if (signature != NULL && rspauth != NULL) {
2690 if (purple_ntlm_verify_signature (signature, rspauth)) {
2691 purple_debug(PURPLE_DEBUG_MISC, "sipe", "incoming message's signature validated\n");
2692 process_input_message(sip, msg);
2693 } else {
2694 purple_debug(PURPLE_DEBUG_MISC, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth, signature);
2695 purple_connection_error(sip->gc, _("Invalid message signature received"));
2696 sip->gc->wants_to_die = TRUE;
2700 sipmsg_breakdown_free(&msgbd);
2701 } else {
2702 process_input_message(sip, msg);
2707 static void sipe_udp_process(gpointer data, gint source, PurpleInputCondition con)
2709 PurpleConnection *gc = data;
2710 struct sipe_account_data *sip = gc->proto_data;
2711 struct sipmsg *msg;
2712 int len;
2713 time_t currtime;
2715 static char buffer[65536];
2716 if ((len = recv(source, buffer, sizeof(buffer) - 1, 0)) > 0) {
2717 buffer[len] = '\0';
2718 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), buffer);
2719 msg = sipmsg_parse_msg(buffer);
2720 if (msg) process_input_message(sip, msg);
2724 static void sipe_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
2726 PurpleConnection *gc = data;
2727 struct sipe_account_data *sip = gc->proto_data;
2728 struct sip_connection *conn = NULL;
2729 int len;
2730 static char buf[4096];
2732 /* TODO: It should be possible to make this check unnecessary */
2733 if (!PURPLE_CONNECTION_IS_VALID(gc)) {
2734 purple_ssl_close(gsc);
2735 return;
2738 if (sip->fd != -1)
2739 conn = connection_find(sip, gsc->fd);
2740 if (!conn) {
2741 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
2742 if (sip->fd != -1) purple_ssl_close(gsc);
2743 purple_connection_error(sip->gc, _("Connection not found; Please try to connect again.\n"));
2744 sip->gc->wants_to_die = TRUE;
2745 return;
2748 if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
2749 conn->inbuflen += SIMPLE_BUF_INC;
2750 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
2753 len = purple_ssl_read(sip->gsc, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1);
2755 if (len < 0 && errno == EAGAIN) {
2756 /* Try again later */
2757 return;
2758 } else if (len < 0) {
2759 purple_debug_info("sipe", "sipe_input_cb_ssl: read error\n");
2760 if (sip->gsc){
2761 connection_remove(sip, sip->gsc->fd);
2762 if (sip->fd == gsc->fd) sip->fd = -1;
2763 purple_debug_info("sipe", "sipe_input_cb_ssl: connection_remove\n");
2765 return;
2766 } else if (len == 0) {
2767 purple_connection_error(gc, _("Server has disconnected"));
2768 if (sip->gsc){
2769 connection_remove(sip, sip->gsc->fd);
2770 if (sip->fd == gsc->fd) sip->fd = -1;
2772 return;
2775 conn->inbufused += len;
2776 conn->inbuf[conn->inbufused] = '\0';
2778 process_input(sip, conn);
2782 static void sipe_input_cb(gpointer data, gint source, PurpleInputCondition cond)
2784 PurpleConnection *gc = data;
2785 struct sipe_account_data *sip = gc->proto_data;
2786 int len;
2787 struct sip_connection *conn = connection_find(sip, source);
2788 if (!conn) {
2789 purple_debug_error("sipe", "Connection not found!\n");
2790 return;
2793 if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
2794 conn->inbuflen += SIMPLE_BUF_INC;
2795 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
2798 len = read(source, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1);
2800 if (len < 0 && errno == EAGAIN)
2801 return;
2802 else if (len <= 0) {
2803 purple_debug_info("sipe", "sipe_input_cb: read error\n");
2804 connection_remove(sip, source);
2805 if (sip->fd == source) sip->fd = -1;
2806 return;
2809 conn->inbufused += len;
2810 conn->inbuf[conn->inbufused] = '\0';
2812 process_input(sip, conn);
2815 /* Callback for new connections on incoming TCP port */
2816 static void sipe_newconn_cb(gpointer data, gint source, PurpleInputCondition cond)
2818 PurpleConnection *gc = data;
2819 struct sipe_account_data *sip = gc->proto_data;
2820 struct sip_connection *conn;
2822 int newfd = accept(source, NULL, NULL);
2824 conn = connection_create(sip, newfd);
2826 conn->inputhandler = purple_input_add(newfd, PURPLE_INPUT_READ, sipe_input_cb, gc);
2829 static void login_cb(gpointer data, gint source, const gchar *error_message)
2831 PurpleConnection *gc = data;
2832 struct sipe_account_data *sip;
2833 struct sip_connection *conn;
2835 if (!PURPLE_CONNECTION_IS_VALID(gc))
2837 if (source >= 0)
2838 close(source);
2839 return;
2842 if (source < 0) {
2843 purple_connection_error(gc, _("Could not connect"));
2844 return;
2847 sip = gc->proto_data;
2848 sip->fd = source;
2850 conn = connection_create(sip, source);
2852 sip->registertimeout = purple_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
2854 do_register(sip);
2856 conn->inputhandler = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_input_cb, gc);
2859 static void login_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
2861 PurpleConnection *gc = data;
2862 struct sipe_account_data *sip;
2863 struct sip_connection *conn;
2865 if (!PURPLE_CONNECTION_IS_VALID(gc))
2867 if (gsc) purple_ssl_close(gsc);
2868 return;
2871 sip = gc->proto_data;
2872 sip->fd = gsc->fd;
2873 sip->gsc = gsc;
2874 conn = connection_create(sip, sip->fd);
2875 sip->listenport = purple_network_get_port_from_fd(sip->fd);
2876 sip->listenfd = sip->fd;
2877 sip->registertimeout = purple_timeout_add((rand()%100) + 1000, (GSourceFunc)subscribe_timeout, sip);
2879 do_register(sip);
2880 if(sip)
2881 purple_ssl_input_add(sip->gsc, sipe_input_cb_ssl, gc);
2884 static guint sipe_ht_hash_nick(const char *nick)
2886 char *lc = g_utf8_strdown(nick, -1);
2887 guint bucket = g_str_hash(lc);
2888 g_free(lc);
2890 return bucket;
2893 static gboolean sipe_ht_equals_nick(const char *nick1, const char *nick2)
2895 return (purple_utf8_strcasecmp(nick1, nick2) == 0);
2898 static void sipe_udp_host_resolved_listen_cb(int listenfd, gpointer data)
2900 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2902 sip->listen_data = NULL;
2904 if (listenfd == -1) {
2905 purple_connection_error(sip->gc, _("Could not create listen socket"));
2906 return;
2909 sip->fd = listenfd;
2911 sip->listenport = purple_network_get_port_from_fd(sip->fd);
2912 sip->listenfd = sip->fd;
2914 sip->listenpa = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_udp_process, sip->gc);
2916 sip->resendtimeout = purple_timeout_add(2500, (GSourceFunc) resend_timeout, sip);
2917 sip->registertimeout = purple_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
2918 do_register(sip);
2921 static void sipe_udp_host_resolved(GSList *hosts, gpointer data, const char *error_message)
2923 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2924 int addr_size;
2926 sip->query_data = NULL;
2928 if (!hosts || !hosts->data) {
2929 purple_connection_error(sip->gc, _("Couldn't resolve host"));
2930 return;
2933 addr_size = GPOINTER_TO_INT(hosts->data);
2934 hosts = g_slist_remove(hosts, hosts->data);
2935 memcpy(&(sip->serveraddr), hosts->data, addr_size);
2936 g_free(hosts->data);
2937 hosts = g_slist_remove(hosts, hosts->data);
2938 while (hosts) {
2939 hosts = g_slist_remove(hosts, hosts->data);
2940 g_free(hosts->data);
2941 hosts = g_slist_remove(hosts, hosts->data);
2944 /* create socket for incoming connections */
2945 sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_DGRAM,
2946 sipe_udp_host_resolved_listen_cb, sip);
2947 if (sip->listen_data == NULL) {
2948 purple_connection_error(sip->gc, _("Could not create listen socket"));
2949 return;
2953 static void sipe_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
2954 gpointer data)
2956 PurpleConnection *gc = data;
2957 struct sipe_account_data *sip;
2959 /* If the connection is already disconnected, we don't need to do anything else */
2960 if (!PURPLE_CONNECTION_IS_VALID(gc))
2961 return;
2963 sip = gc->proto_data;
2964 sip->gsc = NULL;
2966 switch(error) {
2967 case PURPLE_SSL_CONNECT_FAILED:
2968 purple_connection_error(gc, _("Connection Failed"));
2969 break;
2970 case PURPLE_SSL_HANDSHAKE_FAILED:
2971 purple_connection_error(gc, _("SSL Handshake Failed"));
2972 break;
2976 static void
2977 sipe_tcp_connect_listen_cb(int listenfd, gpointer data)
2979 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2980 PurpleProxyConnectData *connect_data;
2982 sip->listen_data = NULL;
2984 sip->listenfd = listenfd;
2985 if (sip->listenfd == -1) {
2986 purple_connection_error(sip->gc, _("Could not create listen socket"));
2987 return;
2990 purple_debug_info("sipe", "listenfd: %d\n", sip->listenfd);
2991 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2992 sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2993 sip->listenpa = purple_input_add(sip->listenfd, PURPLE_INPUT_READ,
2994 sipe_newconn_cb, sip->gc);
2995 purple_debug_info("sipe", "connecting to %s port %d\n",
2996 sip->realhostname, sip->realport);
2997 /* open tcp connection to the server */
2998 connect_data = purple_proxy_connect(sip->gc, sip->account, sip->realhostname,
2999 sip->realport, login_cb, sip->gc);
3001 if (connect_data == NULL) {
3002 purple_connection_error(sip->gc, _("Couldn't create socket"));
3007 static void create_connection(struct sipe_account_data *sip, gchar *hostname, int port)
3009 PurpleAccount *account = sip->account;
3010 PurpleConnection *gc = sip->gc;
3012 if (purple_account_get_bool(account, "useport", FALSE)) {
3013 purple_debug(PURPLE_DEBUG_MISC, "sipe", "create_connection - using specified SIP port\n");
3014 port = purple_account_get_int(account, "port", 0);
3015 } else {
3016 port = port ? port : (sip->transport == SIPE_TRANSPORT_TLS) ? 5061 : 5060;
3019 sip->realhostname = hostname;
3020 sip->realport = port;
3022 purple_debug(PURPLE_DEBUG_MISC, "sipe", "create_connection - hostname: %s port: %d\n",
3023 hostname, port);
3025 /* TODO: is there a good default grow size? */
3026 if (sip->transport != SIPE_TRANSPORT_UDP)
3027 sip->txbuf = purple_circ_buffer_new(0);
3029 if (sip->transport == SIPE_TRANSPORT_TLS) {
3030 /* SSL case */
3031 if (!purple_ssl_is_supported()) {
3032 gc->wants_to_die = TRUE;
3033 purple_connection_error(gc, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
3034 return;
3037 purple_debug_info("sipe", "using SSL\n");
3038 sip->gsc = purple_ssl_connect(account, hostname, port,
3039 login_cb_ssl, sipe_ssl_connect_failure, gc);
3040 if (sip->gsc == NULL) {
3041 purple_connection_error(gc, _("Could not create SSL context"));
3042 return;
3044 } else if (sip->transport == SIPE_TRANSPORT_UDP) {
3045 /* UDP case */
3046 purple_debug_info("sipe", "using UDP\n");
3048 sip->query_data = purple_dnsquery_a(hostname, port, sipe_udp_host_resolved, sip);
3049 if (sip->query_data == NULL) {
3050 purple_connection_error(gc, _("Could not resolve hostname"));
3052 } else {
3053 /* TCP case */
3054 purple_debug_info("sipe", "using TCP\n");
3055 /* create socket for incoming connections */
3056 sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_STREAM,
3057 sipe_tcp_connect_listen_cb, sip);
3058 if (sip->listen_data == NULL) {
3059 purple_connection_error(gc, _("Could not create listen socket"));
3060 return;
3065 /* Service list for autodection */
3066 static const struct sipe_service_data service_autodetect[] = {
3067 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS }, /* for internal TLS connections */
3068 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP }, /* for internal TCP connections */
3069 { "sip", "tls", SIPE_TRANSPORT_TLS }, /* for external TLS connections */
3070 { "sip", "tcp", SIPE_TRANSPORT_TCP }, /*.for external TCP connections */
3071 { NULL, NULL, 0 }
3074 /* Service list for SSL/TLS */
3075 static const struct sipe_service_data service_tls[] = {
3076 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS }, /* for internal TLS connections */
3077 { "sip", "tls", SIPE_TRANSPORT_TLS }, /* for external TLS connections */
3078 { NULL, NULL, 0 }
3081 /* Service list for TCP */
3082 static const struct sipe_service_data service_tcp[] = {
3083 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP }, /* for internal TCP connections */
3084 { "sip", "tcp", SIPE_TRANSPORT_TCP }, /*.for external TCP connections */
3085 { NULL, NULL, 0 }
3088 /* Service list for UDP */
3089 static const struct sipe_service_data service_udp[] = {
3090 { "sip", "udp", SIPE_TRANSPORT_UDP },
3091 { NULL, NULL, 0 }
3094 static void srvresolved(PurpleSrvResponse *, int, gpointer);
3095 static void resolve_next_service(struct sipe_account_data *sip,
3096 const struct sipe_service_data *start)
3098 if (start) {
3099 sip->service_data = start;
3100 } else {
3101 sip->service_data++;
3102 if (sip->service_data->service == NULL) {
3103 /* Try connecting to the SIP hostname directly */
3104 purple_debug(PURPLE_DEBUG_MISC, "sipe", "no SRV records found; using SIP domain as fallback\n");
3105 // If SSL is supported, default to using it; OCS servers aren't configured
3106 // by default to accept TCP
3107 sip->transport = purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS : SIPE_TRANSPORT_TCP;
3108 gchar * hostname = g_strdup(sip->sipdomain);
3109 create_connection(sip, hostname, 0);
3110 return;
3114 /* Try to resolve next service */
3115 sip->srv_query_data = purple_srv_resolve(sip->service_data->service,
3116 sip->service_data->transport,
3117 sip->sipdomain,
3118 srvresolved, sip);
3121 static void srvresolved(PurpleSrvResponse *resp, int results, gpointer data)
3123 struct sipe_account_data *sip = data;
3125 sip->srv_query_data = NULL;
3127 /* find the host to connect to */
3128 if (results) {
3129 gchar *hostname = g_strdup(resp->hostname);
3130 int port = resp->port;
3131 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
3132 hostname, port);
3133 g_free(resp);
3135 sip->transport = sip->service_data->type;
3137 create_connection(sip, hostname, port);
3138 } else {
3139 resolve_next_service(sip, NULL);
3143 static void sipe_login(PurpleAccount *account)
3145 PurpleConnection *gc;
3146 struct sipe_account_data *sip;
3147 gchar **userserver;
3148 const char *transport;
3150 const char *username = purple_account_get_username(account);
3151 gc = purple_account_get_connection(account);
3153 if (strpbrk(username, " \t\v\r\n") != NULL) {
3154 gc->wants_to_die = TRUE;
3155 purple_connection_error(gc, _("SIP Exchange usernames may not contain whitespaces"));
3156 return;
3159 gc->proto_data = sip = g_new0(struct sipe_account_data, 1);
3160 sip->gc = gc;
3161 sip->account = account;
3162 sip->registerexpire = 900;
3164 userserver = g_strsplit(username, "@", 2);
3165 purple_connection_set_display_name(gc, userserver[0]);
3166 sip->username = g_strdup(g_strjoin("@", userserver[0], userserver[1], NULL));
3167 sip->sipdomain = g_strdup(userserver[1]);
3168 sip->password = g_strdup(purple_connection_get_password(gc));
3169 g_strfreev(userserver);
3171 sip->buddies = g_hash_table_new((GHashFunc)sipe_ht_hash_nick, (GEqualFunc)sipe_ht_equals_nick);
3173 purple_connection_update_progress(gc, _("Connecting"), 1, 2);
3175 /* TODO: Set the status correctly. */
3176 sip->status = g_strdup("available");
3178 transport = purple_account_get_string(account, "transport", "auto");
3180 if (purple_account_get_bool(account, "useproxy", FALSE)) {
3181 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_login - using specified SIP proxy\n");
3182 sip->transport = (strcmp(transport, "tls") == 0) ? SIPE_TRANSPORT_TLS :
3183 (strcmp(transport, "tcp") == 0) ? SIPE_TRANSPORT_TCP :
3184 SIPE_TRANSPORT_UDP;
3185 create_connection(sip, g_strdup(purple_account_get_string(account, "proxy", sip->sipdomain)), 0);
3186 } else if (strcmp(transport, "auto") == 0) {
3187 resolve_next_service(sip, purple_ssl_is_supported() ? service_autodetect : service_tcp);
3188 } else if (strcmp(transport, "tls") == 0) {
3189 resolve_next_service(sip, service_tls);
3190 } else if (strcmp(transport, "tcp") == 0) {
3191 resolve_next_service(sip, service_tcp);
3192 } else {
3193 resolve_next_service(sip, service_udp);
3197 static void sipe_connection_cleanup(struct sipe_account_data *sip)
3199 connection_free_all(sip);
3201 if (sip->query_data != NULL)
3202 purple_dnsquery_destroy(sip->query_data);
3203 sip->query_data == NULL;
3205 if (sip->srv_query_data != NULL)
3206 purple_srv_cancel(sip->srv_query_data);
3207 sip->srv_query_data = NULL;
3209 if (sip->listen_data != NULL)
3210 purple_network_listen_cancel(sip->listen_data);
3211 sip->listen_data = NULL;
3213 g_free(sip->registrar.nonce);
3214 sip->registrar.nonce = NULL;
3215 g_free(sip->registrar.opaque);
3216 sip->registrar.opaque = NULL;
3217 g_free(sip->registrar.realm);
3218 sip->registrar.realm = NULL;
3219 g_free(sip->registrar.target);
3220 sip->registrar.target = NULL;
3221 g_free(sip->registrar.digest_session_key);
3222 sip->registrar.digest_session_key = NULL;
3223 g_free(sip->registrar.ntlm_key);
3224 sip->registrar.ntlm_key = NULL;
3225 sip->registrar.type = 0;
3226 sip->registrar.retries = 0;
3228 g_free(sip->proxy.nonce);
3229 sip->proxy.nonce = NULL;
3230 g_free(sip->proxy.opaque);
3231 sip->proxy.opaque = NULL;
3232 g_free(sip->proxy.realm);
3233 sip->proxy.realm = NULL;
3234 g_free(sip->proxy.target);
3235 sip->proxy.target = NULL;
3236 g_free(sip->proxy.digest_session_key);
3237 sip->proxy.digest_session_key = NULL;
3238 g_free(sip->proxy.ntlm_key);
3239 sip->proxy.ntlm_key = NULL;
3240 sip->proxy.type = 0;
3241 sip->proxy.retries = 0;
3243 if (sip->txbuf)
3244 purple_circ_buffer_destroy(sip->txbuf);
3245 sip->txbuf = NULL;
3247 g_free(sip->realhostname);
3248 sip->realhostname = NULL;
3250 if (sip->listenpa)
3251 purple_input_remove(sip->listenpa);
3252 sip->listenpa = 0;
3253 if (sip->tx_handler)
3254 purple_input_remove(sip->tx_handler);
3255 sip->tx_handler = 0;
3256 if (sip->resendtimeout)
3257 purple_timeout_remove(sip->resendtimeout);
3258 sip->resendtimeout = 0;
3259 if (sip->registertimeout)
3260 purple_timeout_remove(sip->registertimeout);
3261 sip->registertimeout = 0;
3264 static void sipe_close(PurpleConnection *gc)
3266 struct sipe_account_data *sip = gc->proto_data;
3268 if (sip) {
3269 /* leave all conversations */
3270 im_session_close_all(sip);
3272 /* unregister */
3273 do_register_exp(sip, 0);
3275 sipe_connection_cleanup(sip);
3276 g_free(sip->sipdomain);
3277 g_free(sip->username);
3278 g_free(sip->password);
3280 g_free(gc->proto_data);
3281 gc->proto_data = NULL;
3284 /* not needed since privacy is checked for every subscribe */
3285 static void dummy_add_deny(PurpleConnection *gc, const char *name) {
3288 static void dummy_permit_deny(PurpleConnection *gc)
3292 static gboolean sipe_plugin_load(PurplePlugin *plugin)
3294 return TRUE;
3298 static gboolean sipe_plugin_unload(PurplePlugin *plugin)
3300 return TRUE;
3304 static void sipe_plugin_destroy(PurplePlugin *plugin)
3308 static PurplePlugin *my_protocol = NULL;
3310 static PurplePluginProtocolInfo prpl_info =
3313 NULL, /* user_splits */
3314 NULL, /* protocol_options */
3315 NO_BUDDY_ICONS, /* icon_spec */
3316 sipe_list_icon, /* list_icon */
3317 NULL, /* list_emblems */
3318 NULL, /* status_text */ // TODO
3319 NULL, /* tooltip_text */ // add custom info to contact tooltip
3320 sipe_status_types, /* away_states */
3321 NULL, /* blist_node_menu */
3322 NULL, /* chat_info */
3323 NULL, /* chat_info_defaults */
3324 sipe_login, /* login */
3325 sipe_close, /* close */
3326 sipe_im_send, /* send_im */
3327 NULL, /* set_info */ // TODO maybe
3328 sipe_send_typing, /* send_typing */
3329 NULL, /* get_info */ // TODO maybe
3330 sipe_set_status, /* set_status */
3331 NULL, /* set_idle */
3332 NULL, /* change_passwd */
3333 sipe_add_buddy, /* add_buddy */
3334 NULL, /* add_buddies */
3335 sipe_remove_buddy, /* remove_buddy */
3336 NULL, /* remove_buddies */
3337 dummy_add_deny, /* add_permit */
3338 dummy_add_deny, /* add_deny */
3339 dummy_add_deny, /* rem_permit */
3340 dummy_add_deny, /* rem_deny */
3341 dummy_permit_deny, /* set_permit_deny */
3342 NULL, /* join_chat */
3343 NULL, /* reject_chat */
3344 NULL, /* get_chat_name */
3345 NULL, /* chat_invite */
3346 NULL, /* chat_leave */
3347 NULL, /* chat_whisper */
3348 NULL, /* chat_send */
3349 sipe_keep_alive, /* keepalive */
3350 NULL, /* register_user */
3351 NULL, /* get_cb_info */ // deprecated
3352 NULL, /* get_cb_away */ // deprecated
3353 sipe_alias_buddy, /* alias_buddy */
3354 sipe_group_buddy, /* group_buddy */
3355 sipe_rename_group, /* rename_group */
3356 NULL, /* buddy_free */
3357 sipe_convo_closed, /* convo_closed */
3358 purple_normalize_nocase, /* normalize */
3359 NULL, /* set_buddy_icon */
3360 sipe_remove_group, /* remove_group */
3361 NULL, /* get_cb_real_name */ // TODO?
3362 NULL, /* set_chat_topic */
3363 NULL, /* find_blist_chat */
3364 NULL, /* roomlist_get_list */
3365 NULL, /* roomlist_cancel */
3366 NULL, /* roomlist_expand_category */
3367 NULL, /* can_receive_file */
3368 NULL, /* send_file */
3369 NULL, /* new_xfer */
3370 NULL, /* offline_message */
3371 NULL, /* whiteboard_prpl_ops */
3372 sipe_send_raw, /* send_raw */
3376 static PurplePluginInfo info = {
3377 PURPLE_PLUGIN_MAGIC,
3378 PURPLE_MAJOR_VERSION,
3379 PURPLE_MINOR_VERSION,
3380 PURPLE_PLUGIN_PROTOCOL, /**< type */
3381 NULL, /**< ui_requirement */
3382 0, /**< flags */
3383 NULL, /**< dependencies */
3384 PURPLE_PRIORITY_DEFAULT, /**< priority */
3385 "prpl-sipe", /**< id */
3386 "Microsoft LCS/OCS", /**< name */
3387 VERSION, /**< version */
3388 N_("SIP/SIMPLE OCS/LCS Protocol Plugin"), /** summary */
3389 N_("The SIP/SIMPLE LCS/OCS Protocol Plugin"), /** description */
3390 "Anibal Avelar <avelar@gmail.com>, " /**< author */
3391 "Gabriel Burt <gburt@novell.com>", /**< author */
3392 PURPLE_WEBSITE, /**< homepage */
3393 sipe_plugin_load, /**< load */
3394 sipe_plugin_unload, /**< unload */
3395 sipe_plugin_destroy, /**< destroy */
3396 NULL, /**< ui_info */
3397 &prpl_info, /**< extra_info */
3398 NULL,
3399 NULL,
3400 NULL,
3401 NULL,
3402 NULL,
3403 NULL
3406 static void init_plugin(PurplePlugin *plugin)
3408 PurpleAccountUserSplit *split;
3409 PurpleAccountOption *option;
3410 PurpleKeyValuePair *kvp;
3412 #ifdef ENABLE_NLS
3413 purple_debug_info(PACKAGE, "bindtextdomain = %s", bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR));
3414 purple_debug_info(PACKAGE, "bind_textdomain_codeset = %s",
3415 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"));
3416 #endif
3418 purple_plugin_register(plugin);
3420 option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
3421 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3422 option = purple_account_option_string_new(_("Proxy Server"), "proxy", "");
3423 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3425 option = purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE);
3426 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3427 // Translators: noun (networking port)
3428 option = purple_account_option_int_new(_("Port"), "port", 5061);
3429 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3431 option = purple_account_option_list_new(_("Connection Type"), "transport", NULL);
3432 purple_account_option_add_list_item(option, _("Auto"), "auto");
3433 purple_account_option_add_list_item(option, _("SSL/TLS"), "tls");
3434 purple_account_option_add_list_item(option, _("TCP"), "tcp");
3435 purple_account_option_add_list_item(option, _("UDP"), "udp");
3436 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3438 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
3439 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
3441 option = purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION);
3442 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3444 // TODO commented out so won't show in the preferences until we fix krb message signing
3445 /*option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
3446 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3448 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
3449 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
3450 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3453 option = purple_account_option_string_new(_("Auth User"), "authuser", "");
3454 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3455 option = purple_account_option_string_new(_("Auth Domain"), "authdomain", "");
3456 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3457 my_protocol = plugin;
3460 /* I had to redefined the function for it load, but works */
3461 gboolean purple_init_plugin(PurplePlugin *plugin){
3462 plugin->info = &(info);
3463 init_plugin((plugin));
3464 sipe_plugin_load((plugin));
3465 return purple_plugin_register(plugin);