Fixed when 'activity' got back a garbage value
[siplcs.git] / src / sipe.c
blob33cd708a57418d1a26de8d0607fb971061ec045b
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 gchar *get_epid()
73 return sipe_uuid_get_macaddr();
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,struct sipmsg *msg);
130 static void do_notifies(struct sipe_account_data *sip)
132 GSList *tmp = sip->watcher;
133 purple_debug_info("sipe", "do_notifies()\n");
135 while (tmp) {
136 purple_debug_info("sipe", "notifying %s\n", ((struct sipe_watcher*)tmp->data)->name);
137 send_notify(sip, tmp->data);
138 tmp = tmp->next;
142 static void sipe_set_status(PurpleAccount *account, PurpleStatus *status)
144 PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_type(status));
145 struct sipe_account_data *sip = NULL;
147 if (!purple_status_is_active(status))
148 return;
150 if (account->gc)
151 sip = account->gc->proto_data;
153 if (sip)
155 g_free(sip->status);
156 if (primitive == PURPLE_STATUS_AWAY)
157 sip->status = g_strdup("away");
158 else if (primitive == PURPLE_STATUS_AVAILABLE)
159 sip->status = g_strdup("available");
160 else if (primitive == PURPLE_STATUS_UNAVAILABLE)
161 sip->status = g_strdup("busy");
163 do_notifies(sip);
167 static struct sip_connection *connection_find(struct sipe_account_data *sip, int fd)
169 struct sip_connection *ret = NULL;
170 GSList *entry = sip->openconns;
171 while (entry) {
172 ret = entry->data;
173 if (ret->fd == fd) return ret;
174 entry = entry->next;
176 return NULL;
179 static struct sipe_watcher *watcher_find(struct sipe_account_data *sip,
180 const gchar *name)
182 struct sipe_watcher *watcher;
183 GSList *entry = sip->watcher;
184 while (entry) {
185 watcher = entry->data;
186 if (!strcmp(name, watcher->name)) return watcher;
187 entry = entry->next;
189 return NULL;
192 static struct sipe_watcher *watcher_create(struct sipe_account_data *sip,
193 const gchar *name, const gchar *callid, const gchar *ourtag,
194 const gchar *theirtag, gboolean needsxpidf)
196 struct sipe_watcher *watcher = g_new0(struct sipe_watcher, 1);
197 watcher->name = g_strdup(name);
198 watcher->dialog.callid = g_strdup(callid);
199 watcher->dialog.ourtag = g_strdup(ourtag);
200 watcher->dialog.theirtag = g_strdup(theirtag);
201 watcher->needsxpidf = needsxpidf;
202 sip->watcher = g_slist_append(sip->watcher, watcher);
203 printf("\n\nADDING WATCHER for %s\n\n", name);
204 return watcher;
207 static void watcher_remove(struct sipe_account_data *sip, const gchar *name)
209 struct sipe_watcher *watcher = watcher_find(sip, name);
210 printf("\n\nREMOVING WATCHER for %s\n\n", name);
211 sip->watcher = g_slist_remove(sip->watcher, watcher);
212 g_free(watcher->name);
213 g_free(watcher->dialog.callid);
214 g_free(watcher->dialog.ourtag);
215 g_free(watcher->dialog.theirtag);
216 g_free(watcher);
219 static struct sip_connection *connection_create(struct sipe_account_data *sip, int fd)
221 struct sip_connection *ret = g_new0(struct sip_connection, 1);
222 ret->fd = fd;
223 sip->openconns = g_slist_append(sip->openconns, ret);
224 return ret;
227 static void connection_remove(struct sipe_account_data *sip, int fd)
229 struct sip_connection *conn = connection_find(sip, fd);
230 sip->openconns = g_slist_remove(sip->openconns, conn);
231 if (conn->inputhandler) purple_input_remove(conn->inputhandler);
232 g_free(conn->inbuf);
233 g_free(conn);
236 static void connection_free_all(struct sipe_account_data *sip)
238 struct sip_connection *ret = NULL;
239 GSList *entry = sip->openconns;
240 while (entry) {
241 ret = entry->data;
242 connection_remove(sip, ret->fd);
243 entry = sip->openconns;
247 static void sipe_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
249 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
250 struct sipe_buddy *b;
251 if (strncmp("sip:", buddy->name, 4)) {
252 gchar *buf = g_strdup_printf("sip:%s", buddy->name);
253 purple_blist_rename_buddy(buddy, buf);
254 g_free(buf);
256 if (!g_hash_table_lookup(sip->buddies, buddy->name)) {
257 b = g_new0(struct sipe_buddy, 1);
258 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy->name);
259 b->name = g_strdup(buddy->name);
260 g_hash_table_insert(sip->buddies, b->name, b);
261 } else {
262 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy->name);
266 static void sipe_get_buddies(PurpleConnection *gc)
268 PurpleBlistNode *gnode, *cnode, *bnode;
270 purple_debug_info("sipe", "sipe_get_buddies\n");
272 for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
273 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue;
274 for (cnode = gnode->child; cnode; cnode = cnode->next) {
275 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue;
276 for (bnode = cnode->child; bnode; bnode = bnode->next) {
277 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue;
278 if (((PurpleBuddy*)bnode)->account == gc->account)
279 sipe_add_buddy(gc, (PurpleBuddy*)bnode, (PurpleGroup *)gnode);
285 static void sipe_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
287 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
288 struct sipe_buddy *b = g_hash_table_lookup(sip->buddies, buddy->name);
289 g_hash_table_remove(sip->buddies, buddy->name);
290 g_free(b->name);
291 g_free(b);
294 static GList *sipe_status_types(PurpleAccount *acc)
296 PurpleStatusType *type;
297 GList *types = NULL;
299 // Available
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 // Away
307 type = purple_status_type_new_with_attrs(
308 PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
309 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
310 NULL);
311 types = g_list_append(types, type);
313 // Busy
314 type = purple_status_type_new_with_attrs(
315 PURPLE_STATUS_UNAVAILABLE, "busy", _("Busy"), TRUE, TRUE, FALSE,
316 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
317 NULL);
318 types = g_list_append(types, type);
320 // Offline
321 type = purple_status_type_new_full(
322 PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE);
323 types = g_list_append(types, type);
325 return types;
328 //static struct sipe_krb5_auth krb5_auth;
329 static gchar *auth_header_without_newline(struct sipe_account_data *sip, struct sip_auth *auth, struct sipmsg * msg, gboolean force_reauth)
331 const gchar *method = msg->method;
332 const gchar *target = msg->target;
333 gchar noncecount[9];
334 gchar *response;
335 gchar *ret;
336 gchar *tmp;
337 const char *authdomain;
338 const char *authuser;
339 const char *krb5_realm;
340 const char *host;
341 gchar *krb5_token = NULL;
343 authdomain = purple_account_get_string(sip->account, "authdomain", "");
344 authuser = purple_account_get_string(sip->account, "authuser", sip->username);
346 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
347 // and do error checking
349 // KRB realm should always be uppercase
350 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
352 if (sip->realhostname) {
353 host = sip->realhostname;
354 } else if (purple_account_get_bool(sip->account, "use_proxy", TRUE)) {
355 host = purple_account_get_string(sip->account, "proxy", "");
356 } else {
357 host = sip->sipdomain;
360 /*gboolean new_auth = krb5_auth.gss_context == NULL;
361 if (new_auth) {
362 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
365 if (new_auth || force_reauth) {
366 krb5_token = krb5_auth.base64_token;
369 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
370 krb5_token = krb5_auth.base64_token;*/
372 if (!authuser || strlen(authuser) < 1) {
373 authuser = sip->username;
376 if (auth->type == 1) { /* Digest */
377 sprintf(noncecount, "%08d", auth->nc++);
378 response = purple_cipher_http_digest_calculate_response(
379 "md5", method, target, NULL, NULL,
380 auth->nonce, noncecount, NULL, auth->digest_session_key);
381 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response %s\n", response);
383 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);
384 g_free(response);
385 return ret;
386 } else if (auth->type == 2) { /* NTLM */
387 // If we have a signature for the message, include that
388 if (msg->signature) {
389 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);
390 return tmp;
393 if (auth->nc == 3 && auth->nonce && auth->ntlm_key == NULL) {
394 /* TODO: Don't hardcode "purple" as the hostname */
395 const gchar * ntlm_key;
396 gchar * gssapi_data = purple_ntlm_gen_authenticate(&ntlm_key, authuser, sip->password, "purple", authdomain, (const guint8 *)auth->nonce, &auth->flags);
397 auth->ntlm_key = (gchar *)ntlm_key;
398 tmp = g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth->opaque, auth->realm, auth->target, gssapi_data);
399 g_free(gssapi_data);
400 return tmp;
403 tmp = g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth->realm, auth->target);
404 return tmp;
405 } else if (auth->type == 3) {
406 /* Kerberos */
407 if (auth->nc == 3) {
408 /*if (new_auth || force_reauth) {
409 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
410 if (auth->opaque) {
411 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);
412 } else {
413 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
415 } else {
416 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
417 gchar * mic = "MICTODO";
418 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
419 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
420 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
421 //auth->opaque ? auth->opaque : "", auth->target);
422 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
423 //g_free(mic);
425 return tmp;
427 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth->target);
430 sprintf(noncecount, "%08d", auth->nc++);
431 response = purple_cipher_http_digest_calculate_response(
432 "md5", method, target, NULL, NULL,
433 auth->nonce, noncecount, NULL, auth->digest_session_key);
434 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response %s\n", response);
436 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);
437 g_free(response);
438 return ret;
441 static gchar *auth_header(struct sipe_account_data *sip, struct sip_auth *auth, struct sipmsg * msg, gboolean force_reauth)
443 gchar *with, *without;
445 without = auth_header_without_newline(sip, auth, msg, force_reauth);
446 with = g_strdup_printf("%s\r\n", without);
447 g_free (without);
448 return with;
452 static char *parse_attribute(const char *attrname, const char *source)
454 const char *tmp, *tmp2;
455 char *retval = NULL;
456 int len = strlen(attrname);
458 if (!strncmp(source, attrname, len)) {
459 tmp = source + len;
460 tmp2 = g_strstr_len(tmp, strlen(tmp), "\"");
461 if (tmp2)
462 retval = g_strndup(tmp, tmp2 - tmp);
463 else
464 retval = g_strdup(tmp);
467 return retval;
470 static void fill_auth(struct sipe_account_data *sip, gchar *hdr, struct sip_auth *auth)
472 int i = 0;
473 const char *authuser;
474 char *tmp;
475 gchar **parts;
476 const char *krb5_realm;
477 const char *host;
479 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
480 // and do error checking
482 // KRB realm should always be uppercase
483 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
485 if (sip->realhostname) {
486 host = sip->realhostname;
487 } else if (purple_account_get_bool(sip->account, "use_proxy", TRUE)) {
488 host = purple_account_get_string(sip->account, "proxy", "");
489 } else {
490 host = sip->sipdomain;
493 authuser = purple_account_get_string(sip->account, "authuser", sip->username);
495 if (!authuser || strlen(authuser) < 1) {
496 authuser = sip->username;
499 if (!hdr) {
500 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
501 return;
504 if (!g_strncasecmp(hdr, "NTLM", 4)) {
505 auth->type = 2;
506 parts = g_strsplit(hdr+5, "\", ", 0);
507 i = 0;
508 while (parts[i]) {
509 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
510 if ((tmp = parse_attribute("gssapi-data=\"", parts[i]))) {
511 auth->nonce = g_memdup(purple_ntlm_parse_challenge(tmp, &auth->flags), 8);
512 g_free(tmp);
514 if ((tmp = parse_attribute("targetname=\"",
515 parts[i]))) {
516 auth->target = tmp;
518 else if ((tmp = parse_attribute("realm=\"",
519 parts[i]))) {
520 auth->realm = tmp;
522 else if ((tmp = parse_attribute("opaque=\"", parts[i]))) {
523 auth->opaque = tmp;
525 i++;
527 g_strfreev(parts);
528 auth->nc = 1;
529 if (!strstr(hdr, "gssapi-data")) {
530 auth->nc = 1;
531 } else {
532 auth->nc = 3;
534 return;
537 if (!g_strncasecmp(hdr, "Kerberos", 8)) {
538 purple_debug(PURPLE_DEBUG_MISC, "sipe", "setting auth type to Kerberos (3)\r\n");
539 auth->type = 3;
540 purple_debug(PURPLE_DEBUG_MISC, "sipe", "fill_auth - header: %s\r\n", hdr);
541 parts = g_strsplit(hdr+9, "\", ", 0);
542 i = 0;
543 while (parts[i]) {
544 purple_debug_info("sipe", "krb - parts[i] %s\n", parts[i]);
545 if ((tmp = parse_attribute("gssapi-data=\"", parts[i]))) {
546 /*if (krb5_auth.gss_context == NULL) {
547 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
549 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
550 g_free(tmp);
552 if ((tmp = parse_attribute("targetname=\"", parts[i]))) {
553 auth->target = tmp;
554 } else if ((tmp = parse_attribute("realm=\"", parts[i]))) {
555 auth->realm = tmp;
556 } else if ((tmp = parse_attribute("opaque=\"", parts[i]))) {
557 auth->opaque = tmp;
559 i++;
561 g_strfreev(parts);
562 auth->nc = 3;
563 //if (!strstr(hdr, "gssapi-data")) {
564 // auth->nc = 1;
565 //} else {
566 // auth->nc = 3;
568 return;
571 auth->type = 1;
572 parts = g_strsplit(hdr, " ", 0);
573 while (parts[i]) {
574 if ((tmp = parse_attribute("nonce=\"", parts[i]))) {
575 auth->nonce = tmp;
577 else if ((tmp = parse_attribute("realm=\"", parts[i]))) {
578 auth->realm = tmp;
580 i++;
582 g_strfreev(parts);
584 purple_debug(PURPLE_DEBUG_MISC, "sipe", "nonce: %s realm: %s\n", auth->nonce ? auth->nonce : "(null)", auth->realm ? auth->realm : "(null)");
585 if (auth->realm) {
586 auth->digest_session_key = purple_cipher_http_digest_calculate_session_key(
587 "md5", authuser, auth->realm, sip->password, auth->nonce, NULL);
589 auth->nc = 1;
593 static void sipe_canwrite_cb(gpointer data, gint source, PurpleInputCondition cond)
595 PurpleConnection *gc = data;
596 struct sipe_account_data *sip = gc->proto_data;
597 gsize max_write;
598 gssize written;
600 max_write = purple_circ_buffer_get_max_read(sip->txbuf);
602 if (max_write == 0) {
603 purple_input_remove(sip->tx_handler);
604 sip->tx_handler = 0;
605 return;
608 written = write(sip->fd, sip->txbuf->outptr, max_write);
610 if (written < 0 && errno == EAGAIN)
611 written = 0;
612 else if (written <= 0) {
613 /*TODO: do we really want to disconnect on a failure to write?*/
614 purple_connection_error(gc, _("Could not write"));
615 return;
618 purple_circ_buffer_mark_read(sip->txbuf, written);
621 static void sipe_canwrite_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
623 PurpleConnection *gc = data;
624 struct sipe_account_data *sip = gc->proto_data;
625 gsize max_write;
626 gssize written;
628 max_write = purple_circ_buffer_get_max_read(sip->txbuf);
630 if (max_write == 0) {
631 purple_input_remove(sip->tx_handler);
632 sip->tx_handler = 0;
633 return;
636 written = purple_ssl_write(sip->gsc, sip->txbuf->outptr, max_write);
638 if (written < 0 && errno == EAGAIN)
639 written = 0;
640 else if (written <= 0) {
641 /*TODO: do we really want to disconnect on a failure to write?*/
642 purple_connection_error(gc, _("Could not write"));
643 return;
646 purple_circ_buffer_mark_read(sip->txbuf, written);
649 static void sipe_input_cb(gpointer data, gint source, PurpleInputCondition cond);
651 static void send_later_cb(gpointer data, gint source, const gchar *error)
653 PurpleConnection *gc = data;
654 struct sipe_account_data *sip;
655 struct sip_connection *conn;
657 if (!PURPLE_CONNECTION_IS_VALID(gc))
659 if (source >= 0)
660 close(source);
661 return;
664 if (source < 0) {
665 purple_connection_error(gc, _("Could not connect"));
666 return;
669 sip = gc->proto_data;
670 sip->fd = source;
671 sip->connecting = FALSE;
673 sipe_canwrite_cb(gc, sip->fd, PURPLE_INPUT_WRITE);
675 /* If there is more to write now, we need to register a handler */
676 if (sip->txbuf->bufused > 0)
677 sip->tx_handler = purple_input_add(sip->fd, PURPLE_INPUT_WRITE,
678 sipe_canwrite_cb, gc);
680 conn = connection_create(sip, source);
681 conn->inputhandler = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_input_cb, gc);
684 static void send_later_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
686 PurpleConnection *gc = data;
687 struct sipe_account_data *sip;
688 struct sip_connection *conn;
690 if (!PURPLE_CONNECTION_IS_VALID(gc))
692 if(gsc) purple_ssl_close(gsc);
693 return;
696 sip = gc->proto_data;
697 sip->fd = gsc->fd;
698 sip->connecting = FALSE;
700 sipe_canwrite_cb_ssl(gc, gsc, PURPLE_INPUT_WRITE);
702 /* If there is more to write now, we need to register a handler */
703 if (sip->txbuf->bufused > 0)
704 purple_ssl_input_add(gsc, sipe_canwrite_cb_ssl, gc);
706 conn = connection_create(sip, gsc->fd);
707 purple_ssl_input_add(sip->gsc, sipe_input_cb_ssl, gc);
711 static void sendlater(PurpleConnection *gc, const char *buf)
713 struct sipe_account_data *sip = gc->proto_data;
715 if (!sip->connecting) {
716 purple_debug_info("sipe", "connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport);
717 if (sip->use_ssl){
718 sip->gsc = purple_ssl_connect(sip->account,sip->realhostname, sip->realport, send_later_cb_ssl, sipe_ssl_connect_failure, sip->gc);
720 else{
721 if (purple_proxy_connect(gc, sip->account, sip->realhostname, sip->realport, send_later_cb, gc) == NULL) {
722 purple_connection_error(gc, _("Couldn't create socket"));
725 sip->connecting = TRUE;
728 if (purple_circ_buffer_get_max_read(sip->txbuf) > 0)
729 purple_circ_buffer_append(sip->txbuf, "\r\n", 2);
731 purple_circ_buffer_append(sip->txbuf, buf, strlen(buf));
734 static void sendout_pkt(PurpleConnection *gc, const char *buf)
736 struct sipe_account_data *sip = gc->proto_data;
737 time_t currtime = time(NULL);
738 int writelen = strlen(buf);
740 purple_debug(PURPLE_DEBUG_MISC, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime), buf);
741 if (sip->udp) {
742 if (sendto(sip->fd, buf, writelen, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)) < writelen) {
743 purple_debug_info("sipe", "could not send packet\n");
745 } else {
746 int ret;
747 if (sip->fd < 0) {
748 sendlater(gc, buf);
749 return;
752 if (sip->tx_handler) {
753 ret = -1;
754 errno = EAGAIN;
755 } else{
756 if (sip->gsc){
757 ret = purple_ssl_write(sip->gsc, buf, writelen);
758 }else{
759 ret = write(sip->fd, buf, writelen);
763 if (ret < 0 && errno == EAGAIN)
764 ret = 0;
765 else if (ret <= 0) { /* XXX: When does this happen legitimately? */
766 sendlater(gc, buf);
767 return;
770 if (ret < writelen) {
771 if (!sip->tx_handler){
772 if (sip->gsc){
773 purple_ssl_input_add(sip->gsc, sipe_canwrite_cb_ssl, gc);
775 else{
776 sip->tx_handler = purple_input_add(sip->fd,
777 PURPLE_INPUT_WRITE, sipe_canwrite_cb,
778 gc);
782 /* XXX: is it OK to do this? You might get part of a request sent
783 with part of another. */
784 if (sip->txbuf->bufused > 0)
785 purple_circ_buffer_append(sip->txbuf, "\r\n", 2);
787 purple_circ_buffer_append(sip->txbuf, buf + ret,
788 writelen - ret);
793 static int sipe_send_raw(PurpleConnection *gc, const char *buf, int len)
795 sendout_pkt(gc, buf);
796 return len;
799 static void sendout_sipmsg(struct sipe_account_data *sip, struct sipmsg *msg)
801 GSList *tmp = msg->headers;
802 gchar *name;
803 gchar *value;
804 GString *outstr = g_string_new("");
805 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n", msg->method, msg->target);
806 while (tmp) {
807 name = ((struct siphdrelement*) (tmp->data))->name;
808 value = ((struct siphdrelement*) (tmp->data))->value;
809 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
810 tmp = g_slist_next(tmp);
812 g_string_append_printf(outstr, "\r\n%s", msg->body ? msg->body : "");
813 sendout_pkt(sip->gc, outstr->str);
814 g_string_free(outstr, TRUE);
817 static void sign_outgoing_message (struct sipmsg * msg, struct sipe_account_data *sip, const gchar *method)
819 gchar * buf;
820 if (sip->registrar.ntlm_key) {
821 struct sipmsg_breakdown msgbd;
822 msgbd.msg = msg;
823 sipmsg_breakdown_parse(&msgbd, sip->registrar.realm, sip->registrar.target);
824 // TODO generate this
825 msgbd.rand = g_strdup("0878F41B");
826 sip->registrar.ntlm_num++;
827 msgbd.num = g_strdup_printf("%d", sip->registrar.ntlm_num);
828 gchar * signature_input_str = sipmsg_breakdown_get_string(&msgbd);
829 if (signature_input_str != NULL) {
830 msg->signature = purple_ntlm_sipe_signature_make (signature_input_str, sip->registrar.ntlm_key);
831 msg->rand = g_strdup(msgbd.rand);
832 msg->num = g_strdup(msgbd.num);
834 sipmsg_breakdown_free(&msgbd);
837 if (sip->registrar.type && !strcmp(method, "REGISTER")) {
838 buf = auth_header_without_newline(sip, &sip->registrar, msg, FALSE);
839 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
840 sipmsg_add_header(msg, "Authorization", buf);
841 } else {
842 sipmsg_add_header_pos(msg, "Proxy-Authorization", buf, 5);
843 //sipmsg_add_header_pos(msg, "Authorization", buf, 5);
845 g_free(buf);
846 } 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")) {
847 sip->registrar.nc=3;
848 sip->registrar.type=2;
850 buf = auth_header_without_newline(sip, &sip->registrar, msg, FALSE);
851 //buf = auth_header(sip, &sip->proxy, msg, FALSE);
852 sipmsg_add_header_pos(msg, "Proxy-Authorization", buf, 5);
853 //sipmsg_add_header(msg, "Authorization", buf);
854 g_free(buf);
855 } else {
856 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method);
860 static char *get_contact(struct sipe_account_data *sip)
862 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");
866 static char *get_contact_service(struct sipe_account_data *sip)
868 return g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip->listenport, sip->use_ssl ? "tls" : sip->udp ? "udp" : "tcp",generateUUIDfromEPID(get_epid()));
869 //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");
872 static void send_sip_response(PurpleConnection *gc, struct sipmsg *msg, int code,
873 const char *text, const char *body)
875 GSList *tmp = msg->headers;
876 gchar *name;
877 gchar *value;
878 GString *outstr = g_string_new("");
879 struct sipe_account_data *sip = gc->proto_data;
881 gchar *contact;
882 contact = get_contact(sip);
883 sipmsg_remove_header(msg, "Contact");
884 sipmsg_add_header(msg, "Contact", contact);
885 g_free(contact);
887 /* When sending the acknowlegements and errors, the content length from the original
888 message is still here, but there is no body; we need to make sure we're sending the
889 correct content length */
890 sipmsg_remove_header(msg, "Content-Length");
891 if (body) {
892 gchar len[12];
893 sprintf(len, "%" G_GSIZE_FORMAT , strlen(body));
894 sipmsg_add_header(msg, "Content-Length", len);
895 } else {
896 sipmsg_add_header(msg, "Content-Length", "0");
899 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
900 //gchar * mic = "MICTODO";
901 msg->response = code;
903 sipmsg_remove_header(msg, "Authentication-Info");
904 sign_outgoing_message(msg, sip, msg->method);
906 g_string_append_printf(outstr, "SIP/2.0 %d %s\r\n", code, text);
907 while (tmp) {
908 name = ((struct siphdrelement*) (tmp->data))->name;
909 value = ((struct siphdrelement*) (tmp->data))->value;
911 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
912 tmp = g_slist_next(tmp);
914 g_string_append_printf(outstr, "\r\n%s", body ? body : "");
915 sendout_pkt(gc, outstr->str);
916 g_string_free(outstr, TRUE);
919 static void transactions_remove(struct sipe_account_data *sip, struct transaction *trans)
921 if (trans->msg) sipmsg_free(trans->msg);
922 sip->transactions = g_slist_remove(sip->transactions, trans);
923 g_free(trans);
926 static struct transaction *
927 transactions_add_buf(struct sipe_account_data *sip, const struct sipmsg *msg, void *callback)
929 struct transaction *trans = g_new0(struct transaction, 1);
930 trans->time = time(NULL);
931 trans->msg = (struct sipmsg *)msg;
932 trans->cseq = sipmsg_find_header(trans->msg, "CSeq");
933 trans->callback = callback;
934 sip->transactions = g_slist_append(sip->transactions, trans);
935 return trans;
938 static struct transaction *transactions_find(struct sipe_account_data *sip, struct sipmsg *msg)
940 struct transaction *trans;
941 GSList *transactions = sip->transactions;
942 gchar *cseq = sipmsg_find_header(msg, "CSeq");
944 while (transactions) {
945 trans = transactions->data;
946 if (!strcmp(trans->cseq, cseq)) {
947 return trans;
949 transactions = transactions->next;
952 return NULL;
956 static struct transaction *
957 send_sip_request(PurpleConnection *gc, const gchar *method,
958 const gchar *url, const gchar *to, const gchar *addheaders,
959 const gchar *body, struct sip_dialog *dialog, TransCallback tc)
961 struct sipe_account_data *sip = gc->proto_data;
962 const char *addh = "";
963 char *buf;
964 struct sipmsg *msg;
965 gchar *ptmp;
966 gchar *ourtag = dialog && dialog->ourtag ? g_strdup(dialog->ourtag) : gentag();
967 gchar *theirtag = dialog && dialog->theirtag ? g_strdup(dialog->theirtag) : NULL;
968 gchar *theirepid = dialog && dialog->theirepid ? g_strdup(dialog->theirepid) : NULL;
969 gchar *callid = dialog && dialog->callid ? g_strdup(dialog->callid) : gencallid();
970 gchar *branch = dialog && dialog->callid ? NULL : genbranch();
971 gchar *useragent = purple_account_get_string(sip->account, "useragent", "Purple/" VERSION);
972 if (!strcmp(method, "REGISTER")) {
973 if (sip->regcallid) {
974 g_free(callid);
975 callid = g_strdup(sip->regcallid);
976 } else {
977 sip->regcallid = g_strdup(callid);
981 if (addheaders) addh = addheaders;
983 buf = g_strdup_printf("%s %s SIP/2.0\r\n"
984 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
985 "From: <sip:%s>;tag=%s;epid=%s\r\n"
986 "To: <%s>%s%s%s%s\r\n"
987 "Max-Forwards: 70\r\n"
988 "CSeq: %d %s\r\n"
989 "User-Agent: %s\r\n"
990 "Call-ID: %s\r\n"
991 "%s%s"
992 "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n%s",
993 method,
994 dialog && dialog->request ? dialog->request : url,
995 sip->use_ssl ? "TLS" : sip->udp ? "UDP" : "TCP",
996 purple_network_get_my_ip(-1),
997 sip->listenport,
998 branch ? ";branch=" : "",
999 branch ? branch : "",
1000 sip->username,
1001 ourtag ? ourtag : "",
1002 get_epid(), // TODO generate one per account/login
1004 theirtag ? ";tag=" : "",
1005 theirtag ? theirtag : "",
1006 theirepid ? ";epid=" : "",
1007 theirepid ? theirepid : "",
1008 dialog ? ++dialog->cseq : ++sip->cseq,
1009 method,
1010 useragent,
1011 callid,
1012 dialog && dialog->route ? dialog->route : "",
1013 addh,
1014 body ? strlen(body) : 0,
1015 body ? body : "");
1018 //printf ("parsing msg buf:\n%s\n\n", buf);
1019 msg = sipmsg_parse_msg(buf);
1021 g_free(buf);
1022 g_free(ourtag);
1023 g_free(theirtag);
1024 g_free(theirepid);
1025 g_free(branch);
1026 g_free(callid);
1028 sign_outgoing_message (msg, sip, method);
1030 buf = sipmsg_to_string (msg);
1032 /* add to ongoing transactions */
1033 struct transaction * trans = transactions_add_buf(sip, msg, tc);
1034 sendout_pkt(gc, buf);
1036 return trans;
1039 static char *get_contact_register(struct sipe_account_data *sip)
1041 return g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, BYE, CANCEL, NOTIFY, ACK, BENOTIFY\";proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip->listenport, sip->use_ssl ? "tls" : sip->udp ? "udp" : "tcp",generateUUIDfromEPID(get_epid()));
1044 static void do_register_exp(struct sipe_account_data *sip, int expire)
1046 char *uri = g_strdup_printf("sip:%s", sip->sipdomain);
1047 char *to = g_strdup_printf("sip:%s", sip->username);
1048 char *contact = get_contact_register(sip);
1049 //char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire);
1050 // 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);
1051 //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);
1052 char *hdr = g_strdup_printf("Contact: %s\r\nSupported: com.microsoft.msrtc.presence, gruu-10, adhoclist\r\nEvent: registration\r\nAllow-Events: presence\r\nms-keep-alive: UAC;hop-hop=yes\r\nExpires: %d\r\n", contact,expire);
1053 g_free(contact);
1055 sip->registerstatus = 1;
1057 if (expire) {
1058 sip->reregister = time(NULL) + expire - 50;
1059 } else {
1060 sip->reregister = time(NULL) + 600;
1063 send_sip_request(sip->gc, "REGISTER", uri, to, hdr, "", NULL,
1064 process_register_response);
1066 g_free(hdr);
1067 g_free(uri);
1068 g_free(to);
1071 static void do_register(struct sipe_account_data *sip)
1073 do_register_exp(sip, sip->registerexpire);
1076 static gchar *parse_from(const gchar *hdr)
1078 gchar *from;
1079 const gchar *tmp, *tmp2 = hdr;
1081 if (!hdr) return NULL;
1082 purple_debug_info("sipe", "parsing address out of %s\n", hdr);
1083 tmp = strchr(hdr, '<');
1085 /* i hate the different SIP UA behaviours... */
1086 if (tmp) { /* sip address in <...> */
1087 tmp2 = tmp + 1;
1088 tmp = strchr(tmp2, '>');
1089 if (tmp) {
1090 from = g_strndup(tmp2, tmp - tmp2);
1091 } else {
1092 purple_debug_info("sipe", "found < without > in From\n");
1093 return NULL;
1095 } else {
1096 tmp = strchr(tmp2, ';');
1097 if (tmp) {
1098 from = g_strndup(tmp2, tmp - tmp2);
1099 } else {
1100 from = g_strdup(tmp2);
1103 purple_debug_info("sipe", "got %s\n", from);
1104 return from;
1107 static gboolean process_subscribe_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1109 gchar *to;
1111 if (msg->response == 200 || msg->response == 202) {
1112 return TRUE;
1115 to = parse_from(sipmsg_find_header(tc->msg, "To")); /* cant be NULL since it is our own msg */
1117 /* we can not subscribe -> user is offline (TODO unknown status?) */
1119 purple_prpl_got_user_status(sip->account, to, "offline", NULL);
1120 g_free(to);
1121 return TRUE;
1124 static void sipe_subscribe_to_name(struct sipe_account_data *sip, const char * buddy_name)
1126 gchar *to = strstr(buddy_name, "sip:") ? g_strdup(buddy_name) : g_strdup_printf("sip:%s", buddy_name);
1127 gchar *tmp = get_contact(sip);
1128 gchar *contact = g_strdup_printf(
1129 "Accept: application/pidf+xml, application/xpidf+xml\r\n"
1130 "Event: presence\r\n"
1131 "Contact: %s\r\n", tmp);
1132 g_free(tmp);
1134 /* subscribe to buddy presence */
1135 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL, process_subscribe_response);
1137 g_free(to);
1138 g_free(contact);
1141 static void sipe_subscribe(struct sipe_account_data *sip, struct sipe_buddy *buddy)
1143 sipe_subscribe_to_name(sip, buddy->name);
1145 /* resubscribe before subscription expires */
1146 /* add some jitter */
1147 buddy->resubscribe = time(NULL)+1140+(rand()%50);
1150 static gboolean sipe_add_lcs_contacts(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1152 gchar *tmp;
1153 xmlnode *item, *group, *isc;
1154 const char *name_group, *group_id;
1155 PurpleBuddy *b;
1156 PurpleGroup *g = NULL;
1157 gchar **parts;
1158 gchar *apn;
1159 int ng = 0, i;
1160 struct sipe_buddy *bs;
1161 struct sipe_group *gr;
1162 int len = msg->bodylen;
1164 // Reserved to max 10 groups. TODO be dynamic
1165 gr = g_new0(struct sipe_group, 10);
1167 tmp = sipmsg_find_header(msg, "Event");
1168 if (tmp && !strncmp(tmp, "vnd-microsoft-roaming-contacts", 30)) {
1169 purple_debug_info("sipe", "sipe_add_lcs_contacts->%s-%d\n", msg->body, len);
1170 /*Convert the contact from XML to Purple Buddies*/
1171 isc = xmlnode_from_str(msg->body, len);
1173 /* TODO Find for all groups */
1174 for (group = xmlnode_get_child(isc, "group"); group; group = xmlnode_get_next_twin(group)) {
1175 name_group = xmlnode_get_attrib(group, "name");
1176 group_id = xmlnode_get_attrib(group, "id");
1178 if (!strncmp(name_group, "~", 1)){
1179 name_group=g_strdup("General");
1182 gr[ng].name_group = g_strdup(name_group);
1183 gr[ng].id = g_strdup(group_id);
1184 purple_debug_info("sipe", "name_group->%s\n", name_group);
1185 g = purple_find_group(name_group);
1187 if (!g) {
1188 g = purple_group_new(name_group);
1189 purple_blist_add_group(g, NULL);
1192 if (!g) {
1193 g = purple_find_group("General");
1194 if (!g) {
1195 g = purple_group_new("General");
1196 purple_blist_add_group(g, NULL);
1200 gr[ng].g = g;
1201 ng++;
1204 for (i = 0; i < ng;i++) {
1205 purple_debug_info("sipe", "id->%s\n", gr[i].id);
1206 purple_debug_info("sipe", "id->%s\n", gr[i].name_group);
1209 for (item = xmlnode_get_child(isc, "contact"); item; item = xmlnode_get_next_twin(item)) {
1210 const char *uri, *name, *groups;
1211 char *buddy_name;
1212 i = 0;
1213 uri = xmlnode_get_attrib(item, "uri");
1214 name = xmlnode_get_attrib(item, "name");
1215 groups = xmlnode_get_attrib(item, "groups");
1216 parts = g_strsplit(groups, " ", 0);
1217 purple_debug_info("sipe", "URI->%s,Groups->%s\n", uri, groups);
1218 if (parts[i]!=NULL){
1219 while (parts[i]) {
1220 purple_debug_info("sipe", "Groups->parts[i] %s\n", parts[i]);
1221 if (!strcmp(gr[i].id,parts[i])){
1222 purple_debug_info("sipe", "Found Groups->gr[i].id(%s),gr[i].name_group (%s)\n",gr[i].id,gr[i].name_group);
1224 buddy_name = g_strdup_printf("sip:%s", uri);
1226 //b = purple_find_buddy(sip->account, buddy_name);
1227 b = purple_find_buddy_in_group(sip->account, buddy_name, gr[i].g);
1228 if (!b){
1229 b = purple_buddy_new(sip->account, buddy_name, uri);
1231 g_free(buddy_name);
1233 //sipe_add_buddy(sip->gc, b , gr[i].g);
1234 purple_blist_add_buddy(b, NULL, gr[i].g, NULL);
1235 purple_blist_alias_buddy(b, uri);
1236 bs = g_new0(struct sipe_buddy, 1);
1237 bs->name = g_strdup(b->name);
1238 g_hash_table_insert(sip->buddies, bs->name, bs);
1240 i++;
1245 xmlnode_free(isc);
1247 return 0;
1250 static void sipe_subscribe_buddylist(struct sipe_account_data *sip)
1252 gchar *to = g_strdup_printf("sip:%s", sip->username);
1253 gchar *tmp = get_contact(sip);
1254 gchar *hdr = g_strdup_printf("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\nContact: %s\r\n", tmp);
1255 g_free(tmp);
1257 send_sip_request(sip->gc, "SUBSCRIBE", to, to, hdr, "", NULL, sipe_add_lcs_contacts);
1258 g_free(to);
1259 g_free(hdr);
1262 /* IM Session (INVITE and MESSAGE methods) */
1264 static struct sip_im_session * find_im_session (struct sipe_account_data *sip, const char *who)
1266 if (sip == NULL || who == NULL) {
1267 return NULL;
1270 struct sip_im_session *session;
1271 GSList *entry = sip->im_sessions;
1272 while (entry) {
1273 session = entry->data;
1274 if ((who != NULL && !strcmp(who, session->with))) {
1275 return session;
1277 entry = entry->next;
1279 return NULL;
1282 static struct sip_im_session * find_or_create_im_session (struct sipe_account_data *sip, const char *who)
1284 struct sip_im_session *session = find_im_session(sip, who);
1285 if (!session) {
1286 session = g_new0(struct sip_im_session, 1);
1287 session->with = g_strdup(who);
1288 sip->im_sessions = g_slist_append(sip->im_sessions, session);
1290 return session;
1293 static void im_session_destroy(struct sipe_account_data *sip, struct sip_im_session * session)
1295 sip->im_sessions = g_slist_remove(sip->im_sessions, session);
1296 // TODO free session resources
1299 static void sipe_send_message(struct sipe_account_data *sip, struct sip_im_session * session, const char *msg)
1301 gchar *hdr;
1302 gchar *fullto;
1303 gchar *tmp;
1305 if (strncmp("sip:", session->with, 4)) {
1306 fullto = g_strdup_printf("sip:%s", session->with);
1307 } else {
1308 fullto = g_strdup(session->with);
1311 hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1312 //hdr = g_strdup("Content-Type: text/rtf\r\n");
1313 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
1315 tmp = get_contact(sip);
1316 hdr = g_strdup_printf("Contact: %s\r\n%s", tmp, hdr);
1317 g_free(tmp);
1319 send_sip_request(sip->gc, "MESSAGE", fullto, fullto, hdr, msg, session->outgoing_dialog, NULL);
1321 g_free(hdr);
1322 g_free(fullto);
1326 static void
1327 sipe_im_process_queue (struct sipe_account_data * sip, struct sip_im_session * session)
1329 GSList *entry = session->outgoing_message_queue;
1330 while (entry) {
1331 char *queued_msg = entry->data;
1332 sipe_send_message(sip, session, queued_msg);
1334 // Remove from the queue and free the string
1335 entry = session->outgoing_message_queue = g_slist_remove(session->outgoing_message_queue, queued_msg);
1336 g_free(queued_msg);
1340 static gboolean
1341 process_invite_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *trans)
1343 gchar * with = parse_from(sipmsg_find_header(msg, "To"));
1344 struct sip_im_session * session = find_im_session(sip, with);
1345 g_free(with);
1347 if (!session) {
1348 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
1349 return FALSE;
1352 if (msg->response != 200) {
1353 purple_debug_info("sipe", "process_invite_response: INVITE response not 200, ignoring\n");
1354 im_session_destroy(sip, session);
1355 return FALSE;
1358 struct sip_dialog * dialog = session->outgoing_dialog;
1359 if (!dialog) {
1360 purple_debug_info("sipe", "process_invite_response: session outgoign dialog is NULL\n");
1361 return FALSE;
1364 dialog->callid = sipmsg_find_header(msg, "Call-ID");
1365 dialog->ourtag = find_tag(sipmsg_find_header(msg, "From"));
1366 dialog->theirtag = find_tag(sipmsg_find_header(msg, "To"));
1367 if (!dialog->theirepid) {
1368 dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, "To"), "epid=", ";", NULL);
1370 if (!dialog->theirepid) {
1371 dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, "To"), "epid=", NULL, NULL);
1374 dialog->request = sipmsg_find_part_of_header(sipmsg_find_header(msg, "Record-Route"), "<", ">", NULL);
1375 dialog->route = g_strdup_printf("Route: %s\r\n", sipmsg_find_header(msg, "Contact"));
1376 dialog->cseq = 0;
1378 send_sip_request(sip->gc, "ACK", session->with, session->with, NULL, NULL, dialog, NULL);
1380 session->outgoing_invite = NULL;
1382 sipe_im_process_queue(sip, session);
1384 return TRUE;
1388 static void sipe_invite(struct sipe_account_data *sip, struct sip_im_session * session)
1390 gchar *hdr;
1391 gchar *to;
1392 gchar *contact;
1393 gchar *body;
1395 if (strstr(session->with, "sip:")) {
1396 to = g_strdup(session->with);
1397 } else {
1398 to = g_strdup_printf("sip:%s", session->with);
1401 // Setup the outgoing dialog w/ the epid from the incoming dialog (if any)
1402 struct sip_dialog * dialog = g_new0(struct sip_dialog, 1);
1403 if (session->incoming_dialog) {
1404 printf("incoming dialog epid is %s\n", session->incoming_dialog->theirepid);
1405 dialog->theirepid = session->incoming_dialog->theirepid;
1406 } else {
1407 printf("incoming dialog is NULL\n");
1409 session->outgoing_dialog = dialog;
1411 contact = get_contact(sip);
1412 hdr = g_strdup_printf(
1413 "Contact: %s\r\n"
1414 //"Supported: ms-conf-invite\r\n"
1415 //"Supported: ms-delayed-accept\r\n"
1416 //"Supported: ms-renders-isf\r\n"
1417 //"Supported: ms-renders-gif\r\n"
1418 //"Supported: ms-renders-mime-alternative\r\n"*/
1419 //"Supported: timer\r\n"
1420 //"Supported: ms-sender\r\n"
1421 //"Supported: ms-early-media\r\n"
1422 "Content-Type: application/sdp\r\n",
1423 contact, sip->username, sip->username, to);
1425 body = g_strdup_printf(
1426 "v=0\r\n"
1427 "o=- 0 0 IN IP4 %s\r\n"
1428 "s=session\r\n"
1429 "c=IN IP4 %s\r\n"
1430 "t=0 0\r\n"
1431 "m=message %d sip null\r\n"
1432 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1433 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), 5061);
1435 session->outgoing_invite = send_sip_request(sip->gc, "INVITE",
1436 to, to, hdr, body, session->outgoing_dialog, process_invite_response);
1438 g_free(to);
1439 g_free(body);
1440 g_free(hdr);
1441 g_free(contact);
1444 static void
1445 im_session_close (struct sipe_account_data *sip, struct sip_im_session * session)
1447 if (session) {
1448 send_sip_request(sip->gc, "BYE", session->with, session->with, NULL, NULL, session->outgoing_dialog, NULL);
1449 im_session_destroy(sip, session);
1453 static void
1454 sipe_convo_closed(PurpleConnection * gc, const char *who)
1456 struct sipe_account_data *sip = gc->proto_data;
1458 purple_debug_info("sipe", "conversation with %s closed\n", who);
1459 im_session_close(sip, find_im_session(sip, who));
1462 static void
1463 im_session_close_all (struct sipe_account_data *sip)
1465 GSList *entry = sip->im_sessions;
1466 while (entry) {
1467 im_session_close (sip, entry->data);
1468 entry = sip->im_sessions;
1472 static int sipe_im_send(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags)
1474 struct sipe_account_data *sip = gc->proto_data;
1475 char *to = g_strdup(who);
1476 char *text = purple_unescape_html(what);
1478 struct sip_im_session * session = find_or_create_im_session(sip, who);
1480 // Queue the message
1481 session->outgoing_message_queue = g_slist_append(session->outgoing_message_queue, text);
1483 if (session->outgoing_dialog && session->outgoing_dialog->callid) {
1484 sipe_im_process_queue(sip, session);
1485 } else if (!session->outgoing_invite) {
1486 // Need to send the INVITE to get the outgoing dialog setup
1487 sipe_invite(sip, session);
1490 g_free(to);
1491 return 1;
1495 /* End IM Session (INVITE and MESSAGE methods) */
1497 static void sipe_buddy_resub(char *name, struct sipe_buddy *buddy, struct sipe_account_data *sip)
1499 time_t curtime = time(NULL);
1500 if (buddy->resubscribe < curtime) {
1501 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_buddy_resub %s\n", name);
1502 sipe_subscribe(sip, buddy);
1506 static gboolean resend_timeout(struct sipe_account_data *sip)
1508 GSList *tmp = sip->transactions;
1509 time_t currtime = time(NULL);
1510 while (tmp) {
1511 struct transaction *trans = tmp->data;
1512 tmp = tmp->next;
1513 purple_debug_info("sipe", "have open transaction age: %d\n", currtime- trans->time);
1514 if ((currtime - trans->time > 5) && trans->retries >= 1) {
1515 /* TODO 408 */
1516 } else {
1517 if ((currtime - trans->time > 2) && trans->retries == 0) {
1518 trans->retries++;
1519 sendout_sipmsg(sip, trans->msg);
1523 return TRUE;
1526 static gboolean subscribe_timeout(struct sipe_account_data *sip)
1528 GSList *tmp;
1529 time_t curtime = time(NULL);
1530 /* register again if first registration expires */
1531 if (sip->reregister < curtime) {
1532 do_register(sip);
1534 /* check for every subscription if we need to resubscribe */
1535 //Fixxxer we need resub?
1536 g_hash_table_foreach(sip->buddies, (GHFunc)sipe_buddy_resub, (gpointer)sip);
1538 /* remove a timed out suscriber */
1540 tmp = sip->watcher;
1541 while (tmp) {
1542 struct sipe_watcher *watcher = tmp->data;
1543 if (watcher->expire < curtime) {
1544 watcher_remove(sip, watcher->name);
1545 tmp = sip->watcher;
1547 if (tmp) tmp = tmp->next;
1550 return TRUE;
1553 static void process_incoming_message(struct sipe_account_data *sip, struct sipmsg *msg)
1555 gchar *from;
1556 gchar *contenttype;
1557 gboolean found = FALSE;
1559 from = parse_from(sipmsg_find_header(msg, "From"));
1561 if (!from) return;
1563 purple_debug_info("sipe", "got message from %s: %s\n", from, msg->body);
1565 contenttype = sipmsg_find_header(msg, "Content-Type");
1566 if (!contenttype || !strncmp(contenttype, "text/plain", 10) || !strncmp(contenttype, "text/html", 9)) {
1567 serv_got_im(sip->gc, from, msg->body, 0, time(NULL));
1568 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1569 found = TRUE;
1571 if (!strncmp(contenttype, "application/im-iscomposing+xml", 30)) {
1572 xmlnode *isc = xmlnode_from_str(msg->body, msg->bodylen);
1573 xmlnode *state;
1574 gchar *statedata;
1576 if (!isc) {
1577 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1578 return;
1581 state = xmlnode_get_child(isc, "state");
1583 if (!state) {
1584 purple_debug_info("sipe", "process_incoming_message: no state found\n");
1585 xmlnode_free(isc);
1586 return;
1589 statedata = xmlnode_get_data(state);
1590 if (statedata) {
1591 if (strstr(statedata, "active")) serv_got_typing(sip->gc, from, 0, PURPLE_TYPING);
1592 else serv_got_typing_stopped(sip->gc, from);
1594 g_free(statedata);
1596 xmlnode_free(isc);
1597 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1598 found = TRUE;
1600 if (!found) {
1601 purple_debug_info("sipe", "got unknown mime-type");
1602 send_sip_response(sip->gc, msg, 415, "Unsupported media type", NULL);
1604 g_free(from);
1607 static void process_incoming_invite(struct sipe_account_data *sip, struct sipmsg *msg)
1609 gchar * from = sipmsg_find_part_of_header(sipmsg_find_header(msg, "From"), "<", ">", NULL);
1610 struct sip_im_session * session = find_or_create_im_session (sip, from);
1611 if (session) {
1612 struct sip_dialog * dialog = g_new0(struct sip_dialog, 1);
1613 dialog->callid = sipmsg_find_header(msg, "Call-ID");
1614 dialog->ourtag = find_tag(sipmsg_find_header(msg, "To"));
1615 dialog->theirtag = find_tag(sipmsg_find_header(msg, "From"));
1616 dialog->theirepid = sipmsg_find_part_of_header(sipmsg_find_header(msg, "From"), "epid=", NULL, NULL);
1617 printf("Created incoming dialog and set epid to %s\n", dialog->theirepid);
1619 session->incoming_dialog = dialog;
1620 } else {
1621 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
1623 g_free(from);
1625 send_sip_response(sip->gc, msg, 200, "OK", g_strdup_printf(
1626 "v=0\r\n"
1627 "o=- 0 0 IN IP4 %s\r\n"
1628 "s=session\r\n"
1629 "c=IN IP4 %s\r\n"
1630 "t=0 0\r\n"
1631 "m=message %d sip sip:%s\r\n"
1632 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1633 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
1634 //sip->realport, sip->username
1635 5061, sip->username));
1638 gboolean process_register_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1640 gchar *tmp, krb5_token;
1641 const gchar *expires_header;
1642 int expires;
1644 expires_header = sipmsg_find_header(msg, "Expires");
1645 expires = expires_header != NULL ? strtol(expires_header, NULL, 10) : 0;
1646 purple_debug_info("sipe", "got response to REGISTER; expires = %d\n", expires);
1648 switch (msg->response) {
1649 case 200:
1650 if (expires == 0) {
1651 sip->registerstatus = 0;
1652 } else {
1653 sip->registerstatus = 3;
1654 purple_connection_set_state(sip->gc, PURPLE_CONNECTED);
1656 /* tell everybody we're online */
1657 send_publish (sip,msg);
1659 /* get buddies from blist; Has a bug */
1660 /*sipe_get_buddies(sip->gc);*/
1661 subscribe_timeout(sip);
1663 //sipe_subscribe_to_name(sip, sip->username);
1665 tmp = sipmsg_find_header(msg, "Allow-Events");
1666 if (tmp && strstr(tmp, "vnd-microsoft-provisioning")){
1667 sipe_subscribe_buddylist(sip);
1670 // Should we remove the transaction here?
1671 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip->cseq);
1672 transactions_remove(sip, tc);
1674 break;
1675 case 401:
1676 if (sip->registerstatus != 2) {
1677 purple_debug_info("sipe", "REGISTER retries %d\n", sip->registrar.retries);
1678 if (sip->registrar.retries > 3) {
1679 sip->gc->wants_to_die = TRUE;
1680 purple_connection_error(sip->gc, _("Wrong Password"));
1681 return TRUE;
1683 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
1684 tmp = sipmsg_find_auth_header(msg, "NTLM");
1685 } else {
1686 tmp = sipmsg_find_auth_header(msg, "Kerberos");
1688 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_register_response - Auth header: %s\r\n", tmp);
1689 fill_auth(sip, tmp, &sip->registrar);
1690 sip->registerstatus = 2;
1691 if (sip->account->disconnecting) {
1692 do_register_exp(sip, 0);
1693 } else {
1694 do_register(sip);
1697 break;
1699 return TRUE;
1702 static void process_incoming_notify(struct sipe_account_data *sip, struct sipmsg *msg)
1704 gchar *from;
1705 gchar *fromhdr;
1706 gchar *tmp2;
1707 gchar *activity = g_strdup("available");
1708 xmlnode *pidf;
1709 xmlnode *basicstatus = NULL, *tuple, *status;
1710 gboolean isonline = FALSE;
1712 fromhdr = sipmsg_find_header(msg, "From");
1713 from = parse_from(fromhdr);
1715 if (!from) {
1716 return;
1719 pidf = xmlnode_from_str(msg->body, msg->bodylen);
1720 if (!pidf) {
1721 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
1722 return;
1725 purple_debug_info("sipe", "process_incoming_notify: body(%s)\n",msg->body);
1727 if ((tuple = xmlnode_get_child(pidf, "tuple"))) {
1728 if ((status = xmlnode_get_child(tuple, "status"))) {
1729 basicstatus = xmlnode_get_child(status, "basic");
1733 if (!basicstatus) {
1734 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
1735 xmlnode_free(pidf);
1736 return;
1739 tmp2 = xmlnode_get_data(basicstatus);
1740 if (!tmp2) {
1741 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
1742 xmlnode_free(pidf);
1743 return;
1746 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", tmp2);
1747 if (strstr(tmp2, "open")) {
1748 isonline = TRUE;
1751 if ((tuple = xmlnode_get_child(pidf, "tuple"))) {
1752 if ((status = xmlnode_get_child(tuple, "status"))) {
1753 if (basicstatus = xmlnode_get_child(status, "activities")) {
1754 if (basicstatus = xmlnode_get_child(basicstatus, "activity")) {
1755 activity = xmlnode_get_data(basicstatus);
1760 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity);
1762 if (isonline) {
1763 gchar * status_id;
1764 if (activity) {
1765 if (strstr(activity, "busy")) {
1766 status_id = "busy";
1767 } else if (strstr(activity, "away")) {
1768 status_id = "away";
1770 else{
1771 status_id = "available";
1775 if (!status_id) {
1776 status_id = "available";
1779 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id);
1780 purple_prpl_got_user_status(sip->account, from, status_id, NULL);
1781 } else {
1782 purple_prpl_got_user_status(sip->account, from, "offline", NULL);
1785 xmlnode_free(pidf);
1786 g_free(from);
1787 g_free(tmp2);
1788 g_free(activity);
1790 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1794 static gchar* gen_xpidf(struct sipe_account_data *sip)
1796 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1797 "<presence>\r\n"
1798 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
1799 "<display name=\"sip:%s\"/>\r\n"
1800 "<atom id=\"1234\">\r\n"
1801 "<address uri=\"sip:%s\">\r\n"
1802 "<status status=\"%s\"/>\r\n"
1803 "</address>\r\n"
1804 "</atom>\r\n"
1805 "</presence>\r\n",
1806 sip->username,
1807 sip->username,
1808 sip->username,
1809 sip->status);
1810 return doc;
1815 static gchar* gen_pidf(struct sipe_account_data *sip)
1817 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1818 "<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"
1819 "<tuple id=\"0\">\r\n"
1820 "<status>\r\n"
1821 "<basic>open</basic>\r\n"
1822 "<ep:activities>\r\n"
1823 " <ep:activity>%s</ep:activity>\r\n"
1824 "</ep:activities>"
1825 "</status>\r\n"
1826 "</tuple>\r\n"
1827 "<ci:display-name>%s</ci:display-name>\r\n"
1828 "</presence>",
1829 sip->username,
1830 sip->status,
1831 sip->username);
1832 return doc;
1835 static void send_notify(struct sipe_account_data *sip, struct sipe_watcher *watcher)
1837 gchar *doc = watcher->needsxpidf ? gen_xpidf(sip) : gen_pidf(sip);
1838 gchar *hdr = watcher->needsxpidf ? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
1839 send_sip_request(sip->gc, "NOTIFY", watcher->name, watcher->name, hdr, doc, &watcher->dialog, NULL);
1840 g_free(doc);
1843 static gboolean process_service_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1845 if (msg->response != 200 && msg->response != 408) {
1846 /* never send again */
1847 sip->republish = -1;
1849 return TRUE;
1852 static void send_publish(struct sipe_account_data *sip, struct sipmsg *msg)
1854 gchar *uri = g_strdup_printf("sip:%s", sip->username);
1855 gchar *doc = g_strdup_printf(
1856 "<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>",
1857 uri, generateUUIDfromEPID(get_epid()), uri,
1858 "00:00:00-05:00", // TODO timezone
1859 "PC" // TODO machine name
1862 gchar *tmp = get_contact_service(sip);
1864 gchar *hdr = g_strdup_printf("Contact: %s\r\nAccept: application/ms-location-profile-definition+xml\r\nContent-Type: application/msrtc-category-publish+xml\r\n", tmp);
1865 g_free(tmp);
1867 send_sip_request(sip->gc, "SERVICE", uri, uri, hdr, doc, NULL, process_service_response);
1868 //sip->republish = time(NULL) + 500;
1869 g_free(hdr);
1870 g_free(uri);
1871 g_free(doc);
1874 static void send_service(struct sipe_account_data *sip)
1876 //gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->sipdomain);
1877 gchar *uri = g_strdup_printf("sip:%s", sip->username);
1878 //gchar *doc = gen_pidf(sip);
1880 gchar *doc = gen_pidf(sip);
1881 gchar *hdr = g_strdup("Event: presence\r\nContent-Type: application/pidf+xml\r\n");
1883 //gchar *hdr = g_strdup("Content-Type: application/SOAP+xml\r\n");
1884 gchar *tmp = get_contact(sip);
1885 hdr = g_strdup_printf("Contact: %s\r\n%s; +sip.instance=\"<urn:uuid:%s>\"", tmp, hdr,generateUUIDfromEPID(get_epid()));
1886 g_free(tmp);
1887 send_sip_request(sip->gc, "SERVICE", uri, uri,
1888 hdr,
1889 doc, NULL, process_service_response);
1890 sip->republish = time(NULL) + 500;
1891 g_free(hdr);
1892 g_free(uri);
1893 g_free(doc);
1896 static void process_incoming_subscribe(struct sipe_account_data *sip, struct sipmsg *msg)
1898 const char *from_hdr = sipmsg_find_header(msg, "From");
1899 gchar *from = parse_from(from_hdr);
1900 gchar *theirtag = find_tag(from_hdr);
1901 gchar *ourtag = find_tag(sipmsg_find_header(msg, "To"));
1902 gboolean tagadded = FALSE;
1903 gchar *callid = sipmsg_find_header(msg, "Call-ID");
1904 gchar *expire = sipmsg_find_header(msg, "Expire");
1905 // gchar *ms-received-port =find_received_port(sipmsg_find_header(msg, "Contact"));
1906 gchar *tmp;
1907 struct sipe_watcher *watcher = watcher_find(sip, from);
1908 if (!ourtag) {
1909 tagadded = TRUE;
1910 ourtag = gentag();
1912 if (!watcher) { /* new subscription */
1913 gchar *acceptheader = sipmsg_find_header(msg, "Accept");
1914 gboolean needsxpidf = FALSE;
1915 if (!purple_privacy_check(sip->account, from)) {
1916 send_sip_response(sip->gc, msg, 202, "Ok", NULL);
1917 goto privend;
1919 if (acceptheader) {
1920 gchar *tmp = acceptheader;
1921 gboolean foundpidf = FALSE;
1922 gboolean foundxpidf = FALSE;
1923 while (tmp && tmp < acceptheader + strlen(acceptheader)) {
1924 gchar *tmp2 = strchr(tmp, ',');
1925 if (tmp2) *tmp2 = '\0';
1926 if (!strcmp("application/pidf+xml", tmp))
1927 foundpidf = TRUE;
1928 if (!strcmp("application/xpidf+xml", tmp))
1929 foundxpidf = TRUE;
1930 if (tmp2) {
1931 *tmp2 = ',';
1932 tmp = tmp2;
1933 while (*tmp == ' ') tmp++;
1934 } else
1935 tmp = 0;
1937 if (!foundpidf && foundxpidf) needsxpidf = TRUE;
1938 g_free(acceptheader);
1940 watcher = watcher_create(sip, from, callid, ourtag, theirtag, needsxpidf);
1942 if (tagadded) {
1943 gchar *to = g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg, "To"), ourtag);
1944 sipmsg_remove_header(msg, "To");
1945 sipmsg_add_header(msg, "To", to);
1946 g_free(to);
1948 if (expire)
1949 watcher->expire = time(NULL) + strtol(expire, NULL, 10);
1950 else
1951 watcher->expire = time(NULL) + 600;
1952 //Fixxxer
1953 sipmsg_remove_header(msg, "Contact");
1954 tmp = get_contact(sip);
1955 sipmsg_add_header(msg, "Contact", tmp);
1956 g_free(tmp);
1957 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);
1958 send_sip_response(sip->gc, msg, 200, "Ok", NULL);
1959 send_notify(sip, watcher);
1960 privend:
1961 g_free(from);
1962 g_free(theirtag);
1963 g_free(ourtag);
1964 g_free(callid);
1965 g_free(expire);
1968 static void process_input_message(struct sipe_account_data *sip,struct sipmsg *msg)
1970 gboolean found = FALSE;
1971 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg->response,msg->method);
1972 if (msg->response == 0) { /* request */
1973 if (!strcmp(msg->method, "MESSAGE")) {
1974 process_incoming_message(sip, msg);
1975 found = TRUE;
1976 } else if (!strcmp(msg->method, "NOTIFY")) {
1977 purple_debug_info("sipe","send->process_incoming_notify\n");
1978 process_incoming_notify(sip, msg);
1979 found = TRUE;
1980 } else if (!strcmp(msg->method, "SUBSCRIBE")) {
1981 purple_debug_info("sipe","send->process_incoming_subscribe\n");
1982 process_incoming_subscribe(sip, msg);
1983 found = TRUE;
1984 } else if (!strcmp(msg->method, "INVITE")) {
1985 process_incoming_invite(sip, msg);
1986 found = TRUE;
1987 } else if (!strcmp(msg->method, "INFO")) {
1988 // TODO needs work
1989 gchar * from = parse_from(sipmsg_find_header(msg, "From"));
1990 if (from) {
1991 serv_got_typing(sip->gc, from, 0, PURPLE_TYPING);
1993 printf("INFO body:\n%s\n", msg->body);
1994 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1995 found = TRUE;
1996 } else if (!strcmp(msg->method, "ACK")) {
1997 // ACK's don't need any response
1998 //send_sip_response(sip->gc, msg, 200, "OK", NULL);
1999 found = TRUE;
2000 } else if (!strcmp(msg->method, "BYE")) {
2001 send_sip_response(sip->gc, msg, 200, "OK", NULL);
2003 gchar * from = parse_from(sipmsg_find_header(msg, "From"));
2004 struct sip_im_session * session = find_im_session (sip, from);
2005 g_free(from);
2007 if (session) {
2008 // TODO Let the user know the other user left the conversation?
2009 im_session_destroy(sip, session);
2012 found = TRUE;
2013 } else {
2014 send_sip_response(sip->gc, msg, 501, "Not implemented", NULL);
2016 } else { /* response */
2017 struct transaction *trans = transactions_find(sip, msg);
2018 if (trans) {
2019 if (msg->response == 407) {
2020 gchar *resend, *auth, *ptmp;
2022 if (sip->proxy.retries > 30) return;
2023 sip->proxy.retries++;
2024 /* do proxy authentication */
2026 ptmp = sipmsg_find_header(msg, "Proxy-Authenticate");
2028 fill_auth(sip, ptmp, &sip->proxy);
2029 auth = auth_header(sip, &sip->proxy, trans->msg, TRUE);
2030 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
2031 sipmsg_add_header_pos(trans->msg, "Proxy-Authorization", auth, 5);
2032 g_free(auth);
2033 resend = sipmsg_to_string(trans->msg);
2034 /* resend request */
2035 sendout_pkt(sip->gc, resend);
2036 g_free(resend);
2037 } else {
2038 if (msg->response == 100) {
2039 /* ignore provisional response */
2040 purple_debug_info("sipe", "got trying response\n");
2041 } else {
2042 sip->proxy.retries = 0;
2043 if (!strcmp(trans->msg->method, "REGISTER")) {
2044 if (msg->response == 401) sip->registrar.retries++;
2045 else sip->registrar.retries = 0;
2046 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip->cseq);
2047 } else {
2048 if (msg->response == 401) {
2049 gchar *resend, *auth, *ptmp;
2051 if (sip->registrar.retries > 4) return;
2052 sip->registrar.retries++;
2054 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
2055 ptmp = sipmsg_find_auth_header(msg, "NTLM");
2056 } else {
2057 ptmp = sipmsg_find_auth_header(msg, "Kerberos");
2060 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - Auth header: %s\r\n", ptmp);
2062 fill_auth(sip, ptmp, &sip->registrar);
2063 auth = auth_header(sip, &sip->registrar, trans->msg, TRUE);
2064 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
2065 sipmsg_add_header(trans->msg, "Proxy-Authorization", auth);
2067 //sipmsg_remove_header(trans->msg, "Authorization");
2068 //sipmsg_add_header(trans->msg, "Authorization", auth);
2069 g_free(auth);
2070 resend = sipmsg_to_string(trans->msg);
2071 /* resend request */
2072 sendout_pkt(sip->gc, resend);
2073 g_free(resend);
2077 gchar *contact = sipmsg_find_header(msg, "Contact");
2078 purple_debug(PURPLE_DEBUG_MISC, "sipe", "Get Server Contact Header:%s\r\n",contact);
2079 msg->contact = contact;
2082 if (trans->callback) {
2083 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - we have a transaction callback\r\n");
2084 /* call the callback to process response*/
2085 (trans->callback)(sip, msg, trans);
2087 /* Not sure if this is needed or what needs to be done
2088 but transactions seem to be removed prematurely so
2089 this only removes them if the response is 200 OK */
2090 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - removing CSeq %d\r\n", sip->cseq);
2091 /*Has a bug and it's unneccesary*/
2092 /*transactions_remove(sip, trans);*/
2096 found = TRUE;
2097 } else {
2098 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received response to unknown transaction");
2101 if (!found) {
2102 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received a unknown sip message with method %s and response %d\n", msg->method, msg->response);
2106 static void process_input(struct sipe_account_data *sip, struct sip_connection *conn)
2108 char *cur;
2109 char *dummy;
2110 struct sipmsg *msg;
2111 int restlen;
2112 cur = conn->inbuf;
2114 /* according to the RFC remove CRLF at the beginning */
2115 while (*cur == '\r' || *cur == '\n') {
2116 cur++;
2118 if (cur != conn->inbuf) {
2119 memmove(conn->inbuf, cur, conn->inbufused - (cur - conn->inbuf));
2120 conn->inbufused = strlen(conn->inbuf);
2123 /* Received a full Header? */
2124 if ((cur = strstr(conn->inbuf, "\r\n\r\n")) != NULL) {
2125 time_t currtime = time(NULL);
2126 cur += 2;
2127 cur[0] = '\0';
2128 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), conn->inbuf);
2129 msg = sipmsg_parse_header(conn->inbuf);
2130 cur[0] = '\r';
2131 cur += 2;
2132 restlen = conn->inbufused - (cur - conn->inbuf);
2133 if (restlen >= msg->bodylen) {
2134 dummy = g_malloc(msg->bodylen + 1);
2135 memcpy(dummy, cur, msg->bodylen);
2136 dummy[msg->bodylen] = '\0';
2137 msg->body = dummy;
2138 cur += msg->bodylen;
2139 memmove(conn->inbuf, cur, conn->inbuflen - (cur - conn->inbuf));
2140 conn->inbufused = strlen(conn->inbuf);
2141 } else {
2142 sipmsg_free(msg);
2143 return;
2146 // Verify the signature before processing it
2147 if (sip->registrar.ntlm_key) {
2148 struct sipmsg_breakdown msgbd;
2149 msgbd.msg = msg;
2150 sipmsg_breakdown_parse(&msgbd, sip->registrar.realm, sip->registrar.target);
2151 gchar * signature_input_str = sipmsg_breakdown_get_string(&msgbd);
2152 gchar * signature;
2153 if (signature_input_str != NULL) {
2154 signature = purple_ntlm_sipe_signature_make (signature_input_str, sip->registrar.ntlm_key);
2157 gchar * rspauth = sipmsg_find_part_of_header(sipmsg_find_header(msg, "Authentication-Info"), "rspauth=\"", "\"", NULL);
2158 if (signature != NULL && rspauth != NULL) {
2159 if (purple_ntlm_verify_signature (signature, rspauth)) {
2160 purple_debug(PURPLE_DEBUG_MISC, "sipe", "incoming message's signature validated\n");
2161 process_input_message(sip, msg);
2162 } else {
2163 purple_debug(PURPLE_DEBUG_MISC, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth, signature);
2167 sipmsg_breakdown_free(&msgbd);
2168 } else {
2169 process_input_message(sip, msg);
2172 } else {
2173 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received a incomplete sip msg: %s\n", conn->inbuf);
2177 static void sipe_udp_process(gpointer data, gint source, PurpleInputCondition con)
2179 PurpleConnection *gc = data;
2180 struct sipe_account_data *sip = gc->proto_data;
2181 struct sipmsg *msg;
2182 int len;
2183 time_t currtime;
2185 static char buffer[65536];
2186 if ((len = recv(source, buffer, sizeof(buffer) - 1, 0)) > 0) {
2187 buffer[len] = '\0';
2188 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), buffer);
2189 msg = sipmsg_parse_msg(buffer);
2190 if (msg) process_input_message(sip, msg);
2194 static void sipe_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
2196 PurpleConnection *gc = data;
2197 struct sipe_account_data *sip = gc->proto_data;
2198 struct sip_connection *conn = NULL;
2199 int len;
2200 static char buf[4096];
2202 /* TODO: It should be possible to make this check unnecessary */
2203 if (!PURPLE_CONNECTION_IS_VALID(gc)) {
2204 if (gsc) purple_ssl_close(gsc);
2205 return;
2208 if (sip->gsc)
2209 conn = connection_find(sip, sip->gsc->fd);
2210 if (!conn) {
2211 purple_debug_error("sipe", "Connection not found!\n");
2212 if (sip->gsc) purple_ssl_close(sip->gsc);
2213 return;
2217 if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
2218 conn->inbuflen += SIMPLE_BUF_INC;
2219 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
2222 len = purple_ssl_read(gsc, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1);
2224 if (len < 0 && errno == EAGAIN) {
2225 /* Try again later */
2226 return;
2227 } else if (len < 0) {
2228 purple_debug_info("sipe", "sipe_input_cb_ssl: read error\n");
2229 if (sip->gsc){
2230 connection_remove(sip, sip->gsc->fd);
2231 if (sip->fd == gsc->fd) sip->fd = -1;
2233 return;
2234 } else if (len == 0) {
2235 purple_connection_error(gc, _("Server has disconnected"));
2236 if (sip->gsc){
2237 connection_remove(sip, sip->gsc->fd);
2238 if (sip->fd == gsc->fd) sip->fd = -1;
2240 return;
2243 conn->inbufused += len;
2244 conn->inbuf[conn->inbufused] = '\0';
2246 process_input(sip, conn);
2250 static void sipe_input_cb(gpointer data, gint source, PurpleInputCondition cond)
2252 PurpleConnection *gc = data;
2253 struct sipe_account_data *sip = gc->proto_data;
2254 int len;
2255 struct sip_connection *conn = connection_find(sip, source);
2256 if (!conn) {
2257 purple_debug_error("sipe", "Connection not found!\n");
2258 return;
2261 if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
2262 conn->inbuflen += SIMPLE_BUF_INC;
2263 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
2266 len = read(source, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1);
2268 if (len < 0 && errno == EAGAIN)
2269 return;
2270 else if (len <= 0) {
2271 purple_debug_info("sipe", "sipe_input_cb: read error\n");
2272 connection_remove(sip, source);
2273 if (sip->fd == source) sip->fd = -1;
2274 return;
2277 conn->inbufused += len;
2278 conn->inbuf[conn->inbufused] = '\0';
2280 process_input(sip, conn);
2283 /* Callback for new connections on incoming TCP port */
2284 static void sipe_newconn_cb(gpointer data, gint source, PurpleInputCondition cond)
2286 PurpleConnection *gc = data;
2287 struct sipe_account_data *sip = gc->proto_data;
2288 struct sip_connection *conn;
2290 int newfd = accept(source, NULL, NULL);
2292 conn = connection_create(sip, newfd);
2294 conn->inputhandler = purple_input_add(newfd, PURPLE_INPUT_READ, sipe_input_cb, gc);
2297 static void login_cb(gpointer data, gint source, const gchar *error_message)
2299 PurpleConnection *gc = data;
2300 struct sipe_account_data *sip;
2301 struct sip_connection *conn;
2303 if (!PURPLE_CONNECTION_IS_VALID(gc))
2305 if (source >= 0)
2306 close(source);
2307 return;
2310 if (source < 0) {
2311 purple_connection_error(gc, _("Could not connect"));
2312 return;
2315 sip = gc->proto_data;
2316 sip->fd = source;
2318 conn = connection_create(sip, source);
2320 sip->registertimeout = purple_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
2322 do_register(sip);
2324 conn->inputhandler = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_input_cb, gc);
2327 static void login_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
2329 PurpleConnection *gc = data;
2330 struct sipe_account_data *sip;
2331 struct sip_connection *conn;
2333 if (!PURPLE_CONNECTION_IS_VALID(gc))
2335 if (gsc) purple_ssl_close(gsc);
2336 return;
2339 sip = gc->proto_data;
2340 sip->fd = gsc->fd;
2341 conn = connection_create(sip, sip->fd);
2342 sip->listenport = purple_network_get_port_from_fd(sip->fd);
2343 sip->listenfd = sip->fd;
2344 sip->registertimeout = purple_timeout_add((rand()%100)+3*1000, (GSourceFunc)subscribe_timeout, sip);
2346 do_register(sip);
2348 purple_ssl_input_add(gsc, sipe_input_cb_ssl, gc);
2351 static guint sipe_ht_hash_nick(const char *nick)
2353 char *lc = g_utf8_strdown(nick, -1);
2354 guint bucket = g_str_hash(lc);
2355 g_free(lc);
2357 return bucket;
2360 static gboolean sipe_ht_equals_nick(const char *nick1, const char *nick2)
2362 return (purple_utf8_strcasecmp(nick1, nick2) == 0);
2365 static void sipe_udp_host_resolved_listen_cb(int listenfd, gpointer data)
2367 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2369 sip->listen_data = NULL;
2371 if (listenfd == -1) {
2372 purple_connection_error(sip->gc, _("Could not create listen socket"));
2373 return;
2376 sip->fd = listenfd;
2378 sip->listenport = purple_network_get_port_from_fd(sip->fd);
2379 sip->listenfd = sip->fd;
2381 sip->listenpa = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_udp_process, sip->gc);
2383 sip->resendtimeout = purple_timeout_add(2500, (GSourceFunc) resend_timeout, sip);
2384 sip->registertimeout = purple_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
2385 do_register(sip);
2388 static void sipe_udp_host_resolved(GSList *hosts, gpointer data, const char *error_message)
2390 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2391 int addr_size;
2393 sip->query_data = NULL;
2395 if (!hosts || !hosts->data) {
2396 purple_connection_error(sip->gc, _("Couldn't resolve host"));
2397 return;
2400 addr_size = GPOINTER_TO_INT(hosts->data);
2401 hosts = g_slist_remove(hosts, hosts->data);
2402 memcpy(&(sip->serveraddr), hosts->data, addr_size);
2403 g_free(hosts->data);
2404 hosts = g_slist_remove(hosts, hosts->data);
2405 while (hosts) {
2406 hosts = g_slist_remove(hosts, hosts->data);
2407 g_free(hosts->data);
2408 hosts = g_slist_remove(hosts, hosts->data);
2411 /* create socket for incoming connections */
2412 sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_DGRAM,
2413 sipe_udp_host_resolved_listen_cb, sip);
2414 if (sip->listen_data == NULL) {
2415 purple_connection_error(sip->gc, _("Could not create listen socket"));
2416 return;
2420 static void sipe_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
2421 gpointer data)
2423 PurpleConnection *gc = data;
2424 struct sipe_account_data *sip;
2426 /* If the connection is already disconnected, we don't need to do anything else */
2427 if (!PURPLE_CONNECTION_IS_VALID(gc))
2428 return;
2430 sip = gc->proto_data;
2431 sip->gsc = NULL;
2433 switch(error) {
2434 case PURPLE_SSL_CONNECT_FAILED:
2435 purple_connection_error(gc, _("Connection Failed"));
2436 break;
2437 case PURPLE_SSL_HANDSHAKE_FAILED:
2438 purple_connection_error(gc, _("SSL Handshake Failed"));
2439 break;
2443 static void
2444 sipe_tcp_connect_listen_cb(int listenfd, gpointer data)
2446 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2447 PurpleProxyConnectData *connect_data;
2449 sip->listen_data = NULL;
2451 sip->listenfd = listenfd;
2452 if (sip->listenfd == -1) {
2453 purple_connection_error(sip->gc, _("Could not create listen socket"));
2454 return;
2457 purple_debug_info("sipe", "listenfd: %d\n", sip->listenfd);
2458 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2459 sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2460 sip->listenpa = purple_input_add(sip->listenfd, PURPLE_INPUT_READ,
2461 sipe_newconn_cb, sip->gc);
2462 purple_debug_info("sipe", "connecting to %s port %d\n",
2463 sip->realhostname, sip->realport);
2464 /* open tcp connection to the server */
2465 connect_data = purple_proxy_connect(sip->gc, sip->account, sip->realhostname,
2466 sip->realport, login_cb, sip->gc);
2468 if (connect_data == NULL) {
2469 purple_connection_error(sip->gc, _("Couldn't create socket"));
2475 static void srvresolved(PurpleSrvResponse *resp, int results, gpointer data)
2477 struct sipe_account_data *sip;
2478 gchar *hostname;
2479 int port;
2481 sip = data;
2482 sip->srv_query_data = NULL;
2484 port = purple_account_get_int(sip->account, "port", 0);
2486 /* find the host to connect to */
2487 if (results) {
2488 hostname = g_strdup(resp->hostname);
2489 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - SRV hostname: %s\r\n", hostname);
2490 if (!port)
2491 port = resp->port;
2492 g_free(resp);
2493 } else {
2494 if (!purple_account_get_bool(sip->account, "useproxy", FALSE)) {
2495 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - using sipdomain\r\n");
2496 hostname = g_strdup(sip->sipdomain);
2497 } else {
2498 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - using specified SIP proxy\r\n");
2499 hostname = g_strdup(purple_account_get_string(sip->account, "proxy", sip->sipdomain));
2503 sip->realhostname = hostname;
2504 sip->realport = port;
2505 if (!sip->realport) sip->realport = 5060;
2507 /* TCP case */
2508 //if (!sip->udp) {
2509 // /* create socket for incoming connections */
2510 // sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_STREAM,
2511 // sipe_tcp_connect_listen_cb, sip);
2512 // if (sip->listen_data == NULL) {
2513 // purple_connection_error(sip->gc, _("Could not create listen socket"));
2514 // return;
2515 // }
2516 //} else { /* UDP */
2517 // purple_debug_info("sipe", "using udp with server %s and port %d\n", hostname, port);
2519 // sip->query_data = purple_dnsquery_a(hostname, port, sipe_udp_host_resolved, sip);
2520 // if (sip->query_data == NULL) {
2521 // purple_connection_error(sip->gc, _("Could not resolve hostname"));
2522 // }
2526 static void sipe_login(PurpleAccount *account)
2528 PurpleConnection *gc;
2529 struct sipe_account_data *sip;
2530 gchar **userserver;
2531 gchar *hosttoconnect;
2533 const char *username = purple_account_get_username(account);
2534 gc = purple_account_get_connection(account);
2536 if (strpbrk(username, " \t\v\r\n") != NULL) {
2537 gc->wants_to_die = TRUE;
2538 purple_connection_error(gc, _("SIP Exchange usernames may not contain whitespaces"));
2539 return;
2542 if (!purple_account_get_bool(account, "ssl", FALSE)){
2543 if (!purple_ssl_is_supported())
2545 gc->wants_to_die = TRUE;
2546 purple_connection_error(gc,
2547 _("SSL support is needed for SSL/TLS support. Please install a supported "
2548 "SSL library."));
2549 return;
2554 gc->proto_data = sip = g_new0(struct sipe_account_data, 1);
2555 sip->gc = gc;
2556 sip->account = account;
2557 sip->registerexpire = 900;
2558 sip->udp = purple_account_get_bool(account, "udp", FALSE);
2559 sip->use_ssl = purple_account_get_bool(account, "ssl", FALSE);
2561 purple_debug_info("sipe", "sip->use_ssl->%d\n", sip->use_ssl);
2563 /* TODO: is there a good default grow size? */
2564 if (!sip->udp)
2565 sip->txbuf = purple_circ_buffer_new(0);
2567 userserver = g_strsplit(username, "@", 2);
2568 purple_connection_set_display_name(gc, userserver[0]);
2569 sip->username = g_strdup(g_strjoin("@", userserver[0], userserver[1], NULL));
2570 sip->sipdomain = g_strdup(userserver[1]);
2571 sip->password = g_strdup(purple_connection_get_password(gc));
2572 g_strfreev(userserver);
2574 if (sip->use_ssl) {
2575 // Communicator queries _sipinternaltls._tcp.domain.com and uses that
2576 // information to connect to the OCS server.
2578 // XXX FIXME: eventually we should also query for sipexternaltls as well
2579 // if Pidgin is not on the local LAN
2580 // This doesn't quite work as advertised yet so make sure your have
2581 // your OCS FQDN in the proxy setting in the SIPE account settings
2583 sip->srv_query_data = purple_srv_resolve("sipinternaltls", "tcp", sip->sipdomain, srvresolved, sip);
2586 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_login - realhostname: %s\r\n", sip->realhostname);
2588 sip->buddies = g_hash_table_new((GHashFunc)sipe_ht_hash_nick, (GEqualFunc)sipe_ht_equals_nick);
2590 purple_connection_update_progress(gc, _("Connecting"), 1, 2);
2592 /* TODO: Set the status correctly. */
2593 sip->status = g_strdup("available");
2595 if (!purple_account_get_bool(account, "useproxy", FALSE)) {
2596 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_login - checking realhostname again: %s\r\n", sip->realhostname);
2597 hosttoconnect = g_strdup(sip->sipdomain);
2598 } else {
2599 hosttoconnect = g_strdup(purple_account_get_string(account, "proxy", sip->sipdomain));
2602 /*SSL*/
2603 purple_debug_info("sipe", "HosttoConnect->%s\n", hosttoconnect);
2605 if (sip->use_ssl){
2606 sip->gsc = purple_ssl_connect(account,hosttoconnect, purple_account_get_int(account, "port", 5061), login_cb_ssl, sipe_ssl_connect_failure, gc);
2608 else{
2609 sip->srv_query_data = purple_srv_resolve("sip",
2610 sip->udp ? "udp" : "tcp", hosttoconnect, srvresolved, sip);
2613 g_free(hosttoconnect);
2616 static void sipe_close(PurpleConnection *gc)
2618 struct sipe_account_data *sip = gc->proto_data;
2620 if (sip) {
2621 /* leave all conversations */
2622 im_session_close_all(sip);
2624 /* unregister */
2625 do_register_exp(sip, 0);
2626 connection_free_all(sip);
2628 if (sip->query_data != NULL)
2629 purple_dnsquery_destroy(sip->query_data);
2631 if (sip->srv_query_data != NULL)
2632 purple_srv_cancel(sip->srv_query_data);
2634 if (sip->listen_data != NULL)
2635 purple_network_listen_cancel(sip->listen_data);
2637 g_free(sip->sipdomain);
2638 g_free(sip->username);
2639 g_free(sip->password);
2640 g_free(sip->registrar.nonce);
2641 g_free(sip->registrar.opaque);
2642 g_free(sip->registrar.target);
2643 g_free(sip->registrar.realm);
2644 g_free(sip->registrar.digest_session_key);
2645 g_free(sip->proxy.nonce);
2646 g_free(sip->proxy.opaque);
2647 g_free(sip->proxy.target);
2648 g_free(sip->proxy.realm);
2649 g_free(sip->proxy.digest_session_key);
2650 if (sip->txbuf)
2651 purple_circ_buffer_destroy(sip->txbuf);
2652 g_free(sip->realhostname);
2653 if (sip->listenpa) purple_input_remove(sip->listenpa);
2654 if (sip->tx_handler) purple_input_remove(sip->tx_handler);
2655 if (sip->resendtimeout) purple_timeout_remove(sip->resendtimeout);
2656 if (sip->registertimeout) purple_timeout_remove(sip->registertimeout);
2658 g_free(gc->proto_data);
2659 gc->proto_data = NULL;
2662 /* not needed since privacy is checked for every subscribe */
2663 static void dummy_add_deny(PurpleConnection *gc, const char *name) {
2666 static void dummy_permit_deny(PurpleConnection *gc)
2670 static gboolean sipe_plugin_load(PurplePlugin *plugin)
2672 return TRUE;
2676 static gboolean sipe_plugin_unload(PurplePlugin *plugin)
2678 return TRUE;
2682 static void sipe_plugin_destroy(PurplePlugin *plugin)
2686 static PurplePlugin *my_protocol = NULL;
2688 static PurplePluginProtocolInfo prpl_info =
2691 NULL, /* user_splits */
2692 NULL, /* protocol_options */
2693 NO_BUDDY_ICONS, /* icon_spec */
2694 sipe_list_icon, /* list_icon */
2695 NULL, /* list_emblems */
2696 NULL, /* status_text */
2697 NULL, /* tooltip_text */
2698 sipe_status_types, /* away_states */
2699 NULL, /* blist_node_menu */
2700 NULL, /* chat_info */
2701 NULL, /* chat_info_defaults */
2702 sipe_login, /* login */
2703 sipe_close, /* close */
2704 sipe_im_send, /* send_im */
2705 NULL, /* set_info */
2706 // sipe_typing, /* send_typing */
2707 NULL, /* send_typing */
2708 NULL, /* get_info */
2709 sipe_set_status, /* set_status */
2710 NULL, /* set_idle */
2711 NULL, /* change_passwd */
2712 sipe_add_buddy, /* add_buddy */
2713 NULL, /* add_buddies */
2714 sipe_remove_buddy, /* remove_buddy */
2715 NULL, /* remove_buddies */
2716 dummy_add_deny, /* add_permit */
2717 dummy_add_deny, /* add_deny */
2718 dummy_add_deny, /* rem_permit */
2719 dummy_add_deny, /* rem_deny */
2720 dummy_permit_deny, /* set_permit_deny */
2721 NULL, /* join_chat */
2722 NULL, /* reject_chat */
2723 NULL, /* get_chat_name */
2724 NULL, /* chat_invite */
2725 NULL, /* chat_leave */
2726 NULL, /* chat_whisper */
2727 NULL, /* chat_send */
2728 sipe_keep_alive, /* keepalive */
2729 NULL, /* register_user */
2730 NULL, /* get_cb_info */
2731 NULL, /* get_cb_away */
2732 NULL, /* alias_buddy */
2733 NULL, /* group_buddy */
2734 NULL, /* rename_group */
2735 NULL, /* buddy_free */
2736 sipe_convo_closed, /* convo_closed */
2737 purple_normalize_nocase, /* normalize */
2738 NULL, /* set_buddy_icon */
2739 NULL, /* remove_group */
2740 NULL, /* get_cb_real_name */
2741 NULL, /* set_chat_topic */
2742 NULL, /* find_blist_chat */
2743 NULL, /* roomlist_get_list */
2744 NULL, /* roomlist_cancel */
2745 NULL, /* roomlist_expand_category */
2746 NULL, /* can_receive_file */
2747 NULL, /* send_file */
2748 NULL, /* new_xfer */
2749 NULL, /* offline_message */
2750 NULL, /* whiteboard_prpl_ops */
2751 sipe_send_raw, /* send_raw */
2755 static PurplePluginInfo info = {
2756 PURPLE_PLUGIN_MAGIC,
2757 PURPLE_MAJOR_VERSION,
2758 PURPLE_MINOR_VERSION,
2759 PURPLE_PLUGIN_PROTOCOL, /**< type */
2760 NULL, /**< ui_requirement */
2761 0, /**< flags */
2762 NULL, /**< dependencies */
2763 PURPLE_PRIORITY_DEFAULT, /**< priority */
2764 "prpl-sipe", /**< id */
2765 "Microsoft LCS/OCS", /**< name */
2766 VERSION, /**< version */
2767 N_("SIP/SIMPLE Exchange Protocol Plugin"), /** summary */
2768 N_("The SIP/SIMPLE Exchange Protocol Plugin"), /** description */
2769 "Anibal Avelar <avelar@gmail.com>", /**< author */
2770 PURPLE_WEBSITE, /**< homepage */
2771 sipe_plugin_load, /**< load */
2772 sipe_plugin_unload, /**< unload */
2773 sipe_plugin_destroy, /**< destroy */
2774 NULL, /**< ui_info */
2775 &prpl_info, /**< extra_info */
2776 NULL,
2777 NULL,
2778 NULL,
2779 NULL,
2780 NULL,
2781 NULL
2784 static void init_plugin(PurplePlugin *plugin)
2786 PurpleAccountUserSplit *split;
2787 PurpleAccountOption *option;
2789 purple_plugin_register(plugin);
2791 //split = purple_account_user_split_new(_("Server"), "", '@');
2792 //prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
2793 option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2794 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2795 option = purple_account_option_string_new(_("Proxy Server"), "proxy", "");
2796 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2798 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
2799 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2801 option = purple_account_option_bool_new(_("Use SSL/TLS"), "ssl", FALSE);
2802 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,option);
2804 option = purple_account_option_string_new(_("UserAgent"), "useragent", "Purple/" VERSION);
2805 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,option);
2807 option = purple_account_option_bool_new(_("Use UDP"), "udp", FALSE);
2808 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2810 option = purple_account_option_int_new(_("Connect port"), "port", 5060);
2811 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2813 option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
2814 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2816 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
2817 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
2818 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2820 /*option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2821 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2822 option = purple_account_option_string_new(_("Proxy"), "proxy", "");
2823 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2824 option = purple_account_option_string_new(_("Auth User"), "authuser", "");
2825 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2826 option = purple_account_option_string_new(_("Auth Domain"), "authdomain", "");
2827 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2828 my_protocol = plugin;
2832 /* I had to redefined the function for it load, but works */
2833 gboolean purple_init_plugin(PurplePlugin *plugin){
2834 plugin->info = &(info);
2835 init_plugin((plugin));
2836 sipe_plugin_load((plugin));
2837 return purple_plugin_register(plugin);