Added UUID-Mac-Address support, and other details for presence
[siplcs.git] / src / sipe.c
blob8ee30c1876ef9783bd5d24b2e081aa1bab0d3c79
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
74 return get_macaddr();
77 static char *genbranch()
79 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
80 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
81 rand() & 0xFFFF, rand() & 0xFFFF);
84 static char *gencallid()
86 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
87 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
88 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
89 rand() & 0xFFFF, rand() & 0xFFFF);
92 static gchar *find_tag(const gchar *hdr)
94 gchar * tag = sipmsg_find_part_of_header (hdr, "tag=", ";", NULL);
95 if (!tag) {
96 // In case it's at the end and there's no trailing ;
97 tag = sipmsg_find_part_of_header (hdr, "tag=", NULL, NULL);
99 return tag;
103 static const char *sipe_list_icon(PurpleAccount *a, PurpleBuddy *b)
105 return "sipe";
108 static void sipe_keep_alive(PurpleConnection *gc)
110 struct sipe_account_data *sip = gc->proto_data;
111 if (sip->udp) {
112 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
113 gchar buf[2] = {0, 0};
114 purple_debug_info("sipe", "sending keep alive\n");
115 sendto(sip->fd, buf, 1, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in));
119 static gboolean process_register_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc);
121 static void sipe_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
122 static void sipe_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
123 gpointer data);
125 static void send_notify(struct sipe_account_data *sip, struct sipe_watcher *);
127 static void send_service(struct sipe_account_data *sip);
128 static void sipe_subscribe_to_name(struct sipe_account_data *sip, const char * buddy_name);
129 static void send_publish(struct sipe_account_data *sip);
131 static void do_notifies(struct sipe_account_data *sip)
133 GSList *tmp = sip->watcher;
134 purple_debug_info("sipe", "do_notifies()\n");
136 while (tmp) {
137 purple_debug_info("sipe", "notifying %s\n", ((struct sipe_watcher*)tmp->data)->name);
138 send_notify(sip, tmp->data);
139 tmp = tmp->next;
143 static void sipe_set_status(PurpleAccount *account, PurpleStatus *status)
145 PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_type(status));
146 struct sipe_account_data *sip = NULL;
148 if (!purple_status_is_active(status))
149 return;
151 if (account->gc)
152 sip = account->gc->proto_data;
154 if (sip)
156 g_free(sip->status);
157 if (primitive == PURPLE_STATUS_AVAILABLE)
158 sip->status = g_strdup("available");
159 else
160 sip->status = g_strdup("busy");
162 do_notifies(sip);
166 static struct sip_connection *connection_find(struct sipe_account_data *sip, int fd)
168 struct sip_connection *ret = NULL;
169 GSList *entry = sip->openconns;
170 while (entry) {
171 ret = entry->data;
172 if (ret->fd == fd) return ret;
173 entry = entry->next;
175 return NULL;
178 static struct sipe_watcher *watcher_find(struct sipe_account_data *sip,
179 const gchar *name)
181 struct sipe_watcher *watcher;
182 GSList *entry = sip->watcher;
183 while (entry) {
184 watcher = entry->data;
185 if (!strcmp(name, watcher->name)) return watcher;
186 entry = entry->next;
188 return NULL;
191 static struct sipe_watcher *watcher_create(struct sipe_account_data *sip,
192 const gchar *name, const gchar *callid, const gchar *ourtag,
193 const gchar *theirtag, gboolean needsxpidf)
195 struct sipe_watcher *watcher = g_new0(struct sipe_watcher, 1);
196 watcher->name = g_strdup(name);
197 watcher->dialog.callid = g_strdup(callid);
198 watcher->dialog.ourtag = g_strdup(ourtag);
199 watcher->dialog.theirtag = g_strdup(theirtag);
200 watcher->needsxpidf = needsxpidf;
201 sip->watcher = g_slist_append(sip->watcher, watcher);
202 return watcher;
205 static void watcher_remove(struct sipe_account_data *sip, const gchar *name)
207 struct sipe_watcher *watcher = watcher_find(sip, name);
208 sip->watcher = g_slist_remove(sip->watcher, watcher);
209 g_free(watcher->name);
210 g_free(watcher->dialog.callid);
211 g_free(watcher->dialog.ourtag);
212 g_free(watcher->dialog.theirtag);
213 g_free(watcher);
216 static struct sip_connection *connection_create(struct sipe_account_data *sip, int fd)
218 struct sip_connection *ret = g_new0(struct sip_connection, 1);
219 ret->fd = fd;
220 sip->openconns = g_slist_append(sip->openconns, ret);
221 return ret;
224 static void connection_remove(struct sipe_account_data *sip, int fd)
226 struct sip_connection *conn = connection_find(sip, fd);
227 sip->openconns = g_slist_remove(sip->openconns, conn);
228 if (conn->inputhandler) purple_input_remove(conn->inputhandler);
229 g_free(conn->inbuf);
230 g_free(conn);
233 static void connection_free_all(struct sipe_account_data *sip)
235 struct sip_connection *ret = NULL;
236 GSList *entry = sip->openconns;
237 while (entry) {
238 ret = entry->data;
239 connection_remove(sip, ret->fd);
240 entry = sip->openconns;
244 static void sipe_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
246 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
247 struct sipe_buddy *b;
248 if (strncmp("sip:", buddy->name, 4)) {
249 gchar *buf = g_strdup_printf("sip:%s", buddy->name);
250 purple_blist_rename_buddy(buddy, buf);
251 g_free(buf);
253 if (!g_hash_table_lookup(sip->buddies, buddy->name)) {
254 b = g_new0(struct sipe_buddy, 1);
255 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy->name);
256 b->name = g_strdup(buddy->name);
257 g_hash_table_insert(sip->buddies, b->name, b);
258 } else {
259 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy->name);
263 static void sipe_get_buddies(PurpleConnection *gc)
265 PurpleBlistNode *gnode, *cnode, *bnode;
267 purple_debug_info("sipe", "sipe_get_buddies\n");
269 for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
270 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue;
271 for (cnode = gnode->child; cnode; cnode = cnode->next) {
272 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue;
273 for (bnode = cnode->child; bnode; bnode = bnode->next) {
274 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue;
275 if (((PurpleBuddy*)bnode)->account == gc->account)
276 sipe_add_buddy(gc, (PurpleBuddy*)bnode, (PurpleGroup *)gnode);
282 static void sipe_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
284 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
285 struct sipe_buddy *b = g_hash_table_lookup(sip->buddies, buddy->name);
286 g_hash_table_remove(sip->buddies, buddy->name);
287 g_free(b->name);
288 g_free(b);
291 static GList *sipe_status_types(PurpleAccount *acc)
293 PurpleStatusType *type;
294 GList *types = NULL;
296 type = purple_status_type_new_with_attrs(
297 PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
298 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
299 NULL);
300 types = g_list_append(types, type);
302 type = purple_status_type_new_full(
303 PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE);
304 types = g_list_append(types, type);
306 return types;
309 //static struct sipe_krb5_auth krb5_auth;
310 static gchar *auth_header_without_newline(struct sipe_account_data *sip, struct sip_auth *auth, struct sipmsg * msg, gboolean force_reauth)
312 const gchar *method = msg->method;
313 const gchar *target = msg->target;
314 gchar noncecount[9];
315 gchar *response;
316 gchar *ret;
317 gchar *tmp;
318 const char *authdomain;
319 const char *authuser;
320 const char *krb5_realm;
321 const char *host;
322 gchar *krb5_token = NULL;
324 authdomain = purple_account_get_string(sip->account, "authdomain", "");
325 authuser = purple_account_get_string(sip->account, "authuser", sip->username);
327 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
328 // and do error checking
330 // KRB realm should always be uppercase
331 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
333 if (sip->realhostname) {
334 host = sip->realhostname;
335 } else if (purple_account_get_bool(sip->account, "use_proxy", TRUE)) {
336 host = purple_account_get_string(sip->account, "proxy", "");
337 } else {
338 host = sip->sipdomain;
341 /*gboolean new_auth = krb5_auth.gss_context == NULL;
342 if (new_auth) {
343 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
346 if (new_auth || force_reauth) {
347 krb5_token = krb5_auth.base64_token;
350 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
351 krb5_token = krb5_auth.base64_token;*/
353 if (!authuser || strlen(authuser) < 1) {
354 authuser = sip->username;
357 if (auth->type == 1) { /* Digest */
358 sprintf(noncecount, "%08d", auth->nc++);
359 response = purple_cipher_http_digest_calculate_response(
360 "md5", method, target, NULL, NULL,
361 auth->nonce, noncecount, NULL, auth->digest_session_key);
362 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response %s\n", response);
364 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);
365 g_free(response);
366 return ret;
367 } else if (auth->type == 2) { /* NTLM */
368 // If we have a signature for the message, include that
369 if (msg->signature) {
370 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);
371 return tmp;
374 if (auth->nc == 3 && auth->nonce && auth->ntlm_key == NULL) {
375 /* TODO: Don't hardcode "purple" as the hostname */
376 const gchar * ntlm_key;
377 gchar * gssapi_data = purple_ntlm_gen_authenticate(&ntlm_key, authuser, sip->password, "purple", authdomain, (const guint8 *)auth->nonce, &auth->flags);
378 auth->ntlm_key = (gchar *)ntlm_key;
379 tmp = g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth->opaque, auth->realm, auth->target, gssapi_data);
380 g_free(gssapi_data);
381 return tmp;
384 tmp = g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth->realm, auth->target);
385 return tmp;
386 } else if (auth->type == 3) {
387 /* Kerberos */
388 if (auth->nc == 3) {
389 /*if (new_auth || force_reauth) {
390 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
391 if (auth->opaque) {
392 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);
393 } else {
394 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
396 } else {
397 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
398 gchar * mic = "MICTODO";
399 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
400 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
401 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
402 //auth->opaque ? auth->opaque : "", auth->target);
403 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
404 //g_free(mic);
406 return tmp;
408 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth->target);
411 sprintf(noncecount, "%08d", auth->nc++);
412 response = purple_cipher_http_digest_calculate_response(
413 "md5", method, target, NULL, NULL,
414 auth->nonce, noncecount, NULL, auth->digest_session_key);
415 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response %s\n", response);
417 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);
418 g_free(response);
419 return ret;
422 static gchar *auth_header(struct sipe_account_data *sip, struct sip_auth *auth, struct sipmsg * msg, gboolean force_reauth)
424 gchar *with, *without;
426 without = auth_header_without_newline(sip, auth, msg, force_reauth);
427 with = g_strdup_printf("%s\r\n", without);
428 g_free (without);
429 return with;
433 static char *parse_attribute(const char *attrname, const char *source)
435 const char *tmp, *tmp2;
436 char *retval = NULL;
437 int len = strlen(attrname);
439 if (!strncmp(source, attrname, len)) {
440 tmp = source + len;
441 tmp2 = g_strstr_len(tmp, strlen(tmp), "\"");
442 if (tmp2)
443 retval = g_strndup(tmp, tmp2 - tmp);
444 else
445 retval = g_strdup(tmp);
448 return retval;
451 static void fill_auth(struct sipe_account_data *sip, gchar *hdr, struct sip_auth *auth)
453 int i = 0;
454 const char *authuser;
455 char *tmp;
456 gchar **parts;
457 const char *krb5_realm;
458 const char *host;
460 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
461 // and do error checking
463 // KRB realm should always be uppercase
464 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
466 if (sip->realhostname) {
467 host = sip->realhostname;
468 } else if (purple_account_get_bool(sip->account, "use_proxy", TRUE)) {
469 host = purple_account_get_string(sip->account, "proxy", "");
470 } else {
471 host = sip->sipdomain;
474 authuser = purple_account_get_string(sip->account, "authuser", sip->username);
476 if (!authuser || strlen(authuser) < 1) {
477 authuser = sip->username;
480 if (!hdr) {
481 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
482 return;
485 if (!g_strncasecmp(hdr, "NTLM", 4)) {
486 auth->type = 2;
487 parts = g_strsplit(hdr+5, "\", ", 0);
488 i = 0;
489 while (parts[i]) {
490 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
491 if ((tmp = parse_attribute("gssapi-data=\"", parts[i]))) {
492 auth->nonce = g_memdup(purple_ntlm_parse_challenge(tmp, &auth->flags), 8);
493 g_free(tmp);
495 if ((tmp = parse_attribute("targetname=\"",
496 parts[i]))) {
497 auth->target = tmp;
499 else if ((tmp = parse_attribute("realm=\"",
500 parts[i]))) {
501 auth->realm = tmp;
503 else if ((tmp = parse_attribute("opaque=\"", parts[i]))) {
504 auth->opaque = tmp;
506 i++;
508 g_strfreev(parts);
509 auth->nc = 1;
510 if (!strstr(hdr, "gssapi-data")) {
511 auth->nc = 1;
512 } else {
513 auth->nc = 3;
515 return;
518 if (!g_strncasecmp(hdr, "Kerberos", 8)) {
519 purple_debug(PURPLE_DEBUG_MISC, "sipe", "setting auth type to Kerberos (3)\r\n");
520 auth->type = 3;
521 purple_debug(PURPLE_DEBUG_MISC, "sipe", "fill_auth - header: %s\r\n", hdr);
522 parts = g_strsplit(hdr+9, "\", ", 0);
523 i = 0;
524 while (parts[i]) {
525 purple_debug_info("sipe", "krb - parts[i] %s\n", parts[i]);
526 if ((tmp = parse_attribute("gssapi-data=\"", parts[i]))) {
527 /*if (krb5_auth.gss_context == NULL) {
528 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
530 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
531 g_free(tmp);
533 if ((tmp = parse_attribute("targetname=\"", parts[i]))) {
534 auth->target = tmp;
535 } else if ((tmp = parse_attribute("realm=\"", parts[i]))) {
536 auth->realm = tmp;
537 } else if ((tmp = parse_attribute("opaque=\"", parts[i]))) {
538 auth->opaque = tmp;
540 i++;
542 g_strfreev(parts);
543 auth->nc = 3;
544 //if (!strstr(hdr, "gssapi-data")) {
545 // auth->nc = 1;
546 //} else {
547 // auth->nc = 3;
549 return;
552 auth->type = 1;
553 parts = g_strsplit(hdr, " ", 0);
554 while (parts[i]) {
555 if ((tmp = parse_attribute("nonce=\"", parts[i]))) {
556 auth->nonce = tmp;
558 else if ((tmp = parse_attribute("realm=\"", parts[i]))) {
559 auth->realm = tmp;
561 i++;
563 g_strfreev(parts);
565 purple_debug(PURPLE_DEBUG_MISC, "sipe", "nonce: %s realm: %s\n", auth->nonce ? auth->nonce : "(null)", auth->realm ? auth->realm : "(null)");
566 if (auth->realm) {
567 auth->digest_session_key = purple_cipher_http_digest_calculate_session_key(
568 "md5", authuser, auth->realm, sip->password, auth->nonce, NULL);
570 auth->nc = 1;
574 static void sipe_canwrite_cb(gpointer data, gint source, PurpleInputCondition cond)
576 PurpleConnection *gc = data;
577 struct sipe_account_data *sip = gc->proto_data;
578 gsize max_write;
579 gssize written;
581 max_write = purple_circ_buffer_get_max_read(sip->txbuf);
583 if (max_write == 0) {
584 purple_input_remove(sip->tx_handler);
585 sip->tx_handler = 0;
586 return;
589 written = write(sip->fd, sip->txbuf->outptr, max_write);
591 if (written < 0 && errno == EAGAIN)
592 written = 0;
593 else if (written <= 0) {
594 /*TODO: do we really want to disconnect on a failure to write?*/
595 purple_connection_error(gc, _("Could not write"));
596 return;
599 purple_circ_buffer_mark_read(sip->txbuf, written);
602 static void sipe_canwrite_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
604 PurpleConnection *gc = data;
605 struct sipe_account_data *sip = gc->proto_data;
606 gsize max_write;
607 gssize written;
609 max_write = purple_circ_buffer_get_max_read(sip->txbuf);
611 if (max_write == 0) {
612 purple_input_remove(sip->tx_handler);
613 sip->tx_handler = 0;
614 return;
617 written = purple_ssl_write(sip->gsc, sip->txbuf->outptr, max_write);
619 if (written < 0 && errno == EAGAIN)
620 written = 0;
621 else if (written <= 0) {
622 /*TODO: do we really want to disconnect on a failure to write?*/
623 purple_connection_error(gc, _("Could not write"));
624 return;
627 purple_circ_buffer_mark_read(sip->txbuf, written);
630 static void sipe_input_cb(gpointer data, gint source, PurpleInputCondition cond);
632 static void send_later_cb(gpointer data, gint source, const gchar *error)
634 PurpleConnection *gc = data;
635 struct sipe_account_data *sip;
636 struct sip_connection *conn;
638 if (!PURPLE_CONNECTION_IS_VALID(gc))
640 if (source >= 0)
641 close(source);
642 return;
645 if (source < 0) {
646 purple_connection_error(gc, _("Could not connect"));
647 return;
650 sip = gc->proto_data;
651 sip->fd = source;
652 sip->connecting = FALSE;
654 sipe_canwrite_cb(gc, sip->fd, PURPLE_INPUT_WRITE);
656 /* If there is more to write now, we need to register a handler */
657 if (sip->txbuf->bufused > 0)
658 sip->tx_handler = purple_input_add(sip->fd, PURPLE_INPUT_WRITE,
659 sipe_canwrite_cb, gc);
661 conn = connection_create(sip, source);
662 conn->inputhandler = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_input_cb, gc);
665 static void send_later_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
667 PurpleConnection *gc = data;
668 struct sipe_account_data *sip;
669 struct sip_connection *conn;
671 if (!PURPLE_CONNECTION_IS_VALID(gc))
673 if(gsc) purple_ssl_close(gsc);
674 return;
677 sip = gc->proto_data;
678 sip->fd = gsc->fd;
679 sip->connecting = FALSE;
681 sipe_canwrite_cb_ssl(gc, gsc, PURPLE_INPUT_WRITE);
683 /* If there is more to write now, we need to register a handler */
684 if (sip->txbuf->bufused > 0)
685 purple_ssl_input_add(gsc, sipe_canwrite_cb_ssl, gc);
687 conn = connection_create(sip, gsc->fd);
688 purple_ssl_input_add(sip->gsc, sipe_input_cb_ssl, gc);
692 static void sendlater(PurpleConnection *gc, const char *buf)
694 struct sipe_account_data *sip = gc->proto_data;
696 if (!sip->connecting) {
697 purple_debug_info("sipe", "connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport);
698 if (sip->use_ssl){
699 sip->gsc = purple_ssl_connect(sip->account,sip->realhostname, sip->realport, send_later_cb_ssl, sipe_ssl_connect_failure, sip->gc);
701 else{
702 if (purple_proxy_connect(gc, sip->account, sip->realhostname, sip->realport, send_later_cb, gc) == NULL) {
703 purple_connection_error(gc, _("Couldn't create socket"));
706 sip->connecting = TRUE;
709 if (purple_circ_buffer_get_max_read(sip->txbuf) > 0)
710 purple_circ_buffer_append(sip->txbuf, "\r\n", 2);
712 purple_circ_buffer_append(sip->txbuf, buf, strlen(buf));
715 static void sendout_pkt(PurpleConnection *gc, const char *buf)
717 struct sipe_account_data *sip = gc->proto_data;
718 time_t currtime = time(NULL);
719 int writelen = strlen(buf);
721 purple_debug(PURPLE_DEBUG_MISC, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime), buf);
722 if (sip->udp) {
723 if (sendto(sip->fd, buf, writelen, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)) < writelen) {
724 purple_debug_info("sipe", "could not send packet\n");
726 } else {
727 int ret;
728 if (sip->fd < 0) {
729 sendlater(gc, buf);
730 return;
733 if (sip->tx_handler) {
734 ret = -1;
735 errno = EAGAIN;
736 } else{
737 if (sip->gsc){
738 ret = purple_ssl_write(sip->gsc, buf, writelen);
739 }else{
740 ret = write(sip->fd, buf, writelen);
744 if (ret < 0 && errno == EAGAIN)
745 ret = 0;
746 else if (ret <= 0) { /* XXX: When does this happen legitimately? */
747 sendlater(gc, buf);
748 return;
751 if (ret < writelen) {
752 if (!sip->tx_handler){
753 if (sip->gsc){
754 purple_ssl_input_add(sip->gsc, sipe_canwrite_cb_ssl, gc);
756 else{
757 sip->tx_handler = purple_input_add(sip->fd,
758 PURPLE_INPUT_WRITE, sipe_canwrite_cb,
759 gc);
763 /* XXX: is it OK to do this? You might get part of a request sent
764 with part of another. */
765 if (sip->txbuf->bufused > 0)
766 purple_circ_buffer_append(sip->txbuf, "\r\n", 2);
768 purple_circ_buffer_append(sip->txbuf, buf + ret,
769 writelen - ret);
774 static int sipe_send_raw(PurpleConnection *gc, const char *buf, int len)
776 sendout_pkt(gc, buf);
777 return len;
780 static void sendout_sipmsg(struct sipe_account_data *sip, struct sipmsg *msg)
782 GSList *tmp = msg->headers;
783 gchar *name;
784 gchar *value;
785 GString *outstr = g_string_new("");
786 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n", msg->method, msg->target);
787 while (tmp) {
788 name = ((struct siphdrelement*) (tmp->data))->name;
789 value = ((struct siphdrelement*) (tmp->data))->value;
790 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
791 tmp = g_slist_next(tmp);
793 g_string_append_printf(outstr, "\r\n%s", msg->body ? msg->body : "");
794 sendout_pkt(sip->gc, outstr->str);
795 g_string_free(outstr, TRUE);
798 static void sign_outgoing_message (struct sipmsg * msg, struct sipe_account_data *sip, const gchar *method)
800 gchar * buf;
801 if (sip->registrar.ntlm_key) {
802 struct sipmsg_breakdown msgbd;
803 msgbd.msg = msg;
804 sipmsg_breakdown_parse(&msgbd, sip->registrar.realm, sip->registrar.target);
805 // TODO generate this
806 msgbd.rand = g_strdup("0878F41B");
807 sip->registrar.ntlm_num++;
808 msgbd.num = g_strdup_printf("%d", sip->registrar.ntlm_num);
809 gchar * signature_input_str = sipmsg_breakdown_get_string(&msgbd);
810 if (signature_input_str != NULL) {
811 msg->signature = purple_ntlm_sipe_signature_make (signature_input_str, sip->registrar.ntlm_key);
812 msg->rand = g_strdup(msgbd.rand);
813 msg->num = g_strdup(msgbd.num);
815 sipmsg_breakdown_free(&msgbd);
818 if (sip->registrar.type && !strcmp(method, "REGISTER")) {
819 buf = auth_header_without_newline(sip, &sip->registrar, msg, FALSE);
820 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
821 sipmsg_add_header(msg, "Authorization", buf);
822 } else {
823 sipmsg_add_header_pos(msg, "Proxy-Authorization", buf, 5);
824 //sipmsg_add_header_pos(msg, "Authorization", buf, 5);
826 g_free(buf);
827 } 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")) {
828 sip->registrar.nc=3;
829 sip->registrar.type=2;
831 buf = auth_header_without_newline(sip, &sip->registrar, msg, FALSE);
832 //buf = auth_header(sip, &sip->proxy, msg, FALSE);
833 sipmsg_add_header_pos(msg, "Proxy-Authorization", buf, 5);
834 //sipmsg_add_header(msg, "Authorization", buf);
835 g_free(buf);
836 } else {
837 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method);
841 static char *get_contact(struct sipe_account_data *sip)
843 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");
848 static void send_sip_response(PurpleConnection *gc, struct sipmsg *msg, int code,
849 const char *text, const char *body)
851 GSList *tmp = msg->headers;
852 gchar *name;
853 gchar *value;
854 GString *outstr = g_string_new("");
855 struct sipe_account_data *sip = gc->proto_data;
857 gchar *contact;
858 contact = get_contact(sip);
859 sipmsg_remove_header(msg, "Contact");
860 sipmsg_add_header(msg, "Contact", contact);
861 g_free(contact);
863 /* When sending the acknowlegements and errors, the content length from the original
864 message is still here, but there is no body; we need to make sure we're sending the
865 correct content length */
866 sipmsg_remove_header(msg, "Content-Length");
867 if (body) {
868 gchar len[12];
869 sprintf(len, "%" G_GSIZE_FORMAT , strlen(body));
870 sipmsg_add_header(msg, "Content-Length", len);
871 } else {
872 sipmsg_add_header(msg, "Content-Length", "0");
875 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
876 //gchar * mic = "MICTODO";
877 msg->response = code;
879 sipmsg_remove_header(msg, "Authentication-Info");
880 sign_outgoing_message(msg, sip, msg->method);
882 g_string_append_printf(outstr, "SIP/2.0 %d %s\r\n", code, text);
883 while (tmp) {
884 name = ((struct siphdrelement*) (tmp->data))->name;
885 value = ((struct siphdrelement*) (tmp->data))->value;
887 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
888 tmp = g_slist_next(tmp);
890 g_string_append_printf(outstr, "\r\n%s", body ? body : "");
891 sendout_pkt(gc, outstr->str);
892 g_string_free(outstr, TRUE);
895 static void transactions_remove(struct sipe_account_data *sip, struct transaction *trans)
897 if (trans->msg) sipmsg_free(trans->msg);
898 sip->transactions = g_slist_remove(sip->transactions, trans);
899 g_free(trans);
902 static struct transaction *
903 transactions_add_buf(struct sipe_account_data *sip, const struct sipmsg *msg, void *callback)
905 struct transaction *trans = g_new0(struct transaction, 1);
906 trans->time = time(NULL);
907 trans->msg = (struct sipmsg *)msg;
908 trans->cseq = sipmsg_find_header(trans->msg, "CSeq");
909 trans->callback = callback;
910 sip->transactions = g_slist_append(sip->transactions, trans);
911 return trans;
914 static struct transaction *transactions_find(struct sipe_account_data *sip, struct sipmsg *msg)
916 struct transaction *trans;
917 GSList *transactions = sip->transactions;
918 gchar *cseq = sipmsg_find_header(msg, "CSeq");
920 while (transactions) {
921 trans = transactions->data;
922 if (!strcmp(trans->cseq, cseq)) {
923 return trans;
925 transactions = transactions->next;
928 return NULL;
932 static struct transaction *
933 send_sip_request(PurpleConnection *gc, const gchar *method,
934 const gchar *url, const gchar *to, const gchar *addheaders,
935 const gchar *body, struct sip_dialog *dialog, TransCallback tc)
937 struct sipe_account_data *sip = gc->proto_data;
938 const char *addh = "";
939 char *buf;
940 struct sipmsg *msg;
941 gchar *ptmp;
942 gchar *ourtag = dialog && dialog->ourtag ? g_strdup(dialog->ourtag) : gentag();
943 gchar *theirtag = dialog && dialog->theirtag ? g_strdup(dialog->theirtag) : NULL;
944 gchar *theirepid = dialog && dialog->theirepid ? g_strdup(dialog->theirepid) : NULL;
945 gchar *callid = dialog && dialog->callid ? g_strdup(dialog->callid) : gencallid();
946 gchar *branch = dialog && dialog->callid ? NULL : genbranch();
947 gchar *useragent = purple_account_get_string(sip->account, "useragent", "Purple/" VERSION);
948 if (!strcmp(method, "REGISTER")) {
949 if (sip->regcallid) {
950 g_free(callid);
951 callid = g_strdup(sip->regcallid);
952 } else {
953 sip->regcallid = g_strdup(callid);
957 if (addheaders) addh = addheaders;
959 buf = g_strdup_printf("%s %s SIP/2.0\r\n"
960 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
961 "From: <sip:%s>;tag=%s;epid=%s\r\n"
962 "To: <%s>%s%s%s%s\r\n"
963 "Max-Forwards: 70\r\n"
964 "CSeq: %d %s\r\n"
965 "User-Agent: %s\r\n"
966 "Call-ID: %s\r\n"
967 "%s%s"
968 "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n%s",
969 method,
970 dialog && dialog->request ? dialog->request : url,
971 sip->use_ssl ? "TLS" : sip->udp ? "UDP" : "TCP",
972 purple_network_get_my_ip(-1),
973 sip->listenport,
974 branch ? ";branch=" : "",
975 branch ? branch : "",
976 sip->username,
977 ourtag ? ourtag : "",
978 getuuid(), // TODO generate one per account/login
980 theirtag ? ";tag=" : "",
981 theirtag ? theirtag : "",
982 theirepid ? ";epid=" : "",
983 theirepid ? theirepid : "",
984 dialog ? ++dialog->cseq : ++sip->cseq,
985 method,
986 useragent,
987 callid,
988 dialog && dialog->route ? dialog->route : "",
989 addh,
990 body ? strlen(body) : 0,
991 body ? body : "");
994 //printf ("parsing msg buf:\n%s\n\n", buf);
995 msg = sipmsg_parse_msg(buf);
997 g_free(buf);
998 g_free(ourtag);
999 g_free(theirtag);
1000 g_free(theirepid);
1001 g_free(branch);
1002 g_free(callid);
1004 sign_outgoing_message (msg, sip, method);
1006 buf = sipmsg_to_string (msg);
1008 /* add to ongoing transactions */
1009 struct transaction * trans = transactions_add_buf(sip, msg, tc);
1010 sendout_pkt(gc, buf);
1012 return trans;
1015 static char *get_contact_register(struct sipe_account_data *sip)
1017 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()));
1020 static void do_register_exp(struct sipe_account_data *sip, int expire)
1022 char *uri = g_strdup_printf("sip:%s", sip->sipdomain);
1023 char *to = g_strdup_printf("sip:%s", sip->username);
1024 char *contact = get_contact_register(sip);
1025 //char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire);
1026 // 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);
1027 //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);
1028 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);
1029 g_free(contact);
1031 sip->registerstatus = 1;
1033 if (expire) {
1034 sip->reregister = time(NULL) + expire - 50;
1035 } else {
1036 sip->reregister = time(NULL) + 600;
1039 send_sip_request(sip->gc, "REGISTER", uri, to, hdr, "", NULL,
1040 process_register_response);
1042 g_free(hdr);
1043 g_free(uri);
1044 g_free(to);
1047 static void do_register(struct sipe_account_data *sip)
1049 do_register_exp(sip, sip->registerexpire);
1052 static gchar *parse_from(const gchar *hdr)
1054 gchar *from;
1055 const gchar *tmp, *tmp2 = hdr;
1057 if (!hdr) return NULL;
1058 purple_debug_info("sipe", "parsing address out of %s\n", hdr);
1059 tmp = strchr(hdr, '<');
1061 /* i hate the different SIP UA behaviours... */
1062 if (tmp) { /* sip address in <...> */
1063 tmp2 = tmp + 1;
1064 tmp = strchr(tmp2, '>');
1065 if (tmp) {
1066 from = g_strndup(tmp2, tmp - tmp2);
1067 } else {
1068 purple_debug_info("sipe", "found < without > in From\n");
1069 return NULL;
1071 } else {
1072 tmp = strchr(tmp2, ';');
1073 if (tmp) {
1074 from = g_strndup(tmp2, tmp - tmp2);
1075 } else {
1076 from = g_strdup(tmp2);
1079 purple_debug_info("sipe", "got %s\n", from);
1080 return from;
1083 static gboolean process_subscribe_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1085 gchar *to;
1087 if (msg->response == 200 || msg->response == 202) {
1088 return TRUE;
1091 to = parse_from(sipmsg_find_header(tc->msg, "To")); /* cant be NULL since it is our own msg */
1093 /* we can not subscribe -> user is offline (TODO unknown status?) */
1095 purple_prpl_got_user_status(sip->account, to, "offline", NULL);
1096 g_free(to);
1097 return TRUE;
1100 static void sipe_subscribe_to_name(struct sipe_account_data *sip, const char * buddy_name)
1102 gchar *to = strstr(buddy_name, "sip:") ? g_strdup(buddy_name) : g_strdup_printf("sip:%s", buddy_name);
1103 gchar *tmp = get_contact(sip);
1104 gchar *contact = g_strdup_printf(
1105 "Accept: application/pidf+xml, application/xpidf+xml\r\n"
1106 "Event: presence\r\n"
1107 "Contact: %s\r\n", tmp);
1108 g_free(tmp);
1110 /* subscribe to buddy presence
1111 * we dont need to know the status so we do not need a callback */
1113 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL,
1114 process_subscribe_response);
1116 g_free(to);
1117 g_free(contact);
1120 static void sipe_subscribe(struct sipe_account_data *sip, struct sipe_buddy *buddy)
1122 sipe_subscribe_to_name(sip, buddy->name);
1124 /* resubscribe before subscription expires */
1125 /* add some jitter */
1126 buddy->resubscribe = time(NULL)+1140+(rand()%50);
1129 static gboolean sipe_add_lcs_contacts(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1131 gchar *tmp;
1132 xmlnode *item, *group, *isc;
1133 const char *name_group, *group_id;
1134 PurpleBuddy *b;
1135 PurpleGroup *g = NULL;
1136 gchar **parts;
1137 gchar *apn;
1138 int ng = 0, i;
1139 struct sipe_buddy *bs;
1140 struct sipe_group *gr;
1141 int len = msg->bodylen;
1143 // Reserved to max 10 groups. TODO be dynamic
1144 gr = g_new0(struct sipe_group, 10);
1146 tmp = sipmsg_find_header(msg, "Event");
1147 if (tmp && !strncmp(tmp, "vnd-microsoft-roaming-contacts", 30)) {
1148 purple_debug_info("sipe", "sipe_add_lcs_contacts->%s-%d\n", msg->body, len);
1149 /*Convert the contact from XML to Purple Buddies*/
1150 isc = xmlnode_from_str(msg->body, len);
1152 /* TODO Find for all groups */
1153 for (group = xmlnode_get_child(isc, "group"); group; group = xmlnode_get_next_twin(group)) {
1154 name_group = xmlnode_get_attrib(group, "name");
1155 group_id = xmlnode_get_attrib(group, "id");
1157 if (!strncmp(name_group, "~", 1)){
1158 name_group=g_strdup("General");
1161 gr[ng].name_group = g_strdup(name_group);
1162 gr[ng].id = g_strdup(group_id);
1163 purple_debug_info("sipe", "name_group->%s\n", name_group);
1164 g = purple_find_group(name_group);
1166 if (!g) {
1167 g = purple_group_new(name_group);
1168 purple_blist_add_group(g, NULL);
1171 if (!g) {
1172 g = purple_find_group("General");
1173 if (!g) {
1174 g = purple_group_new("General");
1175 purple_blist_add_group(g, NULL);
1179 gr[ng].g = g;
1180 ng++;
1183 for (i = 0; i < ng;i++) {
1184 purple_debug_info("sipe", "id->%s\n", gr[i].id);
1185 purple_debug_info("sipe", "id->%s\n", gr[i].name_group);
1188 for (item = xmlnode_get_child(isc, "contact"); item; item = xmlnode_get_next_twin(item)) {
1189 const char *uri, *name, *groups;
1190 char *buddy_name;
1191 i = 0;
1192 uri = xmlnode_get_attrib(item, "uri");
1193 name = xmlnode_get_attrib(item, "name");
1194 groups = xmlnode_get_attrib(item, "groups");
1195 parts = g_strsplit(groups, " ", 0);
1196 purple_debug_info("sipe", "URI->%s,Groups->%s\n", uri, groups);
1197 if (parts[i]!=NULL){
1198 while (parts[i]) {
1199 purple_debug_info("sipe", "Groups->parts[i] %s\n", parts[i]);
1200 if (!strcmp(gr[i].id,parts[i])){
1201 purple_debug_info("sipe", "Found Groups->gr[i].id(%s),gr[i].name_group (%s)\n",gr[i].id,gr[i].name_group);
1203 buddy_name = g_strdup_printf("sip:%s", uri);
1205 //b = purple_find_buddy(sip->account, buddy_name);
1206 b = purple_find_buddy_in_group(sip->account, buddy_name, gr[i].g);
1207 if (!b){
1208 b = purple_buddy_new(sip->account, buddy_name, uri);
1210 g_free(buddy_name);
1212 //sipe_add_buddy(sip->gc, b , gr[i].g);
1213 purple_blist_add_buddy(b, NULL, gr[i].g, NULL);
1214 purple_blist_alias_buddy(b, uri);
1215 bs = g_new0(struct sipe_buddy, 1);
1216 bs->name = g_strdup(b->name);
1217 g_hash_table_insert(sip->buddies, bs->name, bs);
1219 i++;
1224 xmlnode_free(isc);
1226 return 0;
1229 static void sipe_subscribe_buddylist(struct sipe_account_data *sip)
1231 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";
1232 gchar *to = g_strdup_printf("sip:%s", sip->username);
1233 gchar *tmp = get_contact(sip);
1234 contact = g_strdup_printf("%sContact: %s\r\n", contact, tmp);
1235 g_free(tmp);
1237 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL, sipe_add_lcs_contacts);
1238 g_free(to);
1239 g_free(contact);
1242 /* IM Session (INVITE and MESSAGE methods) */
1244 static struct sip_im_session * find_im_session (struct sipe_account_data *sip, const char *who)
1246 if (sip == NULL || who == NULL) {
1247 return NULL;
1250 struct sip_im_session *session;
1251 GSList *entry = sip->im_sessions;
1252 while (entry) {
1253 session = entry->data;
1254 if ((who != NULL && !strcmp(who, session->with))) {
1255 return session;
1257 entry = entry->next;
1259 return NULL;
1262 static struct sip_im_session * find_or_create_im_session (struct sipe_account_data *sip, const char *who)
1264 struct sip_im_session *session = find_im_session(sip, who);
1265 if (!session) {
1266 session = g_new0(struct sip_im_session, 1);
1267 session->with = g_strdup(who);
1268 sip->im_sessions = g_slist_append(sip->im_sessions, session);
1270 return session;
1273 static void im_session_destroy(struct sipe_account_data *sip, struct sip_im_session * session)
1275 sip->im_sessions = g_slist_remove(sip->im_sessions, session);
1276 // TODO free session resources
1279 static void sipe_send_message(struct sipe_account_data *sip, struct sip_im_session * session, const char *msg)
1281 gchar *hdr;
1282 gchar *fullto;
1283 gchar *tmp;
1285 if (strncmp("sip:", session->with, 4)) {
1286 fullto = g_strdup_printf("sip:%s", session->with);
1287 } else {
1288 fullto = g_strdup(session->with);
1291 hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1292 //hdr = g_strdup("Content-Type: text/rtf\r\n");
1293 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
1295 tmp = get_contact(sip);
1296 hdr = g_strdup_printf("Contact: %s\r\n%s", tmp, hdr);
1297 g_free(tmp);
1299 send_sip_request(sip->gc, "MESSAGE", fullto, fullto, hdr, msg, session->outgoing_dialog, NULL);
1301 g_free(hdr);
1302 g_free(fullto);
1306 static void
1307 sipe_im_process_queue (struct sipe_account_data * sip, struct sip_im_session * session)
1309 GSList *entry = session->outgoing_message_queue;
1310 while (entry) {
1311 char *queued_msg = entry->data;
1312 sipe_send_message(sip, session, queued_msg);
1314 // Remove from the queue and free the string
1315 entry = session->outgoing_message_queue = g_slist_remove(session->outgoing_message_queue, queued_msg);
1316 g_free(queued_msg);
1320 static gboolean
1321 process_invite_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *trans)
1323 gchar * with = parse_from(sipmsg_find_header(msg, "To"));
1324 struct sip_im_session * session = find_im_session(sip, with);
1325 g_free(with);
1327 if (!session) {
1328 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
1329 return FALSE;
1332 if (msg->response != 200) {
1333 purple_debug_info("sipe", "process_invite_response: INVITE response not 200, ignoring\n");
1334 im_session_destroy(sip, session);
1335 return FALSE;
1338 struct sip_dialog * dialog = session->outgoing_dialog;
1339 if (!dialog) {
1340 purple_debug_info("sipe", "process_invite_response: session outgoign dialog is NULL\n");
1341 return FALSE;
1344 dialog->callid = sipmsg_find_header(msg, "Call-ID");
1345 dialog->ourtag = find_tag(sipmsg_find_header(msg, "From"));
1346 dialog->theirtag = find_tag(sipmsg_find_header(msg, "To"));
1347 if (!dialog->theirepid) {
1348 dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, "To"), "epid=", ";", NULL);
1350 if (!dialog->theirepid) {
1351 dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, "To"), "epid=", NULL, NULL);
1354 dialog->request = sipmsg_find_part_of_header(sipmsg_find_header(msg, "Record-Route"), "<", ">", NULL);
1355 dialog->route = g_strdup_printf("Route: %s\r\n", sipmsg_find_header(msg, "Contact"));
1356 dialog->cseq = 0;
1358 send_sip_request(sip->gc, "ACK", session->with, session->with, NULL, NULL, dialog, NULL);
1360 session->outgoing_invite = NULL;
1362 sipe_im_process_queue(sip, session);
1364 return TRUE;
1368 static void sipe_invite(struct sipe_account_data *sip, struct sip_im_session * session)
1370 gchar *hdr;
1371 gchar *to;
1372 gchar *contact;
1373 gchar *body;
1375 if (strstr(session->with, "sip:")) {
1376 to = g_strdup(session->with);
1377 } else {
1378 to = g_strdup_printf("sip:%s", session->with);
1381 // Setup the outgoing dialog w/ the epid from the incoming dialog (if any)
1382 struct sip_dialog * dialog = g_new0(struct sip_dialog, 1);
1383 if (session->incoming_dialog) {
1384 printf("incoming dialog epid is %s\n", session->incoming_dialog->theirepid);
1385 dialog->theirepid = session->incoming_dialog->theirepid;
1386 } else {
1387 printf("incoming dialog is NULL\n");
1389 session->outgoing_dialog = dialog;
1391 contact = get_contact(sip);
1392 hdr = g_strdup_printf(
1393 "Contact: %s\r\n"
1394 //"Supported: ms-conf-invite\r\n"
1395 //"Supported: ms-delayed-accept\r\n"
1396 //"Supported: ms-renders-isf\r\n"
1397 //"Supported: ms-renders-gif\r\n"
1398 //"Supported: ms-renders-mime-alternative\r\n"*/
1399 //"Supported: timer\r\n"
1400 //"Supported: ms-sender\r\n"
1401 //"Supported: ms-early-media\r\n"
1402 "Content-Type: application/sdp\r\n",
1403 contact, sip->username, sip->username, to);
1405 body = g_strdup_printf(
1406 "v=0\r\n"
1407 "o=- 0 0 IN IP4 %s\r\n"
1408 "s=session\r\n"
1409 "c=IN IP4 %s\r\n"
1410 "t=0 0\r\n"
1411 "m=message %d sip null\r\n"
1412 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1413 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), 5061);
1415 session->outgoing_invite = send_sip_request(sip->gc, "INVITE",
1416 to, to, hdr, body, session->outgoing_dialog, process_invite_response);
1418 g_free(to);
1419 g_free(body);
1420 g_free(hdr);
1421 g_free(contact);
1424 static void
1425 im_session_close (struct sipe_account_data *sip, struct sip_im_session * session)
1427 if (session) {
1428 send_sip_request(sip->gc, "BYE", session->with, session->with, NULL, NULL, session->outgoing_dialog, NULL);
1429 im_session_destroy(sip, session);
1433 static void
1434 sipe_convo_closed(PurpleConnection * gc, const char *who)
1436 struct sipe_account_data *sip = gc->proto_data;
1438 purple_debug_info("sipe", "conversation with %s closed\n", who);
1439 im_session_close(sip, find_im_session(sip, who));
1442 static void
1443 im_session_close_all (struct sipe_account_data *sip)
1445 GSList *entry = sip->im_sessions;
1446 while (entry) {
1447 im_session_close (sip, entry->data);
1448 entry = sip->im_sessions;
1452 static int sipe_im_send(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags)
1454 struct sipe_account_data *sip = gc->proto_data;
1455 char *to = g_strdup(who);
1456 char *text = purple_unescape_html(what);
1458 struct sip_im_session * session = find_or_create_im_session(sip, who);
1460 // Queue the message
1461 session->outgoing_message_queue = g_slist_append(session->outgoing_message_queue, text);
1463 if (session->outgoing_dialog && session->outgoing_dialog->callid) {
1464 sipe_im_process_queue(sip, session);
1465 } else if (!session->outgoing_invite) {
1466 // Need to send the INVITE to get the outgoing dialog setup
1467 sipe_invite(sip, session);
1470 g_free(to);
1471 return 1;
1475 /* End IM Session (INVITE and MESSAGE methods) */
1477 static void sipe_buddy_resub(char *name, struct sipe_buddy *buddy, struct sipe_account_data *sip)
1479 time_t curtime = time(NULL);
1480 if (buddy->resubscribe < curtime) {
1481 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_buddy_resub %s\n", name);
1482 sipe_subscribe(sip, buddy);
1486 static gboolean resend_timeout(struct sipe_account_data *sip)
1488 GSList *tmp = sip->transactions;
1489 time_t currtime = time(NULL);
1490 while (tmp) {
1491 struct transaction *trans = tmp->data;
1492 tmp = tmp->next;
1493 purple_debug_info("sipe", "have open transaction age: %d\n", currtime- trans->time);
1494 if ((currtime - trans->time > 5) && trans->retries >= 1) {
1495 /* TODO 408 */
1496 } else {
1497 if ((currtime - trans->time > 2) && trans->retries == 0) {
1498 trans->retries++;
1499 sendout_sipmsg(sip, trans->msg);
1503 return TRUE;
1506 static gboolean subscribe_timeout(struct sipe_account_data *sip)
1508 GSList *tmp;
1509 time_t curtime = time(NULL);
1510 /* register again if first registration expires */
1511 if (sip->reregister < curtime) {
1512 do_register(sip);
1514 /* check for every subscription if we need to resubscribe */
1515 //Fixxxer we need resub?
1516 g_hash_table_foreach(sip->buddies, (GHFunc)sipe_buddy_resub, (gpointer)sip);
1518 /* remove a timed out suscriber */
1520 tmp = sip->watcher;
1521 while (tmp) {
1522 struct sipe_watcher *watcher = tmp->data;
1523 if (watcher->expire < curtime) {
1524 watcher_remove(sip, watcher->name);
1525 tmp = sip->watcher;
1527 if (tmp) tmp = tmp->next;
1530 return TRUE;
1533 static void process_incoming_message(struct sipe_account_data *sip, struct sipmsg *msg)
1535 gchar *from;
1536 gchar *contenttype;
1537 gboolean found = FALSE;
1539 from = parse_from(sipmsg_find_header(msg, "From"));
1541 if (!from) return;
1543 purple_debug_info("sipe", "got message from %s: %s\n", from, msg->body);
1545 contenttype = sipmsg_find_header(msg, "Content-Type");
1546 if (!contenttype || !strncmp(contenttype, "text/plain", 10) || !strncmp(contenttype, "text/html", 9)) {
1547 serv_got_im(sip->gc, from, msg->body, 0, time(NULL));
1548 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1549 found = TRUE;
1551 if (!strncmp(contenttype, "application/im-iscomposing+xml", 30)) {
1552 xmlnode *isc = xmlnode_from_str(msg->body, msg->bodylen);
1553 xmlnode *state;
1554 gchar *statedata;
1556 if (!isc) {
1557 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1558 return;
1561 state = xmlnode_get_child(isc, "state");
1563 if (!state) {
1564 purple_debug_info("sipe", "process_incoming_message: no state found\n");
1565 xmlnode_free(isc);
1566 return;
1569 statedata = xmlnode_get_data(state);
1570 if (statedata) {
1571 if (strstr(statedata, "active")) serv_got_typing(sip->gc, from, 0, PURPLE_TYPING);
1572 else serv_got_typing_stopped(sip->gc, from);
1574 g_free(statedata);
1576 xmlnode_free(isc);
1577 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1578 found = TRUE;
1580 if (!found) {
1581 purple_debug_info("sipe", "got unknown mime-type");
1582 send_sip_response(sip->gc, msg, 415, "Unsupported media type", NULL);
1584 g_free(from);
1587 static void process_incoming_invite(struct sipe_account_data *sip, struct sipmsg *msg)
1589 gchar * from = sipmsg_find_part_of_header(sipmsg_find_header(msg, "From"), "<", ">", NULL);
1590 struct sip_im_session * session = find_or_create_im_session (sip, from);
1591 if (session) {
1592 struct sip_dialog * dialog = g_new0(struct sip_dialog, 1);
1593 dialog->callid = sipmsg_find_header(msg, "Call-ID");
1594 dialog->ourtag = find_tag(sipmsg_find_header(msg, "To"));
1595 dialog->theirtag = find_tag(sipmsg_find_header(msg, "From"));
1596 dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, "From"), "epid=", NULL, NULL);
1597 printf("Created incoming dialog and set epid to %s\n", dialog->theirepid);
1599 session->incoming_dialog = dialog;
1600 } else {
1601 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
1603 g_free(from);
1605 send_sip_response(sip->gc, msg, 200, "OK", g_strdup_printf(
1606 "v=0\r\n"
1607 "o=- 0 0 IN IP4 %s\r\n"
1608 "s=session\r\n"
1609 "c=IN IP4 %s\r\n"
1610 "t=0 0\r\n"
1611 "m=message %d sip sip:%s\r\n"
1612 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1613 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
1614 //sip->realport, sip->username
1615 5061, sip->username));
1618 gboolean process_register_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1620 gchar *tmp, krb5_token;
1621 const gchar *expires_header;
1622 int expires;
1624 expires_header = sipmsg_find_header(msg, "Expires");
1625 expires = expires_header != NULL ? strtol(expires_header, NULL, 10) : 0;
1626 purple_debug_info("sipe", "got response to REGISTER; expires = %d\n", expires);
1628 switch (msg->response) {
1629 case 200:
1630 if (expires == 0) {
1631 sip->registerstatus = 0;
1632 } else {
1633 sip->registerstatus = 3;
1634 purple_connection_set_state(sip->gc, PURPLE_CONNECTED);
1636 /* tell everybody we're online */
1637 send_publish (sip);
1639 /* get buddies from blist; Has a bug */
1640 /*sipe_get_buddies(sip->gc);*/
1641 subscribe_timeout(sip);
1643 //sipe_subscribe_to_name(sip, sip->username);
1645 tmp = sipmsg_find_header(msg, "Allow-Events");
1646 if (tmp && strstr(tmp, "vnd-microsoft-provisioning")){
1647 sipe_subscribe_buddylist(sip);
1650 // Should we remove the transaction here?
1651 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip->cseq);
1652 transactions_remove(sip, tc);
1654 break;
1655 case 401:
1656 if (sip->registerstatus != 2) {
1657 purple_debug_info("sipe", "REGISTER retries %d\n", sip->registrar.retries);
1658 if (sip->registrar.retries > 3) {
1659 sip->gc->wants_to_die = TRUE;
1660 purple_connection_error(sip->gc, _("Wrong Password"));
1661 return TRUE;
1663 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
1664 tmp = sipmsg_find_auth_header(msg, "NTLM");
1665 } else {
1666 tmp = sipmsg_find_auth_header(msg, "Kerberos");
1668 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_register_response - Auth header: %s\r\n", tmp);
1669 fill_auth(sip, tmp, &sip->registrar);
1670 sip->registerstatus = 2;
1671 if (sip->account->disconnecting) {
1672 do_register_exp(sip, 0);
1673 } else {
1674 do_register(sip);
1677 break;
1679 return TRUE;
1682 static void process_incoming_notify(struct sipe_account_data *sip, struct sipmsg *msg)
1684 gchar *from;
1685 gchar *fromhdr;
1686 gchar *tmp2;
1687 xmlnode *pidf;
1688 xmlnode *basicstatus = NULL, *tuple, *status;
1689 gboolean isonline = FALSE;
1691 fromhdr = sipmsg_find_header(msg, "From");
1692 from = parse_from(fromhdr);
1693 if (!from) return;
1695 pidf = xmlnode_from_str(msg->body, msg->bodylen);
1697 if (!pidf) {
1698 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
1699 return;
1702 purple_debug_info("sipe", "process_incoming_notify: body(%s)\n",msg->body);
1704 if ((tuple = xmlnode_get_child(pidf, "tuple")))
1705 if ((status = xmlnode_get_child(tuple, "status")))
1706 basicstatus = xmlnode_get_child(status, "basic");
1708 if (!basicstatus) {
1709 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
1710 xmlnode_free(pidf);
1711 return;
1714 tmp2 = xmlnode_get_data(basicstatus);
1716 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n",tmp2);
1719 if (!tmp2) {
1720 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
1721 xmlnode_free(pidf);
1722 return;
1725 if (strstr(tmp2, "open")) {
1726 isonline = TRUE;
1729 g_free(tmp2);
1731 if (isonline) purple_prpl_got_user_status(sip->account, from, "available", NULL);
1732 else purple_prpl_got_user_status(sip->account, from, "offline", NULL);
1734 xmlnode_free(pidf);
1736 g_free(from);
1737 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1741 static gchar* gen_xpidf(struct sipe_account_data *sip)
1743 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1744 "<presence>\r\n"
1745 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
1746 "<display name=\"sip:%s\"/>\r\n"
1747 "<atom id=\"1234\">\r\n"
1748 "<address uri=\"sip:%s\">\r\n"
1749 "<status status=\"%s\"/>\r\n"
1750 "</address>\r\n"
1751 "</atom>\r\n"
1752 "</presence>\r\n",
1753 sip->username,
1754 sip->username,
1755 sip->username,
1756 sip->status);
1757 return doc;
1762 static gchar* gen_pidf(struct sipe_account_data *sip)
1764 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1765 "<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"
1766 "<tuple id=\"0\">\r\n"
1767 "<status>\r\n"
1768 "<basic>open</basic>\r\n"
1769 "<ep:activities>\r\n"
1770 " <ep:activity>%s</ep:activity>\r\n"
1771 "</ep:activities>"
1772 "</status>\r\n"
1773 "</tuple>\r\n"
1774 "<ci:display-name>%s</ci:display-name>\r\n"
1775 "</presence>",
1776 sip->username,
1777 sip->status,
1778 sip->username);
1779 return doc;
1782 static void send_notify(struct sipe_account_data *sip, struct sipe_watcher *watcher)
1784 gchar *doc = watcher->needsxpidf ? gen_xpidf(sip) : gen_pidf(sip);
1785 gchar *hdr = watcher->needsxpidf ? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
1786 send_sip_request(sip->gc, "NOTIFY", watcher->name, watcher->name, hdr, doc, &watcher->dialog, NULL);
1787 g_free(doc);
1790 static gboolean process_service_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1792 if (msg->response != 200 && msg->response != 408) {
1793 /* never send again */
1794 sip->republish = -1;
1796 return TRUE;
1799 static void send_publish(struct sipe_account_data *sip)
1801 gchar *uri = g_strdup_printf("sip:%s", sip->username);
1802 gchar *doc = g_strdup_printf(
1803 "<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=\"%s\"><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>",
1804 uri, generateUUIDfromEPID(getuuid()), uri,
1805 "00:00:00-05:00", // TODO timezone
1806 "PC" // TODO machine name
1809 gchar *tmp = get_contact(sip);
1810 gchar *hdr = g_strdup_printf("Contact: %s; +sip.instance=\"<urn:uuid:%s>\"\r\nAccept: application/ms-location-profile-definition+xml\r\nContent-Type: application/msrtc-category-publish+xml\r\n", tmp,generateUUIDfromEPID(getuuid()));
1811 g_free(tmp);
1813 send_sip_request(sip->gc, "SERVICE", uri, uri, hdr, doc, NULL, process_service_response);
1814 //sip->republish = time(NULL) + 500;
1815 g_free(hdr);
1816 g_free(uri);
1817 g_free(doc);
1820 static void send_service(struct sipe_account_data *sip)
1822 //gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->sipdomain);
1823 gchar *uri = g_strdup_printf("sip:%s", sip->username);
1824 //gchar *doc = gen_pidf(sip);
1826 gchar *doc = gen_pidf(sip);
1827 gchar *hdr = g_strdup("Event: presence\r\nContent-Type: application/pidf+xml\r\n");
1829 //gchar *hdr = g_strdup("Content-Type: application/SOAP+xml\r\n");
1830 gchar *tmp = get_contact(sip);
1831 hdr = g_strdup_printf("Contact: %s\r\n%s; +sip.instance=\"<urn:uuid:%s>\"", tmp, hdr,generateUUIDfromEPID(getuuid()));
1832 g_free(tmp);
1833 send_sip_request(sip->gc, "SERVICE", uri, uri,
1834 hdr,
1835 doc, NULL, process_service_response);
1836 sip->republish = time(NULL) + 500;
1837 g_free(hdr);
1838 g_free(uri);
1839 g_free(doc);
1842 static void process_incoming_subscribe(struct sipe_account_data *sip, struct sipmsg *msg)
1844 const char *from_hdr = sipmsg_find_header(msg, "From");
1845 gchar *from = parse_from(from_hdr);
1846 gchar *theirtag = find_tag(from_hdr);
1847 gchar *ourtag = find_tag(sipmsg_find_header(msg, "To"));
1848 gboolean tagadded = FALSE;
1849 gchar *callid = sipmsg_find_header(msg, "Call-ID");
1850 gchar *expire = sipmsg_find_header(msg, "Expire");
1851 // gchar *ms-received-port =find_received_port(sipmsg_find_header(msg, "Contact"));
1852 gchar *tmp;
1853 struct sipe_watcher *watcher = watcher_find(sip, from);
1854 if (!ourtag) {
1855 tagadded = TRUE;
1856 ourtag = gentag();
1858 if (!watcher) { /* new subscription */
1859 gchar *acceptheader = sipmsg_find_header(msg, "Accept");
1860 gboolean needsxpidf = FALSE;
1861 if (!purple_privacy_check(sip->account, from)) {
1862 send_sip_response(sip->gc, msg, 202, "Ok", NULL);
1863 goto privend;
1865 if (acceptheader) {
1866 gchar *tmp = acceptheader;
1867 gboolean foundpidf = FALSE;
1868 gboolean foundxpidf = FALSE;
1869 while (tmp && tmp < acceptheader + strlen(acceptheader)) {
1870 gchar *tmp2 = strchr(tmp, ',');
1871 if (tmp2) *tmp2 = '\0';
1872 if (!strcmp("application/pidf+xml", tmp))
1873 foundpidf = TRUE;
1874 if (!strcmp("application/xpidf+xml", tmp))
1875 foundxpidf = TRUE;
1876 if (tmp2) {
1877 *tmp2 = ',';
1878 tmp = tmp2;
1879 while (*tmp == ' ') tmp++;
1880 } else
1881 tmp = 0;
1883 if (!foundpidf && foundxpidf) needsxpidf = TRUE;
1884 g_free(acceptheader);
1886 watcher = watcher_create(sip, from, callid, ourtag, theirtag, needsxpidf);
1888 if (tagadded) {
1889 gchar *to = g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg, "To"), ourtag);
1890 sipmsg_remove_header(msg, "To");
1891 sipmsg_add_header(msg, "To", to);
1892 g_free(to);
1894 if (expire)
1895 watcher->expire = time(NULL) + strtol(expire, NULL, 10);
1896 else
1897 watcher->expire = time(NULL) + 600;
1898 //Fixxxer
1899 sipmsg_remove_header(msg, "Contact");
1900 tmp = get_contact(sip);
1901 sipmsg_add_header(msg, "Contact", tmp);
1902 g_free(tmp);
1903 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);
1904 send_sip_response(sip->gc, msg, 200, "Ok", NULL);
1905 send_notify(sip, watcher);
1906 privend:
1907 g_free(from);
1908 g_free(theirtag);
1909 g_free(ourtag);
1910 g_free(callid);
1911 g_free(expire);
1914 static void process_input_message(struct sipe_account_data *sip, struct sipmsg *msg)
1916 gboolean found = FALSE;
1917 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg->response,msg->method);
1918 if (msg->response == 0) { /* request */
1919 if (!strcmp(msg->method, "MESSAGE")) {
1920 process_incoming_message(sip, msg);
1921 found = TRUE;
1922 } else if (!strcmp(msg->method, "NOTIFY")) {
1923 purple_debug_info("sipe","send->process_incoming_notify\n");
1924 process_incoming_notify(sip, msg);
1925 found = TRUE;
1926 } else if (!strcmp(msg->method, "SUBSCRIBE")) {
1927 purple_debug_info("sipe","send->process_incoming_subscribe\n");
1928 process_incoming_subscribe(sip, msg);
1929 found = TRUE;
1930 } else if (!strcmp(msg->method, "INVITE")) {
1931 process_incoming_invite(sip, msg);
1932 found = TRUE;
1933 } else if (!strcmp(msg->method, "INFO")) {
1934 // TODO needs work
1935 gchar * from = parse_from(sipmsg_find_header(msg, "From"));
1936 if (from) {
1937 serv_got_typing(sip->gc, from, 0, PURPLE_TYPING);
1939 printf("INFO body:\n%s\n", msg->body);
1940 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1941 found = TRUE;
1942 } else if (!strcmp(msg->method, "ACK")) {
1943 // ACK's don't need any response
1944 //send_sip_response(sip->gc, msg, 200, "OK", NULL);
1945 found = TRUE;
1946 } else if (!strcmp(msg->method, "BYE")) {
1947 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1949 gchar * from = parse_from(sipmsg_find_header(msg, "From"));
1950 struct sip_im_session * session = find_im_session (sip, from);
1951 g_free(from);
1953 if (session) {
1954 // TODO Let the user know the other user left the conversation?
1955 im_session_destroy(sip, session);
1958 found = TRUE;
1959 } else {
1960 send_sip_response(sip->gc, msg, 501, "Not implemented", NULL);
1962 } else { /* response */
1963 struct transaction *trans = transactions_find(sip, msg);
1964 if (trans) {
1965 if (msg->response == 407) {
1966 gchar *resend, *auth, *ptmp;
1968 if (sip->proxy.retries > 30) return;
1969 sip->proxy.retries++;
1970 /* do proxy authentication */
1972 ptmp = sipmsg_find_header(msg, "Proxy-Authenticate");
1974 fill_auth(sip, ptmp, &sip->proxy);
1975 auth = auth_header(sip, &sip->proxy, trans->msg, TRUE);
1976 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
1977 sipmsg_add_header_pos(trans->msg, "Proxy-Authorization", auth, 5);
1978 g_free(auth);
1979 resend = sipmsg_to_string(trans->msg);
1980 /* resend request */
1981 sendout_pkt(sip->gc, resend);
1982 g_free(resend);
1983 } else {
1984 if (msg->response == 100) {
1985 /* ignore provisional response */
1986 purple_debug_info("sipe", "got trying response\n");
1987 } else {
1988 sip->proxy.retries = 0;
1989 if (!strcmp(trans->msg->method, "REGISTER")) {
1990 if (msg->response == 401) sip->registrar.retries++;
1991 else sip->registrar.retries = 0;
1992 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip->cseq);
1993 } else {
1994 if (msg->response == 401) {
1995 gchar *resend, *auth, *ptmp;
1997 if (sip->registrar.retries > 4) return;
1998 sip->registrar.retries++;
2000 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
2001 ptmp = sipmsg_find_auth_header(msg, "NTLM");
2002 } else {
2003 ptmp = sipmsg_find_auth_header(msg, "Kerberos");
2006 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - Auth header: %s\r\n", ptmp);
2008 fill_auth(sip, ptmp, &sip->registrar);
2009 auth = auth_header(sip, &sip->registrar, trans->msg, TRUE);
2010 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
2011 sipmsg_add_header(trans->msg, "Proxy-Authorization", auth);
2013 //sipmsg_remove_header(trans->msg, "Authorization");
2014 //sipmsg_add_header(trans->msg, "Authorization", auth);
2015 g_free(auth);
2016 resend = sipmsg_to_string(trans->msg);
2017 /* resend request */
2018 sendout_pkt(sip->gc, resend);
2019 g_free(resend);
2022 if (trans->callback) {
2023 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - we have a transaction callback\r\n");
2024 /* call the callback to process response*/
2025 (trans->callback)(sip, msg, trans);
2027 /* Not sure if this is needed or what needs to be done
2028 but transactions seem to be removed prematurely so
2029 this only removes them if the response is 200 OK */
2030 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - removing CSeq %d\r\n", sip->cseq);
2031 /*Has a bug and it's unneccesary*/
2032 /*transactions_remove(sip, trans);*/
2036 found = TRUE;
2037 /* This is done because in an OCS2007 server trace the MS
2038 * Communicator client seems to reset the CSeq after an OK */
2040 //sip->cseq=1;
2041 } else {
2042 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received response to unknown transaction");
2045 if (!found) {
2046 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received a unknown sip message with method %s and response %d\n", msg->method, msg->response);
2050 static void process_input(struct sipe_account_data *sip, struct sip_connection *conn)
2052 char *cur;
2053 char *dummy;
2054 struct sipmsg *msg;
2055 int restlen;
2056 cur = conn->inbuf;
2058 /* according to the RFC remove CRLF at the beginning */
2059 while (*cur == '\r' || *cur == '\n') {
2060 cur++;
2062 if (cur != conn->inbuf) {
2063 memmove(conn->inbuf, cur, conn->inbufused - (cur - conn->inbuf));
2064 conn->inbufused = strlen(conn->inbuf);
2067 /* Received a full Header? */
2068 if ((cur = strstr(conn->inbuf, "\r\n\r\n")) != NULL) {
2069 time_t currtime = time(NULL);
2070 cur += 2;
2071 cur[0] = '\0';
2072 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), conn->inbuf);
2073 msg = sipmsg_parse_header(conn->inbuf);
2074 cur[0] = '\r';
2075 cur += 2;
2076 restlen = conn->inbufused - (cur - conn->inbuf);
2077 if (restlen >= msg->bodylen) {
2078 dummy = g_malloc(msg->bodylen + 1);
2079 memcpy(dummy, cur, msg->bodylen);
2080 dummy[msg->bodylen] = '\0';
2081 msg->body = dummy;
2082 cur += msg->bodylen;
2083 memmove(conn->inbuf, cur, conn->inbuflen - (cur - conn->inbuf));
2084 conn->inbufused = strlen(conn->inbuf);
2085 } else {
2086 sipmsg_free(msg);
2087 return;
2090 // Verify the signature before processing it
2091 if (sip->registrar.ntlm_key) {
2092 struct sipmsg_breakdown msgbd;
2093 msgbd.msg = msg;
2094 sipmsg_breakdown_parse(&msgbd, sip->registrar.realm, sip->registrar.target);
2095 gchar * signature_input_str = sipmsg_breakdown_get_string(&msgbd);
2096 gchar * signature;
2097 if (signature_input_str != NULL) {
2098 signature = purple_ntlm_sipe_signature_make (signature_input_str, sip->registrar.ntlm_key);
2101 gchar * rspauth = sipmsg_find_part_of_header(sipmsg_find_header(msg, "Authentication-Info"), "rspauth=\"", "\"", NULL);
2102 if (signature != NULL && rspauth != NULL) {
2103 if (purple_ntlm_verify_signature (signature, rspauth)) {
2104 purple_debug(PURPLE_DEBUG_MISC, "sipe", "incoming message's signature validated\n");
2105 process_input_message(sip, msg);
2106 } else {
2107 purple_debug(PURPLE_DEBUG_MISC, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth, signature);
2111 sipmsg_breakdown_free(&msgbd);
2112 } else {
2113 process_input_message(sip, msg);
2116 } else {
2117 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received a incomplete sip msg: %s\n", conn->inbuf);
2121 static void sipe_udp_process(gpointer data, gint source, PurpleInputCondition con)
2123 PurpleConnection *gc = data;
2124 struct sipe_account_data *sip = gc->proto_data;
2125 struct sipmsg *msg;
2126 int len;
2127 time_t currtime;
2129 static char buffer[65536];
2130 if ((len = recv(source, buffer, sizeof(buffer) - 1, 0)) > 0) {
2131 buffer[len] = '\0';
2132 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), buffer);
2133 msg = sipmsg_parse_msg(buffer);
2134 if (msg) process_input_message(sip, msg);
2138 static void sipe_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
2140 PurpleConnection *gc = data;
2141 struct sipe_account_data *sip = gc->proto_data;
2142 struct sip_connection *conn = NULL;
2143 int len;
2144 static char buf[4096];
2146 /* TODO: It should be possible to make this check unnecessary */
2147 if (!PURPLE_CONNECTION_IS_VALID(gc)) {
2148 if (gsc) purple_ssl_close(gsc);
2149 return;
2152 if (sip->gsc)
2153 conn = connection_find(sip, sip->gsc->fd);
2154 if (!conn) {
2155 purple_debug_error("sipe", "Connection not found!\n");
2156 if (sip->gsc) purple_ssl_close(sip->gsc);
2157 return;
2161 if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
2162 conn->inbuflen += SIMPLE_BUF_INC;
2163 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
2166 len = purple_ssl_read(gsc, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1);
2168 if (len < 0 && errno == EAGAIN) {
2169 /* Try again later */
2170 return;
2171 } else if (len < 0) {
2172 purple_debug_info("sipe", "sipe_input_cb_ssl: read error\n");
2173 if (sip->gsc){
2174 connection_remove(sip, sip->gsc->fd);
2175 if (sip->fd == gsc->fd) sip->fd = -1;
2177 return;
2178 } else if (len == 0) {
2179 purple_connection_error(gc, _("Server has disconnected"));
2180 if (sip->gsc){
2181 connection_remove(sip, sip->gsc->fd);
2182 if (sip->fd == gsc->fd) sip->fd = -1;
2184 return;
2187 conn->inbufused += len;
2188 conn->inbuf[conn->inbufused] = '\0';
2190 process_input(sip, conn);
2194 static void sipe_input_cb(gpointer data, gint source, PurpleInputCondition cond)
2196 PurpleConnection *gc = data;
2197 struct sipe_account_data *sip = gc->proto_data;
2198 int len;
2199 struct sip_connection *conn = connection_find(sip, source);
2200 if (!conn) {
2201 purple_debug_error("sipe", "Connection not found!\n");
2202 return;
2205 if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
2206 conn->inbuflen += SIMPLE_BUF_INC;
2207 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
2210 len = read(source, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1);
2212 if (len < 0 && errno == EAGAIN)
2213 return;
2214 else if (len <= 0) {
2215 purple_debug_info("sipe", "sipe_input_cb: read error\n");
2216 connection_remove(sip, source);
2217 if (sip->fd == source) sip->fd = -1;
2218 return;
2221 conn->inbufused += len;
2222 conn->inbuf[conn->inbufused] = '\0';
2224 process_input(sip, conn);
2227 /* Callback for new connections on incoming TCP port */
2228 static void sipe_newconn_cb(gpointer data, gint source, PurpleInputCondition cond)
2230 PurpleConnection *gc = data;
2231 struct sipe_account_data *sip = gc->proto_data;
2232 struct sip_connection *conn;
2234 int newfd = accept(source, NULL, NULL);
2236 conn = connection_create(sip, newfd);
2238 conn->inputhandler = purple_input_add(newfd, PURPLE_INPUT_READ, sipe_input_cb, gc);
2241 static void login_cb(gpointer data, gint source, const gchar *error_message)
2243 PurpleConnection *gc = data;
2244 struct sipe_account_data *sip;
2245 struct sip_connection *conn;
2247 if (!PURPLE_CONNECTION_IS_VALID(gc))
2249 if (source >= 0)
2250 close(source);
2251 return;
2254 if (source < 0) {
2255 purple_connection_error(gc, _("Could not connect"));
2256 return;
2259 sip = gc->proto_data;
2260 sip->fd = source;
2262 conn = connection_create(sip, source);
2264 sip->registertimeout = purple_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
2266 do_register(sip);
2268 conn->inputhandler = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_input_cb, gc);
2271 static void login_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
2273 PurpleConnection *gc = data;
2274 struct sipe_account_data *sip;
2275 struct sip_connection *conn;
2277 if (!PURPLE_CONNECTION_IS_VALID(gc))
2279 if (gsc) purple_ssl_close(gsc);
2280 return;
2283 sip = gc->proto_data;
2284 sip->fd = gsc->fd;
2285 conn = connection_create(sip, sip->fd);
2286 sip->listenport = purple_network_get_port_from_fd(sip->fd);
2287 sip->listenfd = sip->fd;
2288 sip->registertimeout = purple_timeout_add((rand()%100)+3*1000, (GSourceFunc)subscribe_timeout, sip);
2290 do_register(sip);
2292 purple_ssl_input_add(gsc, sipe_input_cb_ssl, gc);
2295 static guint sipe_ht_hash_nick(const char *nick)
2297 char *lc = g_utf8_strdown(nick, -1);
2298 guint bucket = g_str_hash(lc);
2299 g_free(lc);
2301 return bucket;
2304 static gboolean sipe_ht_equals_nick(const char *nick1, const char *nick2)
2306 return (purple_utf8_strcasecmp(nick1, nick2) == 0);
2309 static void sipe_udp_host_resolved_listen_cb(int listenfd, gpointer data)
2311 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2313 sip->listen_data = NULL;
2315 if (listenfd == -1) {
2316 purple_connection_error(sip->gc, _("Could not create listen socket"));
2317 return;
2320 sip->fd = listenfd;
2322 sip->listenport = purple_network_get_port_from_fd(sip->fd);
2323 sip->listenfd = sip->fd;
2325 sip->listenpa = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_udp_process, sip->gc);
2327 sip->resendtimeout = purple_timeout_add(2500, (GSourceFunc) resend_timeout, sip);
2328 sip->registertimeout = purple_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
2329 do_register(sip);
2332 static void sipe_udp_host_resolved(GSList *hosts, gpointer data, const char *error_message)
2334 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2335 int addr_size;
2337 sip->query_data = NULL;
2339 if (!hosts || !hosts->data) {
2340 purple_connection_error(sip->gc, _("Couldn't resolve host"));
2341 return;
2344 addr_size = GPOINTER_TO_INT(hosts->data);
2345 hosts = g_slist_remove(hosts, hosts->data);
2346 memcpy(&(sip->serveraddr), hosts->data, addr_size);
2347 g_free(hosts->data);
2348 hosts = g_slist_remove(hosts, hosts->data);
2349 while (hosts) {
2350 hosts = g_slist_remove(hosts, hosts->data);
2351 g_free(hosts->data);
2352 hosts = g_slist_remove(hosts, hosts->data);
2355 /* create socket for incoming connections */
2356 sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_DGRAM,
2357 sipe_udp_host_resolved_listen_cb, sip);
2358 if (sip->listen_data == NULL) {
2359 purple_connection_error(sip->gc, _("Could not create listen socket"));
2360 return;
2364 static void sipe_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
2365 gpointer data)
2367 PurpleConnection *gc = data;
2368 struct sipe_account_data *sip;
2370 /* If the connection is already disconnected, we don't need to do anything else */
2371 if (!PURPLE_CONNECTION_IS_VALID(gc))
2372 return;
2374 sip = gc->proto_data;
2375 sip->gsc = NULL;
2377 switch(error) {
2378 case PURPLE_SSL_CONNECT_FAILED:
2379 purple_connection_error(gc, _("Connection Failed"));
2380 break;
2381 case PURPLE_SSL_HANDSHAKE_FAILED:
2382 purple_connection_error(gc, _("SSL Handshake Failed"));
2383 break;
2387 static void
2388 sipe_tcp_connect_listen_cb(int listenfd, gpointer data)
2390 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2391 PurpleProxyConnectData *connect_data;
2393 sip->listen_data = NULL;
2395 sip->listenfd = listenfd;
2396 if (sip->listenfd == -1) {
2397 purple_connection_error(sip->gc, _("Could not create listen socket"));
2398 return;
2401 purple_debug_info("sipe", "listenfd: %d\n", sip->listenfd);
2402 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2403 sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2404 sip->listenpa = purple_input_add(sip->listenfd, PURPLE_INPUT_READ,
2405 sipe_newconn_cb, sip->gc);
2406 purple_debug_info("sipe", "connecting to %s port %d\n",
2407 sip->realhostname, sip->realport);
2408 /* open tcp connection to the server */
2409 connect_data = purple_proxy_connect(sip->gc, sip->account, sip->realhostname,
2410 sip->realport, login_cb, sip->gc);
2412 if (connect_data == NULL) {
2413 purple_connection_error(sip->gc, _("Couldn't create socket"));
2419 static void srvresolved(PurpleSrvResponse *resp, int results, gpointer data)
2421 struct sipe_account_data *sip;
2422 gchar *hostname;
2423 int port;
2425 sip = data;
2426 sip->srv_query_data = NULL;
2428 port = purple_account_get_int(sip->account, "port", 0);
2430 /* find the host to connect to */
2431 if (results) {
2432 hostname = g_strdup(resp->hostname);
2433 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - SRV hostname: %s\r\n", hostname);
2434 if (!port)
2435 port = resp->port;
2436 g_free(resp);
2437 } else {
2438 if (!purple_account_get_bool(sip->account, "useproxy", FALSE)) {
2439 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - using sipdomain\r\n");
2440 hostname = g_strdup(sip->sipdomain);
2441 } else {
2442 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - using specified SIP proxy\r\n");
2443 hostname = g_strdup(purple_account_get_string(sip->account, "proxy", sip->sipdomain));
2447 sip->realhostname = hostname;
2448 sip->realport = port;
2449 if (!sip->realport) sip->realport = 5060;
2451 /* TCP case */
2452 //if (!sip->udp) {
2453 // /* create socket for incoming connections */
2454 // sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_STREAM,
2455 // sipe_tcp_connect_listen_cb, sip);
2456 // if (sip->listen_data == NULL) {
2457 // purple_connection_error(sip->gc, _("Could not create listen socket"));
2458 // return;
2459 // }
2460 //} else { /* UDP */
2461 // purple_debug_info("sipe", "using udp with server %s and port %d\n", hostname, port);
2463 // sip->query_data = purple_dnsquery_a(hostname, port, sipe_udp_host_resolved, sip);
2464 // if (sip->query_data == NULL) {
2465 // purple_connection_error(sip->gc, _("Could not resolve hostname"));
2466 // }
2470 static void sipe_login(PurpleAccount *account)
2472 PurpleConnection *gc;
2473 struct sipe_account_data *sip;
2474 gchar **userserver;
2475 gchar *hosttoconnect;
2477 const char *username = purple_account_get_username(account);
2478 gc = purple_account_get_connection(account);
2480 if (strpbrk(username, " \t\v\r\n") != NULL) {
2481 gc->wants_to_die = TRUE;
2482 purple_connection_error(gc, _("SIP Exchange usernames may not contain whitespaces"));
2483 return;
2486 if (!purple_account_get_bool(account, "ssl", FALSE)){
2487 if (!purple_ssl_is_supported())
2489 gc->wants_to_die = TRUE;
2490 purple_connection_error(gc,
2491 _("SSL support is needed for SSL/TLS support. Please install a supported "
2492 "SSL library."));
2493 return;
2498 gc->proto_data = sip = g_new0(struct sipe_account_data, 1);
2499 sip->gc = gc;
2500 sip->account = account;
2501 sip->registerexpire = 900;
2502 sip->udp = purple_account_get_bool(account, "udp", FALSE);
2503 sip->use_ssl = purple_account_get_bool(account, "ssl", FALSE);
2505 purple_debug_info("sipe", "sip->use_ssl->%d\n", sip->use_ssl);
2507 /* TODO: is there a good default grow size? */
2508 if (!sip->udp)
2509 sip->txbuf = purple_circ_buffer_new(0);
2511 userserver = g_strsplit(username, "@", 2);
2512 purple_connection_set_display_name(gc, userserver[0]);
2513 sip->username = g_strdup(g_strjoin("@", userserver[0], userserver[1], NULL));
2514 sip->sipdomain = g_strdup(userserver[1]);
2515 sip->password = g_strdup(purple_connection_get_password(gc));
2516 g_strfreev(userserver);
2518 if (sip->use_ssl) {
2519 // Communicator queries _sipinternaltls._tcp.domain.com and uses that
2520 // information to connect to the OCS server.
2522 // XXX FIXME: eventually we should also query for sipexternaltls as well
2523 // if Pidgin is not on the local LAN
2524 // This doesn't quite work as advertised yet so make sure your have
2525 // your OCS FQDN in the proxy setting in the SIPE account settings
2527 sip->srv_query_data = purple_srv_resolve("sipinternaltls", "tcp", sip->sipdomain, srvresolved, sip);
2530 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_login - realhostname: %s\r\n", sip->realhostname);
2532 sip->buddies = g_hash_table_new((GHashFunc)sipe_ht_hash_nick, (GEqualFunc)sipe_ht_equals_nick);
2534 purple_connection_update_progress(gc, _("Connecting"), 1, 2);
2536 /* TODO: Set the status correctly. */
2537 sip->status = g_strdup("available");
2539 if (!purple_account_get_bool(account, "useproxy", FALSE)) {
2540 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_login - checking realhostname again: %s\r\n", sip->realhostname);
2541 hosttoconnect = g_strdup(sip->sipdomain);
2542 } else {
2543 hosttoconnect = g_strdup(purple_account_get_string(account, "proxy", sip->sipdomain));
2546 /*SSL*/
2547 purple_debug_info("sipe", "HosttoConnect->%s\n", hosttoconnect);
2549 if (sip->use_ssl){
2550 sip->gsc = purple_ssl_connect(account,hosttoconnect, purple_account_get_int(account, "port", 5061), login_cb_ssl, sipe_ssl_connect_failure, gc);
2552 else{
2553 sip->srv_query_data = purple_srv_resolve("sip",
2554 sip->udp ? "udp" : "tcp", hosttoconnect, srvresolved, sip);
2557 g_free(hosttoconnect);
2560 static void sipe_close(PurpleConnection *gc)
2562 struct sipe_account_data *sip = gc->proto_data;
2564 if (sip) {
2565 /* leave all conversations */
2566 im_session_close_all(sip);
2568 /* unregister */
2569 do_register_exp(sip, 0);
2570 connection_free_all(sip);
2572 if (sip->query_data != NULL)
2573 purple_dnsquery_destroy(sip->query_data);
2575 if (sip->srv_query_data != NULL)
2576 purple_srv_cancel(sip->srv_query_data);
2578 if (sip->listen_data != NULL)
2579 purple_network_listen_cancel(sip->listen_data);
2581 g_free(sip->sipdomain);
2582 g_free(sip->username);
2583 g_free(sip->password);
2584 g_free(sip->registrar.nonce);
2585 g_free(sip->registrar.opaque);
2586 g_free(sip->registrar.target);
2587 g_free(sip->registrar.realm);
2588 g_free(sip->registrar.digest_session_key);
2589 g_free(sip->proxy.nonce);
2590 g_free(sip->proxy.opaque);
2591 g_free(sip->proxy.target);
2592 g_free(sip->proxy.realm);
2593 g_free(sip->proxy.digest_session_key);
2594 if (sip->txbuf)
2595 purple_circ_buffer_destroy(sip->txbuf);
2596 g_free(sip->realhostname);
2597 if (sip->listenpa) purple_input_remove(sip->listenpa);
2598 if (sip->tx_handler) purple_input_remove(sip->tx_handler);
2599 if (sip->resendtimeout) purple_timeout_remove(sip->resendtimeout);
2600 if (sip->registertimeout) purple_timeout_remove(sip->registertimeout);
2602 g_free(gc->proto_data);
2603 gc->proto_data = NULL;
2606 /* not needed since privacy is checked for every subscribe */
2607 static void dummy_add_deny(PurpleConnection *gc, const char *name) {
2610 static void dummy_permit_deny(PurpleConnection *gc)
2614 static gboolean sipe_plugin_load(PurplePlugin *plugin)
2616 return TRUE;
2620 static gboolean sipe_plugin_unload(PurplePlugin *plugin)
2622 return TRUE;
2626 static void sipe_plugin_destroy(PurplePlugin *plugin)
2630 static PurplePlugin *my_protocol = NULL;
2632 static PurplePluginProtocolInfo prpl_info =
2635 NULL, /* user_splits */
2636 NULL, /* protocol_options */
2637 NO_BUDDY_ICONS, /* icon_spec */
2638 sipe_list_icon, /* list_icon */
2639 NULL, /* list_emblems */
2640 NULL, /* status_text */
2641 NULL, /* tooltip_text */
2642 sipe_status_types, /* away_states */
2643 NULL, /* blist_node_menu */
2644 NULL, /* chat_info */
2645 NULL, /* chat_info_defaults */
2646 sipe_login, /* login */
2647 sipe_close, /* close */
2648 sipe_im_send, /* send_im */
2649 NULL, /* set_info */
2650 // sipe_typing, /* send_typing */
2651 NULL, /* send_typing */
2652 NULL, /* get_info */
2653 sipe_set_status, /* set_status */
2654 NULL, /* set_idle */
2655 NULL, /* change_passwd */
2656 sipe_add_buddy, /* add_buddy */
2657 NULL, /* add_buddies */
2658 sipe_remove_buddy, /* remove_buddy */
2659 NULL, /* remove_buddies */
2660 dummy_add_deny, /* add_permit */
2661 dummy_add_deny, /* add_deny */
2662 dummy_add_deny, /* rem_permit */
2663 dummy_add_deny, /* rem_deny */
2664 dummy_permit_deny, /* set_permit_deny */
2665 NULL, /* join_chat */
2666 NULL, /* reject_chat */
2667 NULL, /* get_chat_name */
2668 NULL, /* chat_invite */
2669 NULL, /* chat_leave */
2670 NULL, /* chat_whisper */
2671 NULL, /* chat_send */
2672 sipe_keep_alive, /* keepalive */
2673 NULL, /* register_user */
2674 NULL, /* get_cb_info */
2675 NULL, /* get_cb_away */
2676 NULL, /* alias_buddy */
2677 NULL, /* group_buddy */
2678 NULL, /* rename_group */
2679 NULL, /* buddy_free */
2680 sipe_convo_closed, /* convo_closed */
2681 purple_normalize_nocase, /* normalize */
2682 NULL, /* set_buddy_icon */
2683 NULL, /* remove_group */
2684 NULL, /* get_cb_real_name */
2685 NULL, /* set_chat_topic */
2686 NULL, /* find_blist_chat */
2687 NULL, /* roomlist_get_list */
2688 NULL, /* roomlist_cancel */
2689 NULL, /* roomlist_expand_category */
2690 NULL, /* can_receive_file */
2691 NULL, /* send_file */
2692 NULL, /* new_xfer */
2693 NULL, /* offline_message */
2694 NULL, /* whiteboard_prpl_ops */
2695 sipe_send_raw, /* send_raw */
2699 static PurplePluginInfo info = {
2700 PURPLE_PLUGIN_MAGIC,
2701 PURPLE_MAJOR_VERSION,
2702 PURPLE_MINOR_VERSION,
2703 PURPLE_PLUGIN_PROTOCOL, /**< type */
2704 NULL, /**< ui_requirement */
2705 0, /**< flags */
2706 NULL, /**< dependencies */
2707 PURPLE_PRIORITY_DEFAULT, /**< priority */
2708 "prpl-sipe", /**< id */
2709 "Microsoft LCS/OCS", /**< name */
2710 VERSION, /**< version */
2711 N_("SIP/SIMPLE Exchange Protocol Plugin"), /** summary */
2712 N_("The SIP/SIMPLE Exchange Protocol Plugin"), /** description */
2713 "Anibal Avelar <avelar@gmail.com>", /**< author */
2714 PURPLE_WEBSITE, /**< homepage */
2715 sipe_plugin_load, /**< load */
2716 sipe_plugin_unload, /**< unload */
2717 sipe_plugin_destroy, /**< destroy */
2718 NULL, /**< ui_info */
2719 &prpl_info, /**< extra_info */
2720 NULL,
2721 NULL,
2722 NULL,
2723 NULL,
2724 NULL,
2725 NULL
2728 static void init_plugin(PurplePlugin *plugin)
2730 PurpleAccountUserSplit *split;
2731 PurpleAccountOption *option;
2733 purple_plugin_register(plugin);
2735 //split = purple_account_user_split_new(_("Server"), "", '@');
2736 //prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
2737 option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2738 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2739 option = purple_account_option_string_new(_("Proxy Server"), "proxy", "");
2740 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2742 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
2743 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2745 option = purple_account_option_bool_new(_("Use SSL/TLS"), "ssl", FALSE);
2746 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,option);
2748 option = purple_account_option_string_new(_("UserAgent"), "useragent", "Purple/" VERSION);
2749 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,option);
2751 option = purple_account_option_bool_new(_("Use UDP"), "udp", FALSE);
2752 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2754 option = purple_account_option_int_new(_("Connect port"), "port", 5060);
2755 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2757 option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
2758 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2760 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
2761 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
2762 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2764 /*option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2765 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2766 option = purple_account_option_string_new(_("Proxy"), "proxy", "");
2767 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2768 option = purple_account_option_string_new(_("Auth User"), "authuser", "");
2769 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2770 option = purple_account_option_string_new(_("Auth Domain"), "authdomain", "");
2771 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2772 my_protocol = plugin;
2776 /* I had to redefined the function for it load, but works */
2777 gboolean purple_init_plugin(PurplePlugin *plugin){
2778 plugin->info = &(info);
2779 init_plugin((plugin));
2780 sipe_plugin_load((plugin));
2781 return purple_plugin_register(plugin);