added User-Agent account option
[siplcs.git] / src / sipe.c
blob709db12b741e85fe1c4a356c992267c137722ab1
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 "accountopt.h"
45 #include "blist.h"
46 #include "conversation.h"
47 #include "dnsquery.h"
48 #include "debug.h"
49 #include "notify.h"
50 #include "privacy.h"
51 #include "prpl.h"
52 #include "plugin.h"
53 #include "util.h"
54 #include "version.h"
55 #include "network.h"
56 #include "xmlnode.h"
58 #include "sipe.h"
59 #include "sip-ntlm.h"
60 #include "sipkrb5.h"
62 #include "sipmsg.h"
63 #include "sipe-sign.h"
64 #include "dnssrv.h"
66 static char *gentag()
68 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
71 static char *getuuid()
73 return g_strdup_printf("01010101"); //TODO Should be taken from the MAC ADDRESS
76 static char *genbranch()
78 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
79 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
80 rand() & 0xFFFF, rand() & 0xFFFF);
83 static char *gencallid()
85 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
86 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
87 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
88 rand() & 0xFFFF, rand() & 0xFFFF);
91 static gchar *find_tag(const gchar *hdr)
93 gchar * tag = sipmsg_find_part_of_header (hdr, "tag=", ";", NULL);
94 if (!tag) {
95 // In case it's at the end and there's no trailing ;
96 tag = sipmsg_find_part_of_header (hdr, "tag=", NULL, NULL);
98 return tag;
102 static const char *sipe_list_icon(PurpleAccount *a, PurpleBuddy *b)
104 return "sipe";
107 static void sipe_keep_alive(PurpleConnection *gc)
109 struct sipe_account_data *sip = gc->proto_data;
110 if (sip->udp) {
111 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
112 gchar buf[2] = {0, 0};
113 purple_debug_info("sipe", "sending keep alive\n");
114 sendto(sip->fd, buf, 1, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in));
118 static gboolean process_register_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc);
120 static void sipe_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
121 static void sipe_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
122 gpointer data);
124 static void send_notify(struct sipe_account_data *sip, struct sipe_watcher *);
126 static void send_service(struct sipe_account_data *sip);
127 static void sipe_subscribe_to_name(struct sipe_account_data *sip, const char * buddy_name);
128 static void send_publish(struct sipe_account_data *sip);
130 static void do_notifies(struct sipe_account_data *sip)
132 GSList *tmp = sip->watcher;
133 purple_debug_info("sipe", "do_notifies()\n");
134 //if ((sip->republish != -1) || sip->republish < time(NULL)) {
135 // if (purple_account_get_bool(sip->account, "doservice", TRUE)) {
136 // send_service(sip);
137 // }
140 while (tmp) {
141 purple_debug_info("sipe", "notifying %s\n", ((struct sipe_watcher*)tmp->data)->name);
142 send_notify(sip, tmp->data);
143 tmp = tmp->next;
147 static void sipe_set_status(PurpleAccount *account, PurpleStatus *status)
149 PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_type(status));
150 struct sipe_account_data *sip = NULL;
152 if (!purple_status_is_active(status))
153 return;
155 if (account->gc)
156 sip = account->gc->proto_data;
158 if (sip)
160 g_free(sip->status);
161 if (primitive == PURPLE_STATUS_AVAILABLE)
162 sip->status = g_strdup("available");
163 else
164 sip->status = g_strdup("busy");
166 do_notifies(sip);
170 static struct sip_connection *connection_find(struct sipe_account_data *sip, int fd)
172 struct sip_connection *ret = NULL;
173 GSList *entry = sip->openconns;
174 while (entry) {
175 ret = entry->data;
176 if (ret->fd == fd) return ret;
177 entry = entry->next;
179 return NULL;
182 static struct sipe_watcher *watcher_find(struct sipe_account_data *sip,
183 const gchar *name)
185 struct sipe_watcher *watcher;
186 GSList *entry = sip->watcher;
187 while (entry) {
188 watcher = entry->data;
189 if (!strcmp(name, watcher->name)) return watcher;
190 entry = entry->next;
192 return NULL;
195 static struct sipe_watcher *watcher_create(struct sipe_account_data *sip,
196 const gchar *name, const gchar *callid, const gchar *ourtag,
197 const gchar *theirtag, gboolean needsxpidf)
199 struct sipe_watcher *watcher = g_new0(struct sipe_watcher, 1);
200 watcher->name = g_strdup(name);
201 watcher->dialog.callid = g_strdup(callid);
202 watcher->dialog.ourtag = g_strdup(ourtag);
203 watcher->dialog.theirtag = g_strdup(theirtag);
204 watcher->needsxpidf = needsxpidf;
205 sip->watcher = g_slist_append(sip->watcher, watcher);
206 return watcher;
209 static void watcher_remove(struct sipe_account_data *sip, const gchar *name)
211 struct sipe_watcher *watcher = watcher_find(sip, name);
212 sip->watcher = g_slist_remove(sip->watcher, watcher);
213 g_free(watcher->name);
214 g_free(watcher->dialog.callid);
215 g_free(watcher->dialog.ourtag);
216 g_free(watcher->dialog.theirtag);
217 g_free(watcher);
220 static struct sip_connection *connection_create(struct sipe_account_data *sip, int fd)
222 struct sip_connection *ret = g_new0(struct sip_connection, 1);
223 ret->fd = fd;
224 sip->openconns = g_slist_append(sip->openconns, ret);
225 return ret;
228 static void connection_remove(struct sipe_account_data *sip, int fd)
230 struct sip_connection *conn = connection_find(sip, fd);
231 sip->openconns = g_slist_remove(sip->openconns, conn);
232 if (conn->inputhandler) purple_input_remove(conn->inputhandler);
233 g_free(conn->inbuf);
234 g_free(conn);
237 static void connection_free_all(struct sipe_account_data *sip)
239 struct sip_connection *ret = NULL;
240 GSList *entry = sip->openconns;
241 while (entry) {
242 ret = entry->data;
243 connection_remove(sip, ret->fd);
244 entry = sip->openconns;
248 static void sipe_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
250 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
251 struct sipe_buddy *b;
252 if (strncmp("sip:", buddy->name, 4)) {
253 gchar *buf = g_strdup_printf("sip:%s", buddy->name);
254 purple_blist_rename_buddy(buddy, buf);
255 g_free(buf);
257 if (!g_hash_table_lookup(sip->buddies, buddy->name)) {
258 b = g_new0(struct sipe_buddy, 1);
259 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy->name);
260 b->name = g_strdup(buddy->name);
261 g_hash_table_insert(sip->buddies, b->name, b);
262 } else {
263 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy->name);
267 static void sipe_get_buddies(PurpleConnection *gc)
269 PurpleBlistNode *gnode, *cnode, *bnode;
271 purple_debug_info("sipe", "sipe_get_buddies\n");
273 for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
274 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue;
275 for (cnode = gnode->child; cnode; cnode = cnode->next) {
276 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue;
277 for (bnode = cnode->child; bnode; bnode = bnode->next) {
278 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue;
279 if (((PurpleBuddy*)bnode)->account == gc->account)
280 sipe_add_buddy(gc, (PurpleBuddy*)bnode, (PurpleGroup *)gnode);
286 static void sipe_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
288 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
289 struct sipe_buddy *b = g_hash_table_lookup(sip->buddies, buddy->name);
290 g_hash_table_remove(sip->buddies, buddy->name);
291 g_free(b->name);
292 g_free(b);
295 static GList *sipe_status_types(PurpleAccount *acc)
297 PurpleStatusType *type;
298 GList *types = NULL;
300 type = purple_status_type_new_with_attrs(
301 PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
302 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
303 NULL);
304 types = g_list_append(types, type);
306 type = purple_status_type_new_full(
307 PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE);
308 types = g_list_append(types, type);
310 return types;
313 //static struct sipe_krb5_auth krb5_auth;
314 static gchar *auth_header_without_newline(struct sipe_account_data *sip, struct sip_auth *auth, struct sipmsg * msg, gboolean force_reauth)
316 const gchar *method = msg->method;
317 const gchar *target = msg->target;
318 gchar noncecount[9];
319 gchar *response;
320 gchar *ret;
321 gchar *tmp;
322 const char *authdomain;
323 const char *authuser;
324 const char *krb5_realm;
325 const char *host;
326 gchar *krb5_token = NULL;
328 authdomain = purple_account_get_string(sip->account, "authdomain", "");
329 authuser = purple_account_get_string(sip->account, "authuser", sip->username);
331 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
332 // and do error checking
334 // KRB realm should always be uppercase
335 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
337 if (sip->realhostname) {
338 host = sip->realhostname;
339 } else if (purple_account_get_bool(sip->account, "use_proxy", TRUE)) {
340 host = purple_account_get_string(sip->account, "proxy", "");
341 } else {
342 host = sip->sipdomain;
345 /*gboolean new_auth = krb5_auth.gss_context == NULL;
346 if (new_auth) {
347 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
350 if (new_auth || force_reauth) {
351 krb5_token = krb5_auth.base64_token;
354 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
355 krb5_token = krb5_auth.base64_token;*/
357 if (!authuser || strlen(authuser) < 1) {
358 authuser = sip->username;
361 if (auth->type == 1) { /* Digest */
362 sprintf(noncecount, "%08d", auth->nc++);
363 response = purple_cipher_http_digest_calculate_response(
364 "md5", method, target, NULL, NULL,
365 auth->nonce, noncecount, NULL, auth->digest_session_key);
366 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response %s\n", response);
368 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);
369 g_free(response);
370 return ret;
371 } else if (auth->type == 2) { /* NTLM */
372 // If we have a signature for the message, include that
373 if (msg->signature) {
374 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);
375 return tmp;
378 if (auth->nc == 3 && auth->nonce && auth->ntlm_key == NULL) {
379 /* TODO: Don't hardcode "purple" as the hostname */
380 const gchar * ntlm_key;
381 gchar * gssapi_data = purple_ntlm_gen_authenticate(&ntlm_key, authuser, sip->password, "purple", authdomain, (const guint8 *)auth->nonce, &auth->flags);
382 auth->ntlm_key = (gchar *)ntlm_key;
383 tmp = g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth->opaque, auth->realm, auth->target, gssapi_data);
384 g_free(gssapi_data);
385 return tmp;
388 tmp = g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth->realm, auth->target);
389 return tmp;
390 } else if (auth->type == 3) {
391 /* Kerberos */
392 if (auth->nc == 3) {
393 /*if (new_auth || force_reauth) {
394 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
395 if (auth->opaque) {
396 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);
397 } else {
398 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
400 } else {
401 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
402 gchar * mic = "MICTODO";
403 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
404 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
405 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
406 //auth->opaque ? auth->opaque : "", auth->target);
407 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
408 //g_free(mic);
410 return tmp;
412 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth->target);
415 sprintf(noncecount, "%08d", auth->nc++);
416 response = purple_cipher_http_digest_calculate_response(
417 "md5", method, target, NULL, NULL,
418 auth->nonce, noncecount, NULL, auth->digest_session_key);
419 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response %s\n", response);
421 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);
422 g_free(response);
423 return ret;
426 static gchar *auth_header(struct sipe_account_data *sip, struct sip_auth *auth, struct sipmsg * msg, gboolean force_reauth)
428 gchar *with, *without;
430 without = auth_header_without_newline(sip, auth, msg, force_reauth);
431 with = g_strdup_printf("%s\r\n", without);
432 g_free (without);
433 return with;
437 static char *parse_attribute(const char *attrname, const char *source)
439 const char *tmp, *tmp2;
440 char *retval = NULL;
441 int len = strlen(attrname);
443 if (!strncmp(source, attrname, len)) {
444 tmp = source + len;
445 tmp2 = g_strstr_len(tmp, strlen(tmp), "\"");
446 if (tmp2)
447 retval = g_strndup(tmp, tmp2 - tmp);
448 else
449 retval = g_strdup(tmp);
452 return retval;
455 static void fill_auth(struct sipe_account_data *sip, gchar *hdr, struct sip_auth *auth)
457 int i = 0;
458 const char *authuser;
459 char *tmp;
460 gchar **parts;
461 const char *krb5_realm;
462 const char *host;
464 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
465 // and do error checking
467 // KRB realm should always be uppercase
468 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
470 if (sip->realhostname) {
471 host = sip->realhostname;
472 } else if (purple_account_get_bool(sip->account, "use_proxy", TRUE)) {
473 host = purple_account_get_string(sip->account, "proxy", "");
474 } else {
475 host = sip->sipdomain;
478 authuser = purple_account_get_string(sip->account, "authuser", sip->username);
480 if (!authuser || strlen(authuser) < 1) {
481 authuser = sip->username;
484 if (!hdr) {
485 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
486 return;
489 if (!g_strncasecmp(hdr, "NTLM", 4)) {
490 auth->type = 2;
491 parts = g_strsplit(hdr+5, "\", ", 0);
492 i = 0;
493 while (parts[i]) {
494 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
495 if ((tmp = parse_attribute("gssapi-data=\"", parts[i]))) {
496 auth->nonce = g_memdup(purple_ntlm_parse_challenge(tmp, &auth->flags), 8);
497 g_free(tmp);
499 if ((tmp = parse_attribute("targetname=\"",
500 parts[i]))) {
501 auth->target = tmp;
503 else if ((tmp = parse_attribute("realm=\"",
504 parts[i]))) {
505 auth->realm = tmp;
507 else if ((tmp = parse_attribute("opaque=\"", parts[i]))) {
508 auth->opaque = tmp;
510 i++;
512 g_strfreev(parts);
513 auth->nc = 1;
514 if (!strstr(hdr, "gssapi-data")) {
515 auth->nc = 1;
516 } else {
517 auth->nc = 3;
519 return;
522 if (!g_strncasecmp(hdr, "Kerberos", 8)) {
523 purple_debug(PURPLE_DEBUG_MISC, "sipe", "setting auth type to Kerberos (3)\r\n");
524 auth->type = 3;
525 purple_debug(PURPLE_DEBUG_MISC, "sipe", "fill_auth - header: %s\r\n", hdr);
526 parts = g_strsplit(hdr+9, "\", ", 0);
527 i = 0;
528 while (parts[i]) {
529 purple_debug_info("sipe", "krb - parts[i] %s\n", parts[i]);
530 if ((tmp = parse_attribute("gssapi-data=\"", parts[i]))) {
531 /*if (krb5_auth.gss_context == NULL) {
532 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
534 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
535 g_free(tmp);
537 if ((tmp = parse_attribute("targetname=\"", parts[i]))) {
538 auth->target = tmp;
539 } else if ((tmp = parse_attribute("realm=\"", parts[i]))) {
540 auth->realm = tmp;
541 } else if ((tmp = parse_attribute("opaque=\"", parts[i]))) {
542 auth->opaque = tmp;
544 i++;
546 g_strfreev(parts);
547 auth->nc = 3;
548 //if (!strstr(hdr, "gssapi-data")) {
549 // auth->nc = 1;
550 //} else {
551 // auth->nc = 3;
553 return;
556 auth->type = 1;
557 parts = g_strsplit(hdr, " ", 0);
558 while (parts[i]) {
559 if ((tmp = parse_attribute("nonce=\"", parts[i]))) {
560 auth->nonce = tmp;
562 else if ((tmp = parse_attribute("realm=\"", parts[i]))) {
563 auth->realm = tmp;
565 i++;
567 g_strfreev(parts);
569 purple_debug(PURPLE_DEBUG_MISC, "sipe", "nonce: %s realm: %s\n", auth->nonce ? auth->nonce : "(null)", auth->realm ? auth->realm : "(null)");
570 if (auth->realm) {
571 auth->digest_session_key = purple_cipher_http_digest_calculate_session_key(
572 "md5", authuser, auth->realm, sip->password, auth->nonce, NULL);
574 auth->nc = 1;
578 static void sipe_canwrite_cb(gpointer data, gint source, PurpleInputCondition cond)
580 PurpleConnection *gc = data;
581 struct sipe_account_data *sip = gc->proto_data;
582 gsize max_write;
583 gssize written;
585 max_write = purple_circ_buffer_get_max_read(sip->txbuf);
587 if (max_write == 0) {
588 purple_input_remove(sip->tx_handler);
589 sip->tx_handler = 0;
590 return;
593 written = write(sip->fd, sip->txbuf->outptr, max_write);
595 if (written < 0 && errno == EAGAIN)
596 written = 0;
597 else if (written <= 0) {
598 /*TODO: do we really want to disconnect on a failure to write?*/
599 purple_connection_error(gc, _("Could not write"));
600 return;
603 purple_circ_buffer_mark_read(sip->txbuf, written);
606 static void sipe_canwrite_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
608 PurpleConnection *gc = data;
609 struct sipe_account_data *sip = gc->proto_data;
610 gsize max_write;
611 gssize written;
613 max_write = purple_circ_buffer_get_max_read(sip->txbuf);
615 if (max_write == 0) {
616 purple_input_remove(sip->tx_handler);
617 sip->tx_handler = 0;
618 return;
621 written = purple_ssl_write(sip->gsc, sip->txbuf->outptr, max_write);
623 if (written < 0 && errno == EAGAIN)
624 written = 0;
625 else if (written <= 0) {
626 /*TODO: do we really want to disconnect on a failure to write?*/
627 purple_connection_error(gc, _("Could not write"));
628 return;
631 purple_circ_buffer_mark_read(sip->txbuf, written);
634 static void sipe_input_cb(gpointer data, gint source, PurpleInputCondition cond);
636 static void send_later_cb(gpointer data, gint source, const gchar *error)
638 PurpleConnection *gc = data;
639 struct sipe_account_data *sip;
640 struct sip_connection *conn;
642 if (!PURPLE_CONNECTION_IS_VALID(gc))
644 if (source >= 0)
645 close(source);
646 return;
649 if (source < 0) {
650 purple_connection_error(gc, _("Could not connect"));
651 return;
654 sip = gc->proto_data;
655 sip->fd = source;
656 sip->connecting = FALSE;
658 sipe_canwrite_cb(gc, sip->fd, PURPLE_INPUT_WRITE);
660 /* If there is more to write now, we need to register a handler */
661 if (sip->txbuf->bufused > 0)
662 sip->tx_handler = purple_input_add(sip->fd, PURPLE_INPUT_WRITE,
663 sipe_canwrite_cb, gc);
665 conn = connection_create(sip, source);
666 conn->inputhandler = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_input_cb, gc);
669 static void send_later_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
671 PurpleConnection *gc = data;
672 struct sipe_account_data *sip;
673 struct sip_connection *conn;
675 if (!PURPLE_CONNECTION_IS_VALID(gc))
677 if(gsc) purple_ssl_close(gsc);
678 return;
681 sip = gc->proto_data;
682 sip->fd = gsc->fd;
683 sip->connecting = FALSE;
685 sipe_canwrite_cb_ssl(gc, gsc, PURPLE_INPUT_WRITE);
687 /* If there is more to write now, we need to register a handler */
688 if (sip->txbuf->bufused > 0)
689 purple_ssl_input_add(gsc, sipe_canwrite_cb_ssl, gc);
691 conn = connection_create(sip, gsc->fd);
692 purple_ssl_input_add(sip->gsc, sipe_input_cb_ssl, gc);
696 static void sendlater(PurpleConnection *gc, const char *buf)
698 struct sipe_account_data *sip = gc->proto_data;
700 if (!sip->connecting) {
701 purple_debug_info("sipe", "connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport);
702 if (sip->use_ssl){
703 sip->gsc = purple_ssl_connect(sip->account,sip->realhostname, sip->realport, send_later_cb_ssl, sipe_ssl_connect_failure, sip->gc);
705 else{
706 if (purple_proxy_connect(gc, sip->account, sip->realhostname, sip->realport, send_later_cb, gc) == NULL) {
707 purple_connection_error(gc, _("Couldn't create socket"));
710 sip->connecting = TRUE;
713 if (purple_circ_buffer_get_max_read(sip->txbuf) > 0)
714 purple_circ_buffer_append(sip->txbuf, "\r\n", 2);
716 purple_circ_buffer_append(sip->txbuf, buf, strlen(buf));
719 static void sendout_pkt(PurpleConnection *gc, const char *buf)
721 struct sipe_account_data *sip = gc->proto_data;
722 time_t currtime = time(NULL);
723 int writelen = strlen(buf);
725 purple_debug(PURPLE_DEBUG_MISC, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime), buf);
726 if (sip->udp) {
727 if (sendto(sip->fd, buf, writelen, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)) < writelen) {
728 purple_debug_info("sipe", "could not send packet\n");
730 } else {
731 int ret;
732 if (sip->fd < 0) {
733 sendlater(gc, buf);
734 return;
737 if (sip->tx_handler) {
738 ret = -1;
739 errno = EAGAIN;
740 } else{
741 if (sip->gsc){
742 ret = purple_ssl_write(sip->gsc, buf, writelen);
743 }else{
744 ret = write(sip->fd, buf, writelen);
748 if (ret < 0 && errno == EAGAIN)
749 ret = 0;
750 else if (ret <= 0) { /* XXX: When does this happen legitimately? */
751 sendlater(gc, buf);
752 return;
755 if (ret < writelen) {
756 if (!sip->tx_handler){
757 if (sip->gsc){
758 purple_ssl_input_add(sip->gsc, sipe_canwrite_cb_ssl, gc);
760 else{
761 sip->tx_handler = purple_input_add(sip->fd,
762 PURPLE_INPUT_WRITE, sipe_canwrite_cb,
763 gc);
767 /* XXX: is it OK to do this? You might get part of a request sent
768 with part of another. */
769 if (sip->txbuf->bufused > 0)
770 purple_circ_buffer_append(sip->txbuf, "\r\n", 2);
772 purple_circ_buffer_append(sip->txbuf, buf + ret,
773 writelen - ret);
778 static int sipe_send_raw(PurpleConnection *gc, const char *buf, int len)
780 sendout_pkt(gc, buf);
781 return len;
784 static void sendout_sipmsg(struct sipe_account_data *sip, struct sipmsg *msg)
786 GSList *tmp = msg->headers;
787 gchar *name;
788 gchar *value;
789 GString *outstr = g_string_new("");
790 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n", msg->method, msg->target);
791 while (tmp) {
792 name = ((struct siphdrelement*) (tmp->data))->name;
793 value = ((struct siphdrelement*) (tmp->data))->value;
794 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
795 tmp = g_slist_next(tmp);
797 g_string_append_printf(outstr, "\r\n%s", msg->body ? msg->body : "");
798 sendout_pkt(sip->gc, outstr->str);
799 g_string_free(outstr, TRUE);
802 static void sign_outgoing_message (struct sipmsg * msg, struct sipe_account_data *sip, const gchar *method)
804 gchar * buf;
805 if (sip->registrar.ntlm_key) {
806 struct sipmsg_breakdown msgbd;
807 msgbd.msg = msg;
808 sipmsg_breakdown_parse(&msgbd, sip->registrar.realm, sip->registrar.target);
809 // TODO generate this
810 msgbd.rand = g_strdup("0878F41B");
811 sip->registrar.ntlm_num++;
812 msgbd.num = g_strdup_printf("%d", sip->registrar.ntlm_num);
813 gchar * signature_input_str = sipmsg_breakdown_get_string(&msgbd);
814 if (signature_input_str != NULL) {
815 msg->signature = purple_ntlm_sipe_signature_make (signature_input_str, sip->registrar.ntlm_key);
816 msg->rand = g_strdup(msgbd.rand);
817 msg->num = g_strdup(msgbd.num);
819 sipmsg_breakdown_free(&msgbd);
822 if (sip->registrar.type && !strcmp(method, "REGISTER")) {
823 buf = auth_header_without_newline(sip, &sip->registrar, msg, FALSE);
824 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
825 sipmsg_add_header(msg, "Authorization", buf);
826 } else {
827 sipmsg_add_header_pos(msg, "Proxy-Authorization", buf, 5);
828 //sipmsg_add_header_pos(msg, "Authorization", buf, 5);
830 g_free(buf);
831 } else if (!strcmp(method,"SUBSCRIBE") || !strcmp(method,"SERVICE") || !strcmp(method,"MESSAGE") || !strcmp(method,"INVITE") || !strcmp(method,"NOTIFY") || !strcmp(method, "ACK") || !strcmp(method, "BYE") || !strcmp(method, "INFO")) {
832 sip->registrar.nc=3;
833 sip->registrar.type=2;
835 buf = auth_header_without_newline(sip, &sip->registrar, msg, FALSE);
836 //buf = auth_header(sip, &sip->proxy, msg, FALSE);
837 sipmsg_add_header_pos(msg, "Proxy-Authorization", buf, 5);
838 //sipmsg_add_header(msg, "Authorization", buf);
839 g_free(buf);
840 } else {
841 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method);
845 static char *get_contact(struct sipe_account_data *sip)
847 return g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace", sip->username, sip->listenport, purple_network_get_my_ip(-1), sip->use_ssl ? "tls" : sip->udp ? "udp" : "tcp");
852 static void send_sip_response(PurpleConnection *gc, struct sipmsg *msg, int code,
853 const char *text, const char *body)
855 GSList *tmp = msg->headers;
856 gchar *name;
857 gchar *value;
858 GString *outstr = g_string_new("");
859 struct sipe_account_data *sip = gc->proto_data;
861 gchar *contact;
862 contact = get_contact(sip);
863 sipmsg_remove_header(msg, "Contact");
864 sipmsg_add_header(msg, "Contact", contact);
865 g_free(contact);
867 /* When sending the acknowlegements and errors, the content length from the original
868 message is still here, but there is no body; we need to make sure we're sending the
869 correct content length */
870 sipmsg_remove_header(msg, "Content-Length");
871 if (body) {
872 gchar len[12];
873 sprintf(len, "%" G_GSIZE_FORMAT , strlen(body));
874 sipmsg_add_header(msg, "Content-Length", len);
875 } else {
876 sipmsg_add_header(msg, "Content-Length", "0");
879 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
880 //gchar * mic = "MICTODO";
881 msg->response = code;
883 sipmsg_remove_header(msg, "Authentication-Info");
884 sign_outgoing_message(msg, sip, msg->method);
886 g_string_append_printf(outstr, "SIP/2.0 %d %s\r\n", code, text);
887 while (tmp) {
888 name = ((struct siphdrelement*) (tmp->data))->name;
889 value = ((struct siphdrelement*) (tmp->data))->value;
891 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
892 tmp = g_slist_next(tmp);
894 g_string_append_printf(outstr, "\r\n%s", body ? body : "");
895 sendout_pkt(gc, outstr->str);
896 g_string_free(outstr, TRUE);
899 static void transactions_remove(struct sipe_account_data *sip, struct transaction *trans)
901 if (trans->msg) sipmsg_free(trans->msg);
902 sip->transactions = g_slist_remove(sip->transactions, trans);
903 g_free(trans);
906 static struct transaction *
907 transactions_add_buf(struct sipe_account_data *sip, const struct sipmsg *msg, void *callback)
909 struct transaction *trans = g_new0(struct transaction, 1);
910 trans->time = time(NULL);
911 trans->msg = (struct sipmsg *)msg;
912 trans->cseq = sipmsg_find_header(trans->msg, "CSeq");
913 trans->callback = callback;
914 sip->transactions = g_slist_append(sip->transactions, trans);
915 return trans;
918 static struct transaction *transactions_find(struct sipe_account_data *sip, struct sipmsg *msg)
920 struct transaction *trans;
921 GSList *transactions = sip->transactions;
922 gchar *cseq = sipmsg_find_header(msg, "CSeq");
924 while (transactions) {
925 trans = transactions->data;
926 if (!strcmp(trans->cseq, cseq)) {
927 return trans;
929 transactions = transactions->next;
932 return NULL;
936 static struct transaction *
937 send_sip_request(PurpleConnection *gc, const gchar *method,
938 const gchar *url, const gchar *to, const gchar *addheaders,
939 const gchar *body, struct sip_dialog *dialog, TransCallback tc)
941 struct sipe_account_data *sip = gc->proto_data;
942 const char *addh = "";
943 char *buf;
944 struct sipmsg *msg;
945 gchar *ptmp;
946 gchar *ourtag = dialog && dialog->ourtag ? g_strdup(dialog->ourtag) : gentag();
947 gchar *theirtag = dialog && dialog->theirtag ? g_strdup(dialog->theirtag) : NULL;
948 gchar *theirepid = dialog && dialog->theirepid ? g_strdup(dialog->theirepid) : NULL;
949 gchar *callid = dialog && dialog->callid ? g_strdup(dialog->callid) : gencallid();
950 gchar *branch = dialog && dialog->callid ? NULL : genbranch();
951 gchar *useragent = purple_account_get_string(sip->account, "useragent", "Purple/" VERSION);
952 if (!strcmp(method, "REGISTER")) {
953 if (sip->regcallid) {
954 g_free(callid);
955 callid = g_strdup(sip->regcallid);
956 } else {
957 sip->regcallid = g_strdup(callid);
961 if (addheaders) addh = addheaders;
963 buf = g_strdup_printf("%s %s SIP/2.0\r\n"
964 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
965 "From: <sip:%s>;tag=%s;epid=%s\r\n"
966 "To: <%s>%s%s%s%s\r\n"
967 "Max-Forwards: 70\r\n"
968 "CSeq: %d %s\r\n"
969 "User-Agent: %s\r\n"
970 "Call-ID: %s\r\n"
971 "%s%s"
972 "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n%s",
973 method,
974 dialog && dialog->request ? dialog->request : url,
975 sip->use_ssl ? "TLS" : sip->udp ? "UDP" : "TCP",
976 purple_network_get_my_ip(-1),
977 sip->listenport,
978 branch ? ";branch=" : "",
979 branch ? branch : "",
980 sip->username,
981 ourtag ? ourtag : "",
982 getuuid(), // TODO generate one per account/login
984 theirtag ? ";tag=" : "",
985 theirtag ? theirtag : "",
986 theirepid ? ";epid=" : "",
987 theirepid ? theirepid : "",
988 dialog ? ++dialog->cseq : ++sip->cseq,
989 method,
990 useragent,
991 callid,
992 dialog && dialog->route ? dialog->route : "",
993 addh,
994 body ? strlen(body) : 0,
995 body ? body : "");
998 //printf ("parsing msg buf:\n%s\n\n", buf);
999 msg = sipmsg_parse_msg(buf);
1001 g_free(buf);
1002 g_free(ourtag);
1003 g_free(theirtag);
1004 g_free(theirepid);
1005 g_free(branch);
1006 g_free(callid);
1008 sign_outgoing_message (msg, sip, method);
1010 buf = sipmsg_to_string (msg);
1012 /* add to ongoing transactions */
1013 struct transaction * trans = transactions_add_buf(sip, msg, tc);
1014 sendout_pkt(gc, buf);
1016 return trans;
1019 static char *get_contact_register(struct sipe_account_data *sip)
1021 return g_strdup_printf("<sip:%s:%d;transport=%s>;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, sip->use_ssl ? "tls" : sip->udp ? "udp" : "tcp",generateUUIDfromEPID(getuuid()));
1024 static void do_register_exp(struct sipe_account_data *sip, int expire)
1026 char *uri = g_strdup_printf("sip:%s", sip->sipdomain);
1027 char *to = g_strdup_printf("sip:%s", sip->username);
1028 char *contact = get_contact_register(sip);
1029 //char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire);
1030 // 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);
1031 //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);
1032 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);
1033 g_free(contact);
1035 sip->registerstatus = 1;
1037 if (expire) {
1038 sip->reregister = time(NULL) + expire - 50;
1039 } else {
1040 sip->reregister = time(NULL) + 600;
1043 send_sip_request(sip->gc, "REGISTER", uri, to, hdr, "", NULL,
1044 process_register_response);
1046 g_free(hdr);
1047 g_free(uri);
1048 g_free(to);
1051 static void do_register(struct sipe_account_data *sip)
1053 do_register_exp(sip, sip->registerexpire);
1056 static gchar *parse_from(const gchar *hdr)
1058 gchar *from;
1059 const gchar *tmp, *tmp2 = hdr;
1061 if (!hdr) return NULL;
1062 purple_debug_info("sipe", "parsing address out of %s\n", hdr);
1063 tmp = strchr(hdr, '<');
1065 /* i hate the different SIP UA behaviours... */
1066 if (tmp) { /* sip address in <...> */
1067 tmp2 = tmp + 1;
1068 tmp = strchr(tmp2, '>');
1069 if (tmp) {
1070 from = g_strndup(tmp2, tmp - tmp2);
1071 } else {
1072 purple_debug_info("sipe", "found < without > in From\n");
1073 return NULL;
1075 } else {
1076 tmp = strchr(tmp2, ';');
1077 if (tmp) {
1078 from = g_strndup(tmp2, tmp - tmp2);
1079 } else {
1080 from = g_strdup(tmp2);
1083 purple_debug_info("sipe", "got %s\n", from);
1084 return from;
1087 static gboolean process_subscribe_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1089 gchar *to;
1091 if (msg->response == 200 || msg->response == 202) {
1092 return TRUE;
1095 to = parse_from(sipmsg_find_header(tc->msg, "To")); /* cant be NULL since it is our own msg */
1097 /* we can not subscribe -> user is offline (TODO unknown status?) */
1099 purple_prpl_got_user_status(sip->account, to, "offline", NULL);
1100 g_free(to);
1101 return TRUE;
1104 static void sipe_subscribe_to_name(struct sipe_account_data *sip, const char * buddy_name)
1106 gchar *to = strstr(buddy_name, "sip:") ? g_strdup(buddy_name) : g_strdup_printf("sip:%s", buddy_name);
1107 gchar *tmp = get_contact(sip);
1108 gchar *contact = g_strdup_printf(
1109 "Accept: application/pidf+xml, application/xpidf+xml\r\n"
1110 "Event: presence\r\n"
1111 "Contact: %s\r\n", tmp);
1112 g_free(tmp);
1114 /* subscribe to buddy presence
1115 * we dont need to know the status so we do not need a callback */
1117 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL,
1118 process_subscribe_response);
1120 g_free(to);
1121 g_free(contact);
1124 static void sipe_subscribe(struct sipe_account_data *sip, struct sipe_buddy *buddy)
1126 sipe_subscribe_to_name(sip, buddy->name);
1128 /* resubscribe before subscription expires */
1129 /* add some jitter */
1130 buddy->resubscribe = time(NULL)+1140+(rand()%50);
1133 static gboolean sipe_add_lcs_contacts(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1135 gchar *tmp;
1136 xmlnode *item, *group, *isc;
1137 const char *name_group, *group_id;
1138 PurpleBuddy *b;
1139 PurpleGroup *g = NULL;
1140 gchar **parts;
1141 gchar *apn;
1142 int ng = 0, i;
1143 struct sipe_buddy *bs;
1144 struct sipe_group *gr;
1145 int len = msg->bodylen;
1147 // Reserved to max 10 groups. TODO be dynamic
1148 gr = g_new0(struct sipe_group, 10);
1150 tmp = sipmsg_find_header(msg, "Event");
1151 if (tmp && !strncmp(tmp, "vnd-microsoft-roaming-contacts", 30)) {
1152 purple_debug_info("sipe", "sipe_add_lcs_contacts->%s-%d\n", msg->body, len);
1153 /*Convert the contact from XML to Purple Buddies*/
1154 isc = xmlnode_from_str(msg->body, len);
1156 /* TODO Find for all groups */
1157 for (group = xmlnode_get_child(isc, "group"); group; group = xmlnode_get_next_twin(group)) {
1158 name_group = xmlnode_get_attrib(group, "name");
1159 group_id = xmlnode_get_attrib(group, "id");
1161 if (!strncmp(name_group, "~", 1)){
1162 name_group=g_strdup("General");
1165 gr[ng].name_group = g_strdup(name_group);
1166 gr[ng].id = g_strdup(group_id);
1167 purple_debug_info("sipe", "name_group->%s\n", name_group);
1168 g = purple_find_group(name_group);
1170 if (!g) {
1171 g = purple_group_new(name_group);
1172 purple_blist_add_group(g, NULL);
1175 if (!g) {
1176 g = purple_find_group("General");
1177 if (!g) {
1178 g = purple_group_new("General");
1179 purple_blist_add_group(g, NULL);
1183 gr[ng].g = g;
1184 ng++;
1187 for (i = 0; i < ng;i++) {
1188 purple_debug_info("sipe", "id->%s\n", gr[i].id);
1189 purple_debug_info("sipe", "id->%s\n", gr[i].name_group);
1192 for (item = xmlnode_get_child(isc, "contact"); item; item = xmlnode_get_next_twin(item)) {
1193 const char *uri, *name, *groups;
1194 char *buddy_name;
1195 i = 0;
1196 uri = xmlnode_get_attrib(item, "uri");
1197 name = xmlnode_get_attrib(item, "name");
1198 groups = xmlnode_get_attrib(item, "groups");
1199 parts = g_strsplit(groups, " ", 0);
1200 purple_debug_info("sipe", "URI->%s,Groups->%s\n", uri, groups);
1201 if (parts[i]!=NULL){
1202 while (parts[i]) {
1203 purple_debug_info("sipe", "Groups->parts[i] %s\n", parts[i]);
1204 if (!strcmp(gr[i].id,parts[i])){
1205 purple_debug_info("sipe", "Found Groups->gr[i].id(%s),gr[i].name_group (%s)\n",gr[i].id,gr[i].name_group);
1207 buddy_name = g_strdup_printf("sip:%s", uri);
1209 //b = purple_find_buddy(sip->account, buddy_name);
1210 b = purple_find_buddy_in_group(sip->account, buddy_name, gr[i].g);
1211 if (!b){
1212 b = purple_buddy_new(sip->account, buddy_name, uri);
1214 g_free(buddy_name);
1216 //sipe_add_buddy(sip->gc, b , gr[i].g);
1217 purple_blist_add_buddy(b, NULL, gr[i].g, NULL);
1218 purple_blist_alias_buddy(b, uri);
1219 bs = g_new0(struct sipe_buddy, 1);
1220 bs->name = g_strdup(b->name);
1221 g_hash_table_insert(sip->buddies, bs->name, bs);
1223 i++;
1228 xmlnode_free(isc);
1230 return 0;
1233 static void sipe_subscribe_buddylist(struct sipe_account_data *sip)
1235 gchar *contact = "Event: vnd-microsoft-roaming-contacts\r\nAccept: application/vnd-microsoft-roaming-contacts+xml\r\nSupported: com.microsoft.autoextend\r\nSupported: ms-benotify\r\nProxy-Require: ms-benotify\r\nSupported: ms-piggyback-first-notify\r\n";
1236 gchar *to = g_strdup_printf("sip:%s", sip->username);
1237 gchar *tmp = get_contact(sip);
1238 contact = g_strdup_printf("%sContact: %s\r\n", contact, tmp);
1239 g_free(tmp);
1241 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL, sipe_add_lcs_contacts);
1242 g_free(to);
1243 g_free(contact);
1246 /* IM Session (INVITE and MESSAGE methods) */
1248 static struct sip_im_session * find_im_session (struct sipe_account_data *sip, const char *who)
1250 if (sip == NULL || who == NULL) {
1251 return NULL;
1254 struct sip_im_session *session;
1255 GSList *entry = sip->im_sessions;
1256 while (entry) {
1257 session = entry->data;
1258 if ((who != NULL && !strcmp(who, session->with))) {
1259 return session;
1261 entry = entry->next;
1263 return NULL;
1266 static struct sip_im_session * find_or_create_im_session (struct sipe_account_data *sip, const char *who)
1268 struct sip_im_session *session = find_im_session(sip, who);
1269 if (!session) {
1270 session = g_new0(struct sip_im_session, 1);
1271 session->with = g_strdup(who);
1272 sip->im_sessions = g_slist_append(sip->im_sessions, session);
1274 return session;
1277 static void im_session_destroy(struct sipe_account_data *sip, struct sip_im_session * session)
1279 sip->im_sessions = g_slist_remove(sip->im_sessions, session);
1280 // TODO free session resources
1283 static void sipe_send_message(struct sipe_account_data *sip, struct sip_im_session * session, const char *msg)
1285 gchar *hdr;
1286 gchar *fullto;
1287 gchar *tmp;
1289 if (strncmp("sip:", session->with, 4)) {
1290 fullto = g_strdup_printf("sip:%s", session->with);
1291 } else {
1292 fullto = g_strdup(session->with);
1295 hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1296 //hdr = g_strdup("Content-Type: text/rtf\r\n");
1297 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
1299 tmp = get_contact(sip);
1300 hdr = g_strdup_printf("Contact: %s\r\n%s", tmp, hdr);
1301 g_free(tmp);
1303 send_sip_request(sip->gc, "MESSAGE", fullto, fullto, hdr, msg, session->outgoing_dialog, NULL);
1305 g_free(hdr);
1306 g_free(fullto);
1310 static void
1311 sipe_im_process_queue (struct sipe_account_data * sip, struct sip_im_session * session)
1313 GSList *entry = session->outgoing_message_queue;
1314 while (entry) {
1315 char *queued_msg = entry->data;
1316 sipe_send_message(sip, session, queued_msg);
1318 // Remove from the queue and free the string
1319 entry = session->outgoing_message_queue = g_slist_remove(session->outgoing_message_queue, queued_msg);
1320 g_free(queued_msg);
1324 static gboolean
1325 process_invite_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *trans)
1327 gchar * with = parse_from(sipmsg_find_header(msg, "To"));
1328 struct sip_im_session * session = find_im_session(sip, with);
1329 g_free(with);
1331 if (!session) {
1332 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
1333 return FALSE;
1336 if (msg->response != 200) {
1337 purple_debug_info("sipe", "process_invite_response: INVITE response not 200, ignoring\n");
1338 im_session_destroy(sip, session);
1339 return FALSE;
1342 struct sip_dialog * dialog = session->outgoing_dialog;
1343 if (!dialog) {
1344 purple_debug_info("sipe", "process_invite_response: session outgoign dialog is NULL\n");
1345 return FALSE;
1348 dialog->callid = sipmsg_find_header(msg, "Call-ID");
1349 dialog->ourtag = find_tag(sipmsg_find_header(msg, "From"));
1350 dialog->theirtag = find_tag(sipmsg_find_header(msg, "To"));
1351 if (!dialog->theirepid) {
1352 dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, "To"), "epid=", ";", NULL);
1354 if (!dialog->theirepid) {
1355 dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, "To"), "epid=", NULL, NULL);
1358 dialog->request = sipmsg_find_part_of_header(sipmsg_find_header(msg, "Record-Route"), "<", ">", NULL);
1359 dialog->route = g_strdup_printf("Route: %s\r\n", sipmsg_find_header(msg, "Contact"));
1360 dialog->cseq = 0;
1362 send_sip_request(sip->gc, "ACK", session->with, session->with, NULL, NULL, dialog, NULL);
1364 session->outgoing_invite = NULL;
1366 sipe_im_process_queue(sip, session);
1368 return TRUE;
1372 static void sipe_invite(struct sipe_account_data *sip, struct sip_im_session * session)
1374 gchar *hdr;
1375 gchar *to;
1376 gchar *contact;
1377 gchar *body;
1379 if (strstr(session->with, "sip:")) {
1380 to = g_strdup(session->with);
1381 } else {
1382 to = g_strdup_printf("sip:%s", session->with);
1385 // Setup the outgoing dialog w/ the epid from the incoming dialog (if any)
1386 struct sip_dialog * dialog = g_new0(struct sip_dialog, 1);
1387 if (session->incoming_dialog) {
1388 printf("incoming dialog epid is %s\n", session->incoming_dialog->theirepid);
1389 dialog->theirepid = session->incoming_dialog->theirepid;
1390 } else {
1391 printf("incoming dialog is NULL\n");
1393 session->outgoing_dialog = dialog;
1395 contact = get_contact(sip);
1396 hdr = g_strdup_printf(
1397 "Contact: %s\r\n"
1398 //"Supported: ms-conf-invite\r\n"
1399 //"Supported: ms-delayed-accept\r\n"
1400 //"Supported: ms-renders-isf\r\n"
1401 //"Supported: ms-renders-gif\r\n"
1402 //"Supported: ms-renders-mime-alternative\r\n"*/
1403 //"Supported: timer\r\n"
1404 //"Supported: ms-sender\r\n"
1405 //"Supported: ms-early-media\r\n"
1406 "Content-Type: application/sdp\r\n",
1407 contact, sip->username, sip->username, to);
1409 body = g_strdup_printf(
1410 "v=0\r\n"
1411 "o=- 0 0 IN IP4 %s\r\n"
1412 "s=session\r\n"
1413 "c=IN IP4 %s\r\n"
1414 "t=0 0\r\n"
1415 "m=message %d sip null\r\n"
1416 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1417 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), 5061);
1419 session->outgoing_invite = send_sip_request(sip->gc, "INVITE",
1420 to, to, hdr, body, session->outgoing_dialog, process_invite_response);
1422 g_free(to);
1423 g_free(body);
1424 g_free(hdr);
1425 g_free(contact);
1428 static void
1429 im_session_close (struct sipe_account_data *sip, struct sip_im_session * session)
1431 if (session) {
1432 send_sip_request(sip->gc, "BYE", session->with, session->with, NULL, NULL, session->outgoing_dialog, NULL);
1433 im_session_destroy(sip, session);
1437 static void
1438 sipe_convo_closed(PurpleConnection * gc, const char *who)
1440 struct sipe_account_data *sip = gc->proto_data;
1442 purple_debug_info("sipe", "conversation with %s closed\n", who);
1443 im_session_close(sip, find_im_session(sip, who));
1446 static void
1447 im_session_close_all (struct sipe_account_data *sip)
1449 GSList *entry = sip->im_sessions;
1450 while (entry) {
1451 im_session_close (sip, entry->data);
1452 entry = sip->im_sessions;
1456 static int sipe_im_send(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags)
1458 struct sipe_account_data *sip = gc->proto_data;
1459 char *to = g_strdup(who);
1460 char *text = purple_unescape_html(what);
1462 struct sip_im_session * session = find_or_create_im_session(sip, who);
1464 // Queue the message
1465 session->outgoing_message_queue = g_slist_append(session->outgoing_message_queue, text);
1467 if (session->outgoing_dialog && session->outgoing_dialog->callid) {
1468 sipe_im_process_queue(sip, session);
1469 } else if (!session->outgoing_invite) {
1470 // Need to send the INVITE to get the outgoing dialog setup
1471 sipe_invite(sip, session);
1474 g_free(to);
1475 return 1;
1479 /* End IM Session (INVITE and MESSAGE methods) */
1481 static void sipe_buddy_resub(char *name, struct sipe_buddy *buddy, struct sipe_account_data *sip)
1483 time_t curtime = time(NULL);
1484 if (buddy->resubscribe < curtime) {
1485 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_buddy_resub %s\n", name);
1486 sipe_subscribe(sip, buddy);
1490 static gboolean resend_timeout(struct sipe_account_data *sip)
1492 GSList *tmp = sip->transactions;
1493 time_t currtime = time(NULL);
1494 while (tmp) {
1495 struct transaction *trans = tmp->data;
1496 tmp = tmp->next;
1497 purple_debug_info("sipe", "have open transaction age: %d\n", currtime- trans->time);
1498 if ((currtime - trans->time > 5) && trans->retries >= 1) {
1499 /* TODO 408 */
1500 } else {
1501 if ((currtime - trans->time > 2) && trans->retries == 0) {
1502 trans->retries++;
1503 sendout_sipmsg(sip, trans->msg);
1507 return TRUE;
1510 static gboolean subscribe_timeout(struct sipe_account_data *sip)
1512 GSList *tmp;
1513 time_t curtime = time(NULL);
1514 /* register again if first registration expires */
1515 if (sip->reregister < curtime) {
1516 do_register(sip);
1518 /* check for every subscription if we need to resubscribe */
1519 //Fixxxer we need resub?
1520 g_hash_table_foreach(sip->buddies, (GHFunc)sipe_buddy_resub, (gpointer)sip);
1522 /* remove a timed out suscriber */
1524 tmp = sip->watcher;
1525 while (tmp) {
1526 struct sipe_watcher *watcher = tmp->data;
1527 if (watcher->expire < curtime) {
1528 watcher_remove(sip, watcher->name);
1529 tmp = sip->watcher;
1531 if (tmp) tmp = tmp->next;
1534 return TRUE;
1537 static void process_incoming_message(struct sipe_account_data *sip, struct sipmsg *msg)
1539 gchar *from;
1540 gchar *contenttype;
1541 gboolean found = FALSE;
1543 from = parse_from(sipmsg_find_header(msg, "From"));
1545 if (!from) return;
1547 purple_debug_info("sipe", "got message from %s: %s\n", from, msg->body);
1549 contenttype = sipmsg_find_header(msg, "Content-Type");
1550 if (!contenttype || !strncmp(contenttype, "text/plain", 10) || !strncmp(contenttype, "text/html", 9)) {
1551 serv_got_im(sip->gc, from, msg->body, 0, time(NULL));
1552 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1553 found = TRUE;
1555 if (!strncmp(contenttype, "application/im-iscomposing+xml", 30)) {
1556 xmlnode *isc = xmlnode_from_str(msg->body, msg->bodylen);
1557 xmlnode *state;
1558 gchar *statedata;
1560 if (!isc) {
1561 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1562 return;
1565 state = xmlnode_get_child(isc, "state");
1567 if (!state) {
1568 purple_debug_info("sipe", "process_incoming_message: no state found\n");
1569 xmlnode_free(isc);
1570 return;
1573 statedata = xmlnode_get_data(state);
1574 if (statedata) {
1575 if (strstr(statedata, "active")) serv_got_typing(sip->gc, from, 0, PURPLE_TYPING);
1576 else serv_got_typing_stopped(sip->gc, from);
1578 g_free(statedata);
1580 xmlnode_free(isc);
1581 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1582 found = TRUE;
1584 if (!found) {
1585 purple_debug_info("sipe", "got unknown mime-type");
1586 send_sip_response(sip->gc, msg, 415, "Unsupported media type", NULL);
1588 g_free(from);
1591 static void process_incoming_invite(struct sipe_account_data *sip, struct sipmsg *msg)
1593 gchar * from = sipmsg_find_part_of_header(sipmsg_find_header(msg, "From"), "<", ">", NULL);
1594 struct sip_im_session * session = find_or_create_im_session (sip, from);
1595 if (session) {
1596 struct sip_dialog * dialog = g_new0(struct sip_dialog, 1);
1597 dialog->callid = sipmsg_find_header(msg, "Call-ID");
1598 dialog->ourtag = find_tag(sipmsg_find_header(msg, "To"));
1599 dialog->theirtag = find_tag(sipmsg_find_header(msg, "From"));
1600 dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, "From"), "epid=", NULL, NULL);
1601 printf("Created incoming dialog and set epid to %s\n", dialog->theirepid);
1603 session->incoming_dialog = dialog;
1604 } else {
1605 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
1607 g_free(from);
1609 send_sip_response(sip->gc, msg, 200, "OK", g_strdup_printf(
1610 "v=0\r\n"
1611 "o=- 0 0 IN IP4 %s\r\n"
1612 "s=session\r\n"
1613 "c=IN IP4 %s\r\n"
1614 "t=0 0\r\n"
1615 "m=message %d sip sip:%s\r\n"
1616 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1617 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
1618 //sip->realport, sip->username
1619 5061, sip->username));
1622 gboolean process_register_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1624 gchar *tmp, krb5_token;
1625 const gchar *expires_header;
1626 int expires;
1628 expires_header = sipmsg_find_header(msg, "Expires");
1629 expires = expires_header != NULL ? strtol(expires_header, NULL, 10) : 0;
1630 purple_debug_info("sipe", "got response to REGISTER; expires = %d\n", expires);
1632 switch (msg->response) {
1633 case 200:
1634 if (expires == 0) {
1635 sip->registerstatus = 0;
1636 } else {
1637 sip->registerstatus = 3;
1638 purple_connection_set_state(sip->gc, PURPLE_CONNECTED);
1640 /* tell everybody we're online */
1641 send_publish (sip);
1643 /* get buddies from blist; Has a bug */
1644 /*sipe_get_buddies(sip->gc);*/
1645 subscribe_timeout(sip);
1647 //sipe_subscribe_to_name(sip, sip->username);
1649 tmp = sipmsg_find_header(msg, "Allow-Events");
1650 if (tmp && strstr(tmp, "vnd-microsoft-provisioning")){
1651 sipe_subscribe_buddylist(sip);
1654 // Should we remove the transaction here?
1655 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip->cseq);
1656 transactions_remove(sip, tc);
1658 break;
1659 case 401:
1660 if (sip->registerstatus != 2) {
1661 purple_debug_info("sipe", "REGISTER retries %d\n", sip->registrar.retries);
1662 if (sip->registrar.retries > 3) {
1663 sip->gc->wants_to_die = TRUE;
1664 purple_connection_error(sip->gc, _("Wrong Password"));
1665 return TRUE;
1667 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
1668 tmp = sipmsg_find_auth_header(msg, "NTLM");
1669 } else {
1670 tmp = sipmsg_find_auth_header(msg, "Kerberos");
1672 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_register_response - Auth header: %s\r\n", tmp);
1673 fill_auth(sip, tmp, &sip->registrar);
1674 sip->registerstatus = 2;
1675 if (sip->account->disconnecting) {
1676 do_register_exp(sip, 0);
1677 } else {
1678 do_register(sip);
1681 break;
1683 return TRUE;
1686 static void process_incoming_notify(struct sipe_account_data *sip, struct sipmsg *msg)
1688 gchar *from;
1689 gchar *fromhdr;
1690 gchar *tmp2;
1691 xmlnode *pidf;
1692 xmlnode *basicstatus = NULL, *tuple, *status;
1693 gboolean isonline = FALSE;
1695 fromhdr = sipmsg_find_header(msg, "From");
1696 from = parse_from(fromhdr);
1697 if (!from) return;
1699 pidf = xmlnode_from_str(msg->body, msg->bodylen);
1701 if (!pidf) {
1702 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
1703 return;
1706 purple_debug_info("sipe", "process_incoming_notify: body(%s)\n",msg->body);
1708 if ((tuple = xmlnode_get_child(pidf, "tuple")))
1709 if ((status = xmlnode_get_child(tuple, "status")))
1710 basicstatus = xmlnode_get_child(status, "basic");
1712 if (!basicstatus) {
1713 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
1714 xmlnode_free(pidf);
1715 return;
1718 tmp2 = xmlnode_get_data(basicstatus);
1720 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n",tmp2);
1723 if (!tmp2) {
1724 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
1725 xmlnode_free(pidf);
1726 return;
1729 if (strstr(tmp2, "open")) {
1730 isonline = TRUE;
1733 g_free(tmp2);
1735 if (isonline) purple_prpl_got_user_status(sip->account, from, "available", NULL);
1736 else purple_prpl_got_user_status(sip->account, from, "offline", NULL);
1738 xmlnode_free(pidf);
1740 g_free(from);
1741 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1745 static gchar* gen_xpidf(struct sipe_account_data *sip)
1747 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1748 "<presence>\r\n"
1749 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
1750 "<display name=\"sip:%s\"/>\r\n"
1751 "<atom id=\"1234\">\r\n"
1752 "<address uri=\"sip:%s\">\r\n"
1753 "<status status=\"%s\"/>\r\n"
1754 "</address>\r\n"
1755 "</atom>\r\n"
1756 "</presence>\r\n",
1757 sip->username,
1758 sip->username,
1759 sip->username,
1760 sip->status);
1761 return doc;
1766 static gchar* gen_pidf(struct sipe_account_data *sip)
1768 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1769 "<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"
1770 "<tuple id=\"0\">\r\n"
1771 "<status>\r\n"
1772 "<basic>open</basic>\r\n"
1773 "<ep:activities>\r\n"
1774 " <ep:activity>%s</ep:activity>\r\n"
1775 "</ep:activities>"
1776 "</status>\r\n"
1777 "</tuple>\r\n"
1778 "<ci:display-name>%s</ci:display-name>\r\n"
1779 "</presence>",
1780 sip->username,
1781 sip->status,
1782 sip->username);
1783 return doc;
1786 static void send_notify(struct sipe_account_data *sip, struct sipe_watcher *watcher)
1788 gchar *doc = watcher->needsxpidf ? gen_xpidf(sip) : gen_pidf(sip);
1789 gchar *hdr = watcher->needsxpidf ? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
1790 send_sip_request(sip->gc, "NOTIFY", watcher->name, watcher->name, hdr, doc, &watcher->dialog, NULL);
1791 g_free(doc);
1794 static gboolean process_service_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1796 if (msg->response != 200 && msg->response != 408) {
1797 /* never send again */
1798 sip->republish = -1;
1800 return TRUE;
1803 static void send_publish(struct sipe_account_data *sip)
1805 gchar *uri = g_strdup_printf("sip:%s", sip->username);
1806 gchar *doc = g_strdup_printf(
1807 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\"><publications uri=\"%s\"><publication categoryName=\"device\" instance=\"1617359818\" container=\"2\" version=\"0\" expireType=\"endpoint\"><device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"BB44F8D5-1540-547D-9ECE-6486D33DC804\"><capabilities preferred=\"false\" uri=\"%s\"><text capture=\"true\" render=\"true\" publish=\"false\"/><gifInk capture=\"false\" render=\"true\" publish=\"false\"/><isfInk capture=\"false\" render=\"true\" publish=\"false\"/></capabilities><timezone>%s</timezone><machineName>%s</machineName></device></publication><publication categoryName=\"state\" instance=\"906391356\" container=\"2\" version=\"0\" expireType=\"endpoint\"><state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\"><availability>3500</availability><endpointLocation></endpointLocation></state></publication><publication categoryName=\"state\" instance=\"906391356\" container=\"3\" version=\"0\" expireType=\"endpoint\"><state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\"><availability>3500</availability><endpointLocation></endpointLocation></state></publication></publications></publish>",
1808 uri, uri,
1809 "00:00:00-05:00", // TODO timezone
1810 "PC" // TODO machine name
1813 gchar *tmp = get_contact(sip);
1814 gchar *hdr = g_strdup_printf("Contact: %s; +sip.instance=\"<urn:uuid:%s>\"\r\nContent-Type: application/msrtc-category-publish+xml\r\n", tmp,generateUUIDfromEPID(getuuid()));
1815 g_free(tmp);
1817 send_sip_request(sip->gc, "SERVICE", uri, uri, hdr, doc, NULL, process_service_response);
1818 //sip->republish = time(NULL) + 500;
1820 g_free(hdr);
1821 g_free(uri);
1822 g_free(doc);
1825 static void send_service(struct sipe_account_data *sip)
1827 //gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->sipdomain);
1828 gchar *uri = g_strdup_printf("sip:%s", sip->username);
1829 //gchar *doc = gen_pidf(sip);
1831 gchar *doc = gen_pidf(sip);
1832 gchar *hdr = g_strdup("Event: presence\r\nContent-Type: application/pidf+xml\r\n");
1834 //gchar *hdr = g_strdup("Content-Type: application/SOAP+xml\r\n");
1835 gchar *tmp = get_contact(sip);
1836 hdr = g_strdup_printf("Contact: %s\r\n%s; +sip.instance=\"<urn:uuid:%s>\"", tmp, hdr,generateUUIDfromEPID(getuuid()));
1837 g_free(tmp);
1838 send_sip_request(sip->gc, "SERVICE", uri, uri,
1839 hdr,
1840 doc, NULL, process_service_response);
1841 sip->republish = time(NULL) + 500;
1842 g_free(hdr);
1843 g_free(uri);
1844 g_free(doc);
1847 static void process_incoming_subscribe(struct sipe_account_data *sip, struct sipmsg *msg)
1849 const char *from_hdr = sipmsg_find_header(msg, "From");
1850 gchar *from = parse_from(from_hdr);
1851 gchar *theirtag = find_tag(from_hdr);
1852 gchar *ourtag = find_tag(sipmsg_find_header(msg, "To"));
1853 gboolean tagadded = FALSE;
1854 gchar *callid = sipmsg_find_header(msg, "Call-ID");
1855 gchar *expire = sipmsg_find_header(msg, "Expire");
1856 // gchar *ms-received-port =find_received_port(sipmsg_find_header(msg, "Contact"));
1857 gchar *tmp;
1858 struct sipe_watcher *watcher = watcher_find(sip, from);
1859 if (!ourtag) {
1860 tagadded = TRUE;
1861 ourtag = gentag();
1863 if (!watcher) { /* new subscription */
1864 gchar *acceptheader = sipmsg_find_header(msg, "Accept");
1865 gboolean needsxpidf = FALSE;
1866 if (!purple_privacy_check(sip->account, from)) {
1867 send_sip_response(sip->gc, msg, 202, "Ok", NULL);
1868 goto privend;
1870 if (acceptheader) {
1871 gchar *tmp = acceptheader;
1872 gboolean foundpidf = FALSE;
1873 gboolean foundxpidf = FALSE;
1874 while (tmp && tmp < acceptheader + strlen(acceptheader)) {
1875 gchar *tmp2 = strchr(tmp, ',');
1876 if (tmp2) *tmp2 = '\0';
1877 if (!strcmp("application/pidf+xml", tmp))
1878 foundpidf = TRUE;
1879 if (!strcmp("application/xpidf+xml", tmp))
1880 foundxpidf = TRUE;
1881 if (tmp2) {
1882 *tmp2 = ',';
1883 tmp = tmp2;
1884 while (*tmp == ' ') tmp++;
1885 } else
1886 tmp = 0;
1888 if (!foundpidf && foundxpidf) needsxpidf = TRUE;
1889 g_free(acceptheader);
1891 watcher = watcher_create(sip, from, callid, ourtag, theirtag, needsxpidf);
1893 if (tagadded) {
1894 gchar *to = g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg, "To"), ourtag);
1895 sipmsg_remove_header(msg, "To");
1896 sipmsg_add_header(msg, "To", to);
1897 g_free(to);
1899 if (expire)
1900 watcher->expire = time(NULL) + strtol(expire, NULL, 10);
1901 else
1902 watcher->expire = time(NULL) + 600;
1903 //Fixxxer
1904 sipmsg_remove_header(msg, "Contact");
1905 tmp = get_contact(sip);
1906 sipmsg_add_header(msg, "Contact", tmp);
1907 g_free(tmp);
1908 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);
1909 send_sip_response(sip->gc, msg, 200, "Ok", NULL);
1910 send_notify(sip, watcher);
1911 privend:
1912 g_free(from);
1913 g_free(theirtag);
1914 g_free(ourtag);
1915 g_free(callid);
1916 g_free(expire);
1919 static void process_input_message(struct sipe_account_data *sip, struct sipmsg *msg)
1921 gboolean found = FALSE;
1922 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg->response,msg->method);
1923 if (msg->response == 0) { /* request */
1924 if (!strcmp(msg->method, "MESSAGE")) {
1925 process_incoming_message(sip, msg);
1926 found = TRUE;
1927 } else if (!strcmp(msg->method, "NOTIFY")) {
1928 purple_debug_info("sipe","send->process_incoming_notify\n");
1929 process_incoming_notify(sip, msg);
1930 found = TRUE;
1931 } else if (!strcmp(msg->method, "SUBSCRIBE")) {
1932 purple_debug_info("sipe","send->process_incoming_subscribe\n");
1933 process_incoming_subscribe(sip, msg);
1934 found = TRUE;
1935 } else if (!strcmp(msg->method, "INVITE")) {
1936 process_incoming_invite(sip, msg);
1937 found = TRUE;
1938 } else if (!strcmp(msg->method, "INFO")) {
1939 // TODO needs work
1940 gchar * from = parse_from(sipmsg_find_header(msg, "From"));
1941 if (from) {
1942 serv_got_typing(sip->gc, from, 0, PURPLE_TYPING);
1944 printf("INFO body:\n%s\n", msg->body);
1945 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1946 found = TRUE;
1947 } else if (!strcmp(msg->method, "ACK")) {
1948 // ACK's don't need any response
1949 //send_sip_response(sip->gc, msg, 200, "OK", NULL);
1950 found = TRUE;
1951 } else if (!strcmp(msg->method, "BYE")) {
1952 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1954 gchar * from = parse_from(sipmsg_find_header(msg, "From"));
1955 struct sip_im_session * session = find_im_session (sip, from);
1956 g_free(from);
1958 if (session) {
1959 // TODO Let the user know the other user left the conversation?
1960 im_session_destroy(sip, session);
1963 found = TRUE;
1964 } else {
1965 send_sip_response(sip->gc, msg, 501, "Not implemented", NULL);
1967 } else { /* response */
1968 struct transaction *trans = transactions_find(sip, msg);
1969 if (trans) {
1970 if (msg->response == 407) {
1971 gchar *resend, *auth, *ptmp;
1973 if (sip->proxy.retries > 30) return;
1974 sip->proxy.retries++;
1975 /* do proxy authentication */
1977 ptmp = sipmsg_find_header(msg, "Proxy-Authenticate");
1979 fill_auth(sip, ptmp, &sip->proxy);
1980 auth = auth_header(sip, &sip->proxy, trans->msg, TRUE);
1981 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
1982 sipmsg_add_header_pos(trans->msg, "Proxy-Authorization", auth, 5);
1983 g_free(auth);
1984 resend = sipmsg_to_string(trans->msg);
1985 /* resend request */
1986 sendout_pkt(sip->gc, resend);
1987 g_free(resend);
1988 } else {
1989 if (msg->response == 100) {
1990 /* ignore provisional response */
1991 purple_debug_info("sipe", "got trying response\n");
1992 } else {
1993 sip->proxy.retries = 0;
1994 if (!strcmp(trans->msg->method, "REGISTER")) {
1995 if (msg->response == 401) sip->registrar.retries++;
1996 else sip->registrar.retries = 0;
1997 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip->cseq);
1998 } else {
1999 if (msg->response == 401) {
2000 gchar *resend, *auth, *ptmp;
2002 if (sip->registrar.retries > 4) return;
2003 sip->registrar.retries++;
2005 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
2006 ptmp = sipmsg_find_auth_header(msg, "NTLM");
2007 } else {
2008 ptmp = sipmsg_find_auth_header(msg, "Kerberos");
2011 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - Auth header: %s\r\n", ptmp);
2013 fill_auth(sip, ptmp, &sip->registrar);
2014 auth = auth_header(sip, &sip->registrar, trans->msg, TRUE);
2015 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
2016 sipmsg_add_header(trans->msg, "Proxy-Authorization", auth);
2018 //sipmsg_remove_header(trans->msg, "Authorization");
2019 //sipmsg_add_header(trans->msg, "Authorization", auth);
2020 g_free(auth);
2021 resend = sipmsg_to_string(trans->msg);
2022 /* resend request */
2023 sendout_pkt(sip->gc, resend);
2024 g_free(resend);
2027 if (trans->callback) {
2028 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - we have a transaction callback\r\n");
2029 /* call the callback to process response*/
2030 (trans->callback)(sip, msg, trans);
2032 /* Not sure if this is needed or what needs to be done
2033 but transactions seem to be removed prematurely so
2034 this only removes them if the response is 200 OK */
2035 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - removing CSeq %d\r\n", sip->cseq);
2036 /*Has a bug and it's unneccesary*/
2037 /*transactions_remove(sip, trans);*/
2041 found = TRUE;
2042 /* This is done because in an OCS2007 server trace the MS
2043 * Communicator client seems to reset the CSeq after an OK */
2045 //sip->cseq=1;
2046 } else {
2047 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received response to unknown transaction");
2050 if (!found) {
2051 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received a unknown sip message with method %s and response %d\n", msg->method, msg->response);
2055 static void process_input(struct sipe_account_data *sip, struct sip_connection *conn)
2057 char *cur;
2058 char *dummy;
2059 struct sipmsg *msg;
2060 int restlen;
2061 cur = conn->inbuf;
2063 /* according to the RFC remove CRLF at the beginning */
2064 while (*cur == '\r' || *cur == '\n') {
2065 cur++;
2067 if (cur != conn->inbuf) {
2068 memmove(conn->inbuf, cur, conn->inbufused - (cur - conn->inbuf));
2069 conn->inbufused = strlen(conn->inbuf);
2072 /* Received a full Header? */
2073 if ((cur = strstr(conn->inbuf, "\r\n\r\n")) != NULL) {
2074 time_t currtime = time(NULL);
2075 cur += 2;
2076 cur[0] = '\0';
2077 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), conn->inbuf);
2078 msg = sipmsg_parse_header(conn->inbuf);
2079 cur[0] = '\r';
2080 cur += 2;
2081 restlen = conn->inbufused - (cur - conn->inbuf);
2082 if (restlen >= msg->bodylen) {
2083 dummy = g_malloc(msg->bodylen + 1);
2084 memcpy(dummy, cur, msg->bodylen);
2085 dummy[msg->bodylen] = '\0';
2086 msg->body = dummy;
2087 cur += msg->bodylen;
2088 memmove(conn->inbuf, cur, conn->inbuflen - (cur - conn->inbuf));
2089 conn->inbufused = strlen(conn->inbuf);
2090 } else {
2091 sipmsg_free(msg);
2092 return;
2095 // Verify the signature before processing it
2096 if (sip->registrar.ntlm_key) {
2097 struct sipmsg_breakdown msgbd;
2098 msgbd.msg = msg;
2099 sipmsg_breakdown_parse(&msgbd, sip->registrar.realm, sip->registrar.target);
2100 gchar * signature_input_str = sipmsg_breakdown_get_string(&msgbd);
2101 gchar * signature;
2102 if (signature_input_str != NULL) {
2103 signature = purple_ntlm_sipe_signature_make (signature_input_str, sip->registrar.ntlm_key);
2106 gchar * rspauth = sipmsg_find_part_of_header(sipmsg_find_header(msg, "Authentication-Info"), "rspauth=\"", "\"", NULL);
2107 if (signature != NULL && rspauth != NULL) {
2108 if (purple_ntlm_verify_signature (signature, rspauth)) {
2109 purple_debug(PURPLE_DEBUG_MISC, "sipe", "incoming message's signature validated\n");
2110 process_input_message(sip, msg);
2111 } else {
2112 purple_debug(PURPLE_DEBUG_MISC, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth, signature);
2116 sipmsg_breakdown_free(&msgbd);
2117 } else {
2118 process_input_message(sip, msg);
2121 } else {
2122 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received a incomplete sip msg: %s\n", conn->inbuf);
2126 static void sipe_udp_process(gpointer data, gint source, PurpleInputCondition con)
2128 PurpleConnection *gc = data;
2129 struct sipe_account_data *sip = gc->proto_data;
2130 struct sipmsg *msg;
2131 int len;
2132 time_t currtime;
2134 static char buffer[65536];
2135 if ((len = recv(source, buffer, sizeof(buffer) - 1, 0)) > 0) {
2136 buffer[len] = '\0';
2137 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), buffer);
2138 msg = sipmsg_parse_msg(buffer);
2139 if (msg) process_input_message(sip, msg);
2143 static void sipe_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
2145 PurpleConnection *gc = data;
2146 struct sipe_account_data *sip = gc->proto_data;
2147 struct sip_connection *conn = NULL;
2148 int len;
2149 static char buf[4096];
2151 /* TODO: It should be possible to make this check unnecessary */
2152 if (!PURPLE_CONNECTION_IS_VALID(gc)) {
2153 if (gsc) purple_ssl_close(gsc);
2154 return;
2157 if (sip->gsc)
2158 conn = connection_find(sip, sip->gsc->fd);
2159 if (!conn) {
2160 purple_debug_error("sipe", "Connection not found!\n");
2161 if (sip->gsc) purple_ssl_close(sip->gsc);
2162 return;
2166 if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
2167 conn->inbuflen += SIMPLE_BUF_INC;
2168 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
2171 len = purple_ssl_read(gsc, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1);
2173 if (len < 0 && errno == EAGAIN) {
2174 /* Try again later */
2175 return;
2176 } else if (len < 0) {
2177 purple_debug_info("sipe", "sipe_input_cb_ssl: read error\n");
2178 if (sip->gsc){
2179 connection_remove(sip, sip->gsc->fd);
2180 if (sip->fd == gsc->fd) sip->fd = -1;
2182 return;
2183 } else if (len == 0) {
2184 purple_connection_error(gc, _("Server has disconnected"));
2185 if (sip->gsc){
2186 connection_remove(sip, sip->gsc->fd);
2187 if (sip->fd == gsc->fd) sip->fd = -1;
2189 return;
2192 conn->inbufused += len;
2193 conn->inbuf[conn->inbufused] = '\0';
2195 process_input(sip, conn);
2199 static void sipe_input_cb(gpointer data, gint source, PurpleInputCondition cond)
2201 PurpleConnection *gc = data;
2202 struct sipe_account_data *sip = gc->proto_data;
2203 int len;
2204 struct sip_connection *conn = connection_find(sip, source);
2205 if (!conn) {
2206 purple_debug_error("sipe", "Connection not found!\n");
2207 return;
2210 if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
2211 conn->inbuflen += SIMPLE_BUF_INC;
2212 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
2215 len = read(source, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1);
2217 if (len < 0 && errno == EAGAIN)
2218 return;
2219 else if (len <= 0) {
2220 purple_debug_info("sipe", "sipe_input_cb: read error\n");
2221 connection_remove(sip, source);
2222 if (sip->fd == source) sip->fd = -1;
2223 return;
2226 conn->inbufused += len;
2227 conn->inbuf[conn->inbufused] = '\0';
2229 process_input(sip, conn);
2232 /* Callback for new connections on incoming TCP port */
2233 static void sipe_newconn_cb(gpointer data, gint source, PurpleInputCondition cond)
2235 PurpleConnection *gc = data;
2236 struct sipe_account_data *sip = gc->proto_data;
2237 struct sip_connection *conn;
2239 int newfd = accept(source, NULL, NULL);
2241 conn = connection_create(sip, newfd);
2243 conn->inputhandler = purple_input_add(newfd, PURPLE_INPUT_READ, sipe_input_cb, gc);
2246 static void login_cb(gpointer data, gint source, const gchar *error_message)
2248 PurpleConnection *gc = data;
2249 struct sipe_account_data *sip;
2250 struct sip_connection *conn;
2252 if (!PURPLE_CONNECTION_IS_VALID(gc))
2254 if (source >= 0)
2255 close(source);
2256 return;
2259 if (source < 0) {
2260 purple_connection_error(gc, _("Could not connect"));
2261 return;
2264 sip = gc->proto_data;
2265 sip->fd = source;
2267 conn = connection_create(sip, source);
2269 sip->registertimeout = purple_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
2271 do_register(sip);
2273 conn->inputhandler = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_input_cb, gc);
2276 static void login_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
2278 PurpleConnection *gc = data;
2279 struct sipe_account_data *sip;
2280 struct sip_connection *conn;
2282 if (!PURPLE_CONNECTION_IS_VALID(gc))
2284 if (gsc) purple_ssl_close(gsc);
2285 return;
2288 sip = gc->proto_data;
2289 sip->fd = gsc->fd;
2290 conn = connection_create(sip, sip->fd);
2291 sip->listenport = purple_network_get_port_from_fd(sip->fd);
2292 sip->listenfd = sip->fd;
2293 sip->registertimeout = purple_timeout_add((rand()%100)+3*1000, (GSourceFunc)subscribe_timeout, sip);
2295 do_register(sip);
2297 purple_ssl_input_add(gsc, sipe_input_cb_ssl, gc);
2300 static guint sipe_ht_hash_nick(const char *nick)
2302 char *lc = g_utf8_strdown(nick, -1);
2303 guint bucket = g_str_hash(lc);
2304 g_free(lc);
2306 return bucket;
2309 static gboolean sipe_ht_equals_nick(const char *nick1, const char *nick2)
2311 return (purple_utf8_strcasecmp(nick1, nick2) == 0);
2314 static void sipe_udp_host_resolved_listen_cb(int listenfd, gpointer data)
2316 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2318 sip->listen_data = NULL;
2320 if (listenfd == -1) {
2321 purple_connection_error(sip->gc, _("Could not create listen socket"));
2322 return;
2325 sip->fd = listenfd;
2327 sip->listenport = purple_network_get_port_from_fd(sip->fd);
2328 sip->listenfd = sip->fd;
2330 sip->listenpa = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_udp_process, sip->gc);
2332 sip->resendtimeout = purple_timeout_add(2500, (GSourceFunc) resend_timeout, sip);
2333 sip->registertimeout = purple_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
2334 do_register(sip);
2337 static void sipe_udp_host_resolved(GSList *hosts, gpointer data, const char *error_message)
2339 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2340 int addr_size;
2342 sip->query_data = NULL;
2344 if (!hosts || !hosts->data) {
2345 purple_connection_error(sip->gc, _("Couldn't resolve host"));
2346 return;
2349 addr_size = GPOINTER_TO_INT(hosts->data);
2350 hosts = g_slist_remove(hosts, hosts->data);
2351 memcpy(&(sip->serveraddr), hosts->data, addr_size);
2352 g_free(hosts->data);
2353 hosts = g_slist_remove(hosts, hosts->data);
2354 while (hosts) {
2355 hosts = g_slist_remove(hosts, hosts->data);
2356 g_free(hosts->data);
2357 hosts = g_slist_remove(hosts, hosts->data);
2360 /* create socket for incoming connections */
2361 sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_DGRAM,
2362 sipe_udp_host_resolved_listen_cb, sip);
2363 if (sip->listen_data == NULL) {
2364 purple_connection_error(sip->gc, _("Could not create listen socket"));
2365 return;
2369 static void sipe_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
2370 gpointer data)
2372 PurpleConnection *gc = data;
2373 struct sipe_account_data *sip;
2375 /* If the connection is already disconnected, we don't need to do anything else */
2376 if (!PURPLE_CONNECTION_IS_VALID(gc))
2377 return;
2379 sip = gc->proto_data;
2380 sip->gsc = NULL;
2382 switch(error) {
2383 case PURPLE_SSL_CONNECT_FAILED:
2384 purple_connection_error(gc, _("Connection Failed"));
2385 break;
2386 case PURPLE_SSL_HANDSHAKE_FAILED:
2387 purple_connection_error(gc, _("SSL Handshake Failed"));
2388 break;
2392 static void
2393 sipe_tcp_connect_listen_cb(int listenfd, gpointer data)
2395 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2396 PurpleProxyConnectData *connect_data;
2398 sip->listen_data = NULL;
2400 sip->listenfd = listenfd;
2401 if (sip->listenfd == -1) {
2402 purple_connection_error(sip->gc, _("Could not create listen socket"));
2403 return;
2406 purple_debug_info("sipe", "listenfd: %d\n", sip->listenfd);
2407 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2408 sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2409 sip->listenpa = purple_input_add(sip->listenfd, PURPLE_INPUT_READ,
2410 sipe_newconn_cb, sip->gc);
2411 purple_debug_info("sipe", "connecting to %s port %d\n",
2412 sip->realhostname, sip->realport);
2413 /* open tcp connection to the server */
2414 connect_data = purple_proxy_connect(sip->gc, sip->account, sip->realhostname,
2415 sip->realport, login_cb, sip->gc);
2417 if (connect_data == NULL) {
2418 purple_connection_error(sip->gc, _("Couldn't create socket"));
2424 static void srvresolved(PurpleSrvResponse *resp, int results, gpointer data)
2426 struct sipe_account_data *sip;
2427 gchar *hostname;
2428 int port;
2430 sip = data;
2431 sip->srv_query_data = NULL;
2433 port = purple_account_get_int(sip->account, "port", 0);
2435 /* find the host to connect to */
2436 if (results) {
2437 hostname = g_strdup(resp->hostname);
2438 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - SRV hostname: %s\r\n", hostname);
2439 if (!port)
2440 port = resp->port;
2441 g_free(resp);
2442 } else {
2443 if (!purple_account_get_bool(sip->account, "useproxy", FALSE)) {
2444 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - using sipdomain\r\n");
2445 hostname = g_strdup(sip->sipdomain);
2446 } else {
2447 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - using specified SIP proxy\r\n");
2448 hostname = g_strdup(purple_account_get_string(sip->account, "proxy", sip->sipdomain));
2452 sip->realhostname = hostname;
2453 sip->realport = port;
2454 if (!sip->realport) sip->realport = 5060;
2456 /* TCP case */
2457 //if (!sip->udp) {
2458 // /* create socket for incoming connections */
2459 // sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_STREAM,
2460 // sipe_tcp_connect_listen_cb, sip);
2461 // if (sip->listen_data == NULL) {
2462 // purple_connection_error(sip->gc, _("Could not create listen socket"));
2463 // return;
2464 // }
2465 //} else { /* UDP */
2466 // purple_debug_info("sipe", "using udp with server %s and port %d\n", hostname, port);
2468 // sip->query_data = purple_dnsquery_a(hostname, port, sipe_udp_host_resolved, sip);
2469 // if (sip->query_data == NULL) {
2470 // purple_connection_error(sip->gc, _("Could not resolve hostname"));
2471 // }
2475 static void sipe_login(PurpleAccount *account)
2477 PurpleConnection *gc;
2478 struct sipe_account_data *sip;
2479 gchar **userserver;
2480 gchar *hosttoconnect;
2482 const char *username = purple_account_get_username(account);
2483 gc = purple_account_get_connection(account);
2485 if (strpbrk(username, " \t\v\r\n") != NULL) {
2486 gc->wants_to_die = TRUE;
2487 purple_connection_error(gc, _("SIP Exchange usernames may not contain whitespaces"));
2488 return;
2491 if (!purple_account_get_bool(account, "ssl", FALSE)){
2492 if (!purple_ssl_is_supported())
2494 gc->wants_to_die = TRUE;
2495 purple_connection_error(gc,
2496 _("SSL support is needed for SSL/TLS support. Please install a supported "
2497 "SSL library."));
2498 return;
2503 gc->proto_data = sip = g_new0(struct sipe_account_data, 1);
2504 sip->gc = gc;
2505 sip->account = account;
2506 sip->registerexpire = 900;
2507 sip->udp = purple_account_get_bool(account, "udp", FALSE);
2508 sip->use_ssl = purple_account_get_bool(account, "ssl", FALSE);
2510 purple_debug_info("sipe", "sip->use_ssl->%d\n", sip->use_ssl);
2512 /* TODO: is there a good default grow size? */
2513 if (!sip->udp)
2514 sip->txbuf = purple_circ_buffer_new(0);
2516 userserver = g_strsplit(username, "@", 2);
2517 purple_connection_set_display_name(gc, userserver[0]);
2518 sip->username = g_strdup(g_strjoin("@", userserver[0], userserver[1], NULL));
2519 sip->sipdomain = g_strdup(userserver[1]);
2520 sip->password = g_strdup(purple_connection_get_password(gc));
2521 g_strfreev(userserver);
2523 if (sip->use_ssl) {
2524 // Communicator queries _sipinternaltls._tcp.domain.com and uses that
2525 // information to connect to the OCS server.
2527 // XXX FIXME: eventually we should also query for sipexternaltls as well
2528 // if Pidgin is not on the local LAN
2529 // This doesn't quite work as advertised yet so make sure your have
2530 // your OCS FQDN in the proxy setting in the SIPE account settings
2532 sip->srv_query_data = purple_srv_resolve("sipinternaltls", "tcp", sip->sipdomain, srvresolved, sip);
2535 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_login - realhostname: %s\r\n", sip->realhostname);
2537 sip->buddies = g_hash_table_new((GHashFunc)sipe_ht_hash_nick, (GEqualFunc)sipe_ht_equals_nick);
2539 purple_connection_update_progress(gc, _("Connecting"), 1, 2);
2541 /* TODO: Set the status correctly. */
2542 sip->status = g_strdup("available");
2544 if (!purple_account_get_bool(account, "useproxy", FALSE)) {
2545 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_login - checking realhostname again: %s\r\n", sip->realhostname);
2546 hosttoconnect = g_strdup(sip->sipdomain);
2547 } else {
2548 hosttoconnect = g_strdup(purple_account_get_string(account, "proxy", sip->sipdomain));
2551 /*SSL*/
2552 purple_debug_info("sipe", "HosttoConnect->%s\n", hosttoconnect);
2554 if (sip->use_ssl){
2555 sip->gsc = purple_ssl_connect(account,hosttoconnect, purple_account_get_int(account, "port", 5061), login_cb_ssl, sipe_ssl_connect_failure, gc);
2557 else{
2558 sip->srv_query_data = purple_srv_resolve("sip",
2559 sip->udp ? "udp" : "tcp", hosttoconnect, srvresolved, sip);
2562 g_free(hosttoconnect);
2565 static void sipe_close(PurpleConnection *gc)
2567 struct sipe_account_data *sip = gc->proto_data;
2569 if (sip) {
2570 /* leave all conversations */
2571 im_session_close_all(sip);
2573 /* unregister */
2574 do_register_exp(sip, 0);
2575 connection_free_all(sip);
2577 if (sip->query_data != NULL)
2578 purple_dnsquery_destroy(sip->query_data);
2580 if (sip->srv_query_data != NULL)
2581 purple_srv_cancel(sip->srv_query_data);
2583 if (sip->listen_data != NULL)
2584 purple_network_listen_cancel(sip->listen_data);
2586 g_free(sip->sipdomain);
2587 g_free(sip->username);
2588 g_free(sip->password);
2589 g_free(sip->registrar.nonce);
2590 g_free(sip->registrar.opaque);
2591 g_free(sip->registrar.target);
2592 g_free(sip->registrar.realm);
2593 g_free(sip->registrar.digest_session_key);
2594 g_free(sip->proxy.nonce);
2595 g_free(sip->proxy.opaque);
2596 g_free(sip->proxy.target);
2597 g_free(sip->proxy.realm);
2598 g_free(sip->proxy.digest_session_key);
2599 if (sip->txbuf)
2600 purple_circ_buffer_destroy(sip->txbuf);
2601 g_free(sip->realhostname);
2602 if (sip->listenpa) purple_input_remove(sip->listenpa);
2603 if (sip->tx_handler) purple_input_remove(sip->tx_handler);
2604 if (sip->resendtimeout) purple_timeout_remove(sip->resendtimeout);
2605 if (sip->registertimeout) purple_timeout_remove(sip->registertimeout);
2607 g_free(gc->proto_data);
2608 gc->proto_data = NULL;
2611 /* not needed since privacy is checked for every subscribe */
2612 static void dummy_add_deny(PurpleConnection *gc, const char *name) {
2615 static void dummy_permit_deny(PurpleConnection *gc)
2619 static gboolean sipe_plugin_load(PurplePlugin *plugin)
2621 return TRUE;
2625 static gboolean sipe_plugin_unload(PurplePlugin *plugin)
2627 return TRUE;
2631 static void sipe_plugin_destroy(PurplePlugin *plugin)
2635 static PurplePlugin *my_protocol = NULL;
2637 static PurplePluginProtocolInfo prpl_info =
2640 NULL, /* user_splits */
2641 NULL, /* protocol_options */
2642 NO_BUDDY_ICONS, /* icon_spec */
2643 sipe_list_icon, /* list_icon */
2644 NULL, /* list_emblems */
2645 NULL, /* status_text */
2646 NULL, /* tooltip_text */
2647 sipe_status_types, /* away_states */
2648 NULL, /* blist_node_menu */
2649 NULL, /* chat_info */
2650 NULL, /* chat_info_defaults */
2651 sipe_login, /* login */
2652 sipe_close, /* close */
2653 sipe_im_send, /* send_im */
2654 NULL, /* set_info */
2655 // sipe_typing, /* send_typing */
2656 NULL, /* send_typing */
2657 NULL, /* get_info */
2658 sipe_set_status, /* set_status */
2659 NULL, /* set_idle */
2660 NULL, /* change_passwd */
2661 sipe_add_buddy, /* add_buddy */
2662 NULL, /* add_buddies */
2663 sipe_remove_buddy, /* remove_buddy */
2664 NULL, /* remove_buddies */
2665 dummy_add_deny, /* add_permit */
2666 dummy_add_deny, /* add_deny */
2667 dummy_add_deny, /* rem_permit */
2668 dummy_add_deny, /* rem_deny */
2669 dummy_permit_deny, /* set_permit_deny */
2670 NULL, /* join_chat */
2671 NULL, /* reject_chat */
2672 NULL, /* get_chat_name */
2673 NULL, /* chat_invite */
2674 NULL, /* chat_leave */
2675 NULL, /* chat_whisper */
2676 NULL, /* chat_send */
2677 sipe_keep_alive, /* keepalive */
2678 NULL, /* register_user */
2679 NULL, /* get_cb_info */
2680 NULL, /* get_cb_away */
2681 NULL, /* alias_buddy */
2682 NULL, /* group_buddy */
2683 NULL, /* rename_group */
2684 NULL, /* buddy_free */
2685 sipe_convo_closed, /* convo_closed */
2686 purple_normalize_nocase, /* normalize */
2687 NULL, /* set_buddy_icon */
2688 NULL, /* remove_group */
2689 NULL, /* get_cb_real_name */
2690 NULL, /* set_chat_topic */
2691 NULL, /* find_blist_chat */
2692 NULL, /* roomlist_get_list */
2693 NULL, /* roomlist_cancel */
2694 NULL, /* roomlist_expand_category */
2695 NULL, /* can_receive_file */
2696 NULL, /* send_file */
2697 NULL, /* new_xfer */
2698 NULL, /* offline_message */
2699 NULL, /* whiteboard_prpl_ops */
2700 sipe_send_raw, /* send_raw */
2704 static PurplePluginInfo info = {
2705 PURPLE_PLUGIN_MAGIC,
2706 PURPLE_MAJOR_VERSION,
2707 PURPLE_MINOR_VERSION,
2708 PURPLE_PLUGIN_PROTOCOL, /**< type */
2709 NULL, /**< ui_requirement */
2710 0, /**< flags */
2711 NULL, /**< dependencies */
2712 PURPLE_PRIORITY_DEFAULT, /**< priority */
2713 "prpl-sipe", /**< id */
2714 "Microsoft LCS/OCS", /**< name */
2715 VERSION, /**< version */
2716 N_("SIP/SIMPLE Exchange Protocol Plugin"), /** summary */
2717 N_("The SIP/SIMPLE Exchange Protocol Plugin"), /** description */
2718 "Anibal Avelar <avelar@gmail.com>", /**< author */
2719 PURPLE_WEBSITE, /**< homepage */
2720 sipe_plugin_load, /**< load */
2721 sipe_plugin_unload, /**< unload */
2722 sipe_plugin_destroy, /**< destroy */
2723 NULL, /**< ui_info */
2724 &prpl_info, /**< extra_info */
2725 NULL,
2726 NULL,
2727 NULL,
2728 NULL,
2729 NULL,
2730 NULL
2733 static void init_plugin(PurplePlugin *plugin)
2735 PurpleAccountUserSplit *split;
2736 PurpleAccountOption *option;
2738 purple_plugin_register(plugin);
2740 //split = purple_account_user_split_new(_("Server"), "", '@');
2741 //prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
2742 option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2743 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2744 option = purple_account_option_string_new(_("Proxy Server"), "proxy", "");
2745 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2747 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
2748 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2750 option = purple_account_option_bool_new(_("Use SSL/TLS"), "ssl", FALSE);
2751 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,option);
2753 option = purple_account_option_string_new(_("UserAgent"), "useragent", "Purple/" VERSION);
2754 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,option);
2756 option = purple_account_option_bool_new(_("Use UDP"), "udp", FALSE);
2757 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2759 option = purple_account_option_int_new(_("Connect port"), "port", 5060);
2760 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2762 option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
2763 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2765 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
2766 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
2767 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2769 /*option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2770 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2771 option = purple_account_option_string_new(_("Proxy"), "proxy", "");
2772 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2773 option = purple_account_option_string_new(_("Auth User"), "authuser", "");
2774 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2775 option = purple_account_option_string_new(_("Auth Domain"), "authdomain", "");
2776 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2777 my_protocol = plugin;
2781 /* I had to redefined the function for it load, but works */
2782 gboolean purple_init_plugin(PurplePlugin *plugin){
2783 plugin->info = &(info);
2784 init_plugin((plugin));
2785 sipe_plugin_load((plugin));
2786 return purple_plugin_register(plugin);