NTLM signing is almost here. Clean up tests and add test verifying the
[siplcs.git] / src / sipe.c
blobb2d34176ad9bd80e2b0eaf51de06109a3cf2b570
1 /**
2 * @file sip-exchange.c
4 * purple
6 * Copyright (C) 2007 Anibal Avelar "Fixxxer"<avelar@gmail.com>
7 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
9 * ***
10 * Thanks to Google's Summer of Code Program and the helpful mentors
11 * ***
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #ifndef _WIN32
29 #include "sip-internal.h"
30 #else /* _WIN32 */
31 #ifdef _DLL
32 #define _WS2TCPIP_H_
33 #define _WINSOCK2API_
34 #define _LIBC_INTERNAL_
35 #endif /* _DLL */
37 #include "internal.h"
38 #endif /* _WIN32 */
40 #include "accountopt.h"
41 #include "blist.h"
42 #include "conversation.h"
43 #include "dnsquery.h"
44 #include "debug.h"
45 #include "notify.h"
46 #include "privacy.h"
47 #include "prpl.h"
48 #include "plugin.h"
49 #include "util.h"
50 #include "version.h"
51 #include "network.h"
52 #include "xmlnode.h"
54 #include "sipe.h"
55 #include "sip-ntlm.h"
56 #include "sipkrb5.h"
58 #include "sipmsg.h"
59 #include "sipe-sign.h"
60 #include "dnssrv.h"
62 static char *gentag()
64 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
67 static char *genbranch()
69 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
70 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
71 rand() & 0xFFFF, rand() & 0xFFFF);
74 static char *gencallid()
76 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
77 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
78 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
79 rand() & 0xFFFF, rand() & 0xFFFF);
82 static const char *sipe_list_icon(PurpleAccount *a, PurpleBuddy *b)
84 return "sipe";
87 static void sipe_keep_alive(PurpleConnection *gc)
89 struct sipe_account_data *sip = gc->proto_data;
90 if (sip->udp) { /* in case of UDP send a packet only with a 0 byte to
91 remain in the NAT table */
92 gchar buf[2] = {0, 0};
93 purple_debug_info("sipe", "sending keep alive\n");
94 sendto(sip->fd, buf, 1, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in));
96 return;
99 static gboolean process_register_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc);
101 static void sipe_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
102 static void sipe_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
103 gpointer data);
105 static void send_notify(struct sipe_account_data *sip, struct sipe_watcher *);
107 static void send_service(struct sipe_account_data *sip);
108 static void sipe_subscribe_to_name(struct sipe_account_data *sip, const char * buddy_name);
109 static void send_publish(struct sipe_account_data *sip);
111 static void do_notifies(struct sipe_account_data *sip)
113 GSList *tmp = sip->watcher;
114 purple_debug_info("sipe", "do_notifies()\n");
115 //if ((sip->republish != -1) || sip->republish < time(NULL)) {
116 // if (purple_account_get_bool(sip->account, "doservice", TRUE)) {
117 // send_service(sip);
118 // }
121 while (tmp) {
122 purple_debug_info("sipe", "notifying %s\n", ((struct sipe_watcher*)tmp->data)->name);
123 send_notify(sip, tmp->data);
124 tmp = tmp->next;
128 static void sipe_set_status(PurpleAccount *account, PurpleStatus *status)
130 PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_type(status));
131 struct sipe_account_data *sip = NULL;
133 if (!purple_status_is_active(status))
134 return;
136 if (account->gc)
137 sip = account->gc->proto_data;
139 if (sip)
141 g_free(sip->status);
142 if (primitive == PURPLE_STATUS_AVAILABLE)
143 sip->status = g_strdup("available");
144 else
145 sip->status = g_strdup("busy");
147 do_notifies(sip);
151 static struct sip_connection *connection_find(struct sipe_account_data *sip, int fd)
153 struct sip_connection *ret = NULL;
154 GSList *entry = sip->openconns;
155 while (entry) {
156 ret = entry->data;
157 if (ret->fd == fd) return ret;
158 entry = entry->next;
160 return NULL;
163 static struct sipe_watcher *watcher_find(struct sipe_account_data *sip,
164 const gchar *name)
166 struct sipe_watcher *watcher;
167 GSList *entry = sip->watcher;
168 while (entry) {
169 watcher = entry->data;
170 if (!strcmp(name, watcher->name)) return watcher;
171 entry = entry->next;
173 return NULL;
176 static struct sipe_watcher *watcher_create(struct sipe_account_data *sip,
177 const gchar *name, const gchar *callid, const gchar *ourtag,
178 const gchar *theirtag, gboolean needsxpidf)
180 struct sipe_watcher *watcher = g_new0(struct sipe_watcher, 1);
181 watcher->name = g_strdup(name);
182 watcher->dialog.callid = g_strdup(callid);
183 watcher->dialog.ourtag = g_strdup(ourtag);
184 watcher->dialog.theirtag = g_strdup(theirtag);
185 watcher->needsxpidf = needsxpidf;
186 sip->watcher = g_slist_append(sip->watcher, watcher);
187 return watcher;
190 static void watcher_remove(struct sipe_account_data *sip, const gchar *name)
192 struct sipe_watcher *watcher = watcher_find(sip, name);
193 sip->watcher = g_slist_remove(sip->watcher, watcher);
194 g_free(watcher->name);
195 g_free(watcher->dialog.callid);
196 g_free(watcher->dialog.ourtag);
197 g_free(watcher->dialog.theirtag);
198 g_free(watcher);
201 static struct sip_connection *connection_create(struct sipe_account_data *sip, int fd)
203 struct sip_connection *ret = g_new0(struct sip_connection, 1);
204 ret->fd = fd;
205 sip->openconns = g_slist_append(sip->openconns, ret);
206 return ret;
209 static void connection_remove(struct sipe_account_data *sip, int fd)
211 struct sip_connection *conn = connection_find(sip, fd);
212 sip->openconns = g_slist_remove(sip->openconns, conn);
213 if (conn->inputhandler) purple_input_remove(conn->inputhandler);
214 g_free(conn->inbuf);
215 g_free(conn);
218 static void connection_free_all(struct sipe_account_data *sip)
220 struct sip_connection *ret = NULL;
221 GSList *entry = sip->openconns;
222 while (entry) {
223 ret = entry->data;
224 connection_remove(sip, ret->fd);
225 entry = sip->openconns;
229 static void sipe_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
231 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
232 struct sipe_buddy *b;
233 if (strncmp("sip:", buddy->name, 4)) {
234 gchar *buf = g_strdup_printf("sip:%s", buddy->name);
235 purple_blist_rename_buddy(buddy, buf);
236 g_free(buf);
238 if (!g_hash_table_lookup(sip->buddies, buddy->name)) {
239 b = g_new0(struct sipe_buddy, 1);
240 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy->name);
241 b->name = g_strdup(buddy->name);
242 g_hash_table_insert(sip->buddies, b->name, b);
243 } else {
244 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy->name);
248 static void sipe_get_buddies(PurpleConnection *gc)
250 PurpleBlistNode *gnode, *cnode, *bnode;
252 purple_debug_info("sipe", "sipe_get_buddies\n");
254 for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
255 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue;
256 for (cnode = gnode->child; cnode; cnode = cnode->next) {
257 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue;
258 for (bnode = cnode->child; bnode; bnode = bnode->next) {
259 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue;
260 if (((PurpleBuddy*)bnode)->account == gc->account)
261 sipe_add_buddy(gc, (PurpleBuddy*)bnode, (PurpleGroup *)gnode);
267 static void sipe_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
269 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
270 struct sipe_buddy *b = g_hash_table_lookup(sip->buddies, buddy->name);
271 g_hash_table_remove(sip->buddies, buddy->name);
272 g_free(b->name);
273 g_free(b);
276 static GList *sipe_status_types(PurpleAccount *acc)
278 PurpleStatusType *type;
279 GList *types = NULL;
281 type = purple_status_type_new_with_attrs(
282 PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
283 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
284 NULL);
285 types = g_list_append(types, type);
287 type = purple_status_type_new_full(
288 PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE);
289 types = g_list_append(types, type);
291 return types;
294 //static struct sipe_krb5_auth krb5_auth;
295 static gchar *auth_header_without_newline(struct sipe_account_data *sip, struct sip_auth *auth, struct sipmsg * msg, gboolean force_reauth)
297 const gchar *method = msg->method;
298 const gchar *target = msg->target;
299 gchar noncecount[9];
300 gchar *response;
301 gchar *ret;
302 gchar *tmp;
303 const char *authdomain;
304 const char *authuser;
305 const char *krb5_realm;
306 const char *host;
307 gchar *krb5_token = NULL;
309 authdomain = purple_account_get_string(sip->account, "authdomain", "");
310 authuser = purple_account_get_string(sip->account, "authuser", sip->username);
312 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
313 // and do error checking
315 // KRB realm should always be uppercase
316 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
318 if (sip->realhostname) {
319 host = sip->realhostname;
320 } else if (purple_account_get_bool(sip->account, "use_proxy", TRUE)) {
321 host = purple_account_get_string(sip->account, "proxy", "");
322 } else {
323 host = sip->sipdomain;
326 /*gboolean new_auth = krb5_auth.gss_context == NULL;
327 if (new_auth) {
328 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
331 if (new_auth || force_reauth) {
332 krb5_token = krb5_auth.base64_token;
335 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
336 krb5_token = krb5_auth.base64_token;*/
338 if (!authuser || strlen(authuser) < 1) {
339 authuser = sip->username;
342 if (auth->type == 1) { /* Digest */
343 sprintf(noncecount, "%08d", auth->nc++);
344 response = purple_cipher_http_digest_calculate_response(
345 "md5", method, target, NULL, NULL,
346 auth->nonce, noncecount, NULL, auth->digest_session_key);
347 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response %s\n", response);
349 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);
350 g_free(response);
351 return ret;
352 } else if (auth->type == 2) { /* NTLM */
353 // If we have a signature for the message, include that
354 if (msg->signature) {
355 tmp = g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", response=\"%s\"", auth->realm, auth->target, msg->signature);
356 return tmp;
359 if (auth->nc == 3 && auth->nonce) {
360 /* TODO: Don't hardcode "purple" as the hostname */
361 gchar * gssapi_data = purple_ntlm_gen_authenticate(authuser, sip->password, "purple", authdomain, (const guint8 *)auth->nonce, &auth->flags);
362 //auth->ntlm_key = purple_ntlm_get_key();
363 //printf ("ntlmkey == now NULL? %i\n", auth->ntlm_key == NULL);
364 tmp = g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth->opaque, auth->realm, auth->target, gssapi_data);
365 g_free(gssapi_data);
366 return tmp;
369 tmp = g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth->realm, auth->target);
370 return tmp;
371 } else if (auth->type == 3) {
372 /* Kerberos */
373 if (auth->nc == 3) {
374 /*if (new_auth || force_reauth) {
375 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
376 if (auth->opaque) {
377 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);
378 } else {
379 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
381 } else {
382 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
383 gchar * mic = "MICTODO";
384 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
385 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
386 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
387 //auth->opaque ? auth->opaque : "", auth->target);
388 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
389 //g_free(mic);
391 return tmp;
393 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth->target);
396 sprintf(noncecount, "%08d", auth->nc++);
397 response = purple_cipher_http_digest_calculate_response(
398 "md5", method, target, NULL, NULL,
399 auth->nonce, noncecount, NULL, auth->digest_session_key);
400 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response %s\n", response);
402 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);
403 g_free(response);
404 return ret;
407 static gchar *auth_header(struct sipe_account_data *sip, struct sip_auth *auth, struct sipmsg * msg, gboolean force_reauth)
409 gchar *with, *without;
411 without = auth_header_without_newline(sip, auth, msg, force_reauth);
412 with = g_strdup_printf("%s\r\n", without);
413 g_free (without);
414 return with;
418 static char *parse_attribute(const char *attrname, const char *source)
420 const char *tmp, *tmp2;
421 char *retval = NULL;
422 int len = strlen(attrname);
424 if (!strncmp(source, attrname, len)) {
425 tmp = source + len;
426 tmp2 = g_strstr_len(tmp, strlen(tmp), "\"");
427 if (tmp2)
428 retval = g_strndup(tmp, tmp2 - tmp);
429 else
430 retval = g_strdup(tmp);
433 return retval;
436 static void fill_auth(struct sipe_account_data *sip, gchar *hdr, struct sip_auth *auth)
438 int i = 0;
439 const char *authuser;
440 char *tmp;
441 gchar **parts;
442 const char *krb5_realm;
443 const char *host;
445 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
446 // and do error checking
448 // KRB realm should always be uppercase
449 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
451 if (sip->realhostname) {
452 host = sip->realhostname;
453 } else if (purple_account_get_bool(sip->account, "use_proxy", TRUE)) {
454 host = purple_account_get_string(sip->account, "proxy", "");
455 } else {
456 host = sip->sipdomain;
459 authuser = purple_account_get_string(sip->account, "authuser", sip->username);
461 if (!authuser || strlen(authuser) < 1) {
462 authuser = sip->username;
465 if (!hdr) {
466 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
467 return;
470 if (!g_strncasecmp(hdr, "NTLM", 4)) {
471 auth->type = 2;
472 parts = g_strsplit(hdr+5, "\", ", 0);
473 i = 0;
474 while (parts[i]) {
475 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
476 if ((tmp = parse_attribute("gssapi-data=\"", parts[i]))) {
477 auth->nonce = g_memdup(purple_ntlm_parse_challenge(tmp, &auth->flags), 8);
478 g_free(tmp);
480 if ((tmp = parse_attribute("targetname=\"",
481 parts[i]))) {
482 auth->target = tmp;
484 else if ((tmp = parse_attribute("realm=\"",
485 parts[i]))) {
486 auth->realm = tmp;
488 else if ((tmp = parse_attribute("opaque=\"", parts[i]))) {
489 auth->opaque = tmp;
491 i++;
493 g_strfreev(parts);
494 auth->nc = 1;
495 if (!strstr(hdr, "gssapi-data")) {
496 auth->nc = 1;
497 } else {
498 auth->nc = 3;
500 return;
503 if (!g_strncasecmp(hdr, "Kerberos", 8)) {
504 purple_debug(PURPLE_DEBUG_MISC, "sipe", "setting auth type to Kerberos (3)\r\n");
505 auth->type = 3;
506 purple_debug(PURPLE_DEBUG_MISC, "sipe", "fill_auth - header: %s\r\n", hdr);
507 parts = g_strsplit(hdr+9, "\", ", 0);
508 i = 0;
509 while (parts[i]) {
510 purple_debug_info("sipe", "krb - parts[i] %s\n", parts[i]);
511 if ((tmp = parse_attribute("gssapi-data=\"", parts[i]))) {
512 /*if (krb5_auth.gss_context == NULL) {
513 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
515 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
516 g_free(tmp);
518 if ((tmp = parse_attribute("targetname=\"", parts[i]))) {
519 auth->target = tmp;
520 } else if ((tmp = parse_attribute("realm=\"", parts[i]))) {
521 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 = 3;
529 //if (!strstr(hdr, "gssapi-data")) {
530 // auth->nc = 1;
531 //} else {
532 // auth->nc = 3;
534 return;
537 auth->type = 1;
538 parts = g_strsplit(hdr, " ", 0);
539 while (parts[i]) {
540 if ((tmp = parse_attribute("nonce=\"", parts[i]))) {
541 auth->nonce = tmp;
543 else if ((tmp = parse_attribute("realm=\"", parts[i]))) {
544 auth->realm = tmp;
546 i++;
548 g_strfreev(parts);
550 purple_debug(PURPLE_DEBUG_MISC, "sipe", "nonce: %s realm: %s\n", auth->nonce ? auth->nonce : "(null)", auth->realm ? auth->realm : "(null)");
551 if (auth->realm) {
552 auth->digest_session_key = purple_cipher_http_digest_calculate_session_key(
553 "md5", authuser, auth->realm, sip->password, auth->nonce, NULL);
555 auth->nc = 1;
559 static void sipe_canwrite_cb(gpointer data, gint source, PurpleInputCondition cond)
561 PurpleConnection *gc = data;
562 struct sipe_account_data *sip = gc->proto_data;
563 gsize max_write;
564 gssize written;
566 max_write = purple_circ_buffer_get_max_read(sip->txbuf);
568 if (max_write == 0) {
569 purple_input_remove(sip->tx_handler);
570 sip->tx_handler = 0;
571 return;
574 written = write(sip->fd, sip->txbuf->outptr, max_write);
576 if (written < 0 && errno == EAGAIN)
577 written = 0;
578 else if (written <= 0) {
579 /*TODO: do we really want to disconnect on a failure to write?*/
580 purple_connection_error(gc, _("Could not write"));
581 return;
584 purple_circ_buffer_mark_read(sip->txbuf, written);
587 static void sipe_canwrite_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
589 PurpleConnection *gc = data;
590 struct sipe_account_data *sip = gc->proto_data;
591 gsize max_write;
592 gssize written;
594 max_write = purple_circ_buffer_get_max_read(sip->txbuf);
596 if (max_write == 0) {
597 purple_input_remove(sip->tx_handler);
598 sip->tx_handler = 0;
599 return;
602 written = purple_ssl_write(sip->gsc, sip->txbuf->outptr, max_write);
604 if (written < 0 && errno == EAGAIN)
605 written = 0;
606 else if (written <= 0) {
607 /*TODO: do we really want to disconnect on a failure to write?*/
608 purple_connection_error(gc, _("Could not write"));
609 return;
612 purple_circ_buffer_mark_read(sip->txbuf, written);
615 static void sipe_input_cb(gpointer data, gint source, PurpleInputCondition cond);
617 static void send_later_cb(gpointer data, gint source, const gchar *error)
619 PurpleConnection *gc = data;
620 struct sipe_account_data *sip;
621 struct sip_connection *conn;
623 if (!PURPLE_CONNECTION_IS_VALID(gc))
625 if (source >= 0)
626 close(source);
627 return;
630 if (source < 0) {
631 purple_connection_error(gc, _("Could not connect"));
632 return;
635 sip = gc->proto_data;
636 sip->fd = source;
637 sip->connecting = FALSE;
639 sipe_canwrite_cb(gc, sip->fd, PURPLE_INPUT_WRITE);
641 /* If there is more to write now, we need to register a handler */
642 if (sip->txbuf->bufused > 0)
643 sip->tx_handler = purple_input_add(sip->fd, PURPLE_INPUT_WRITE,
644 sipe_canwrite_cb, gc);
646 conn = connection_create(sip, source);
647 conn->inputhandler = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_input_cb, gc);
650 static void send_later_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
652 PurpleConnection *gc = data;
653 struct sipe_account_data *sip;
654 struct sip_connection *conn;
656 if (!PURPLE_CONNECTION_IS_VALID(gc))
658 purple_ssl_close(gsc);
659 return;
662 sip = gc->proto_data;
663 sip->fd = gsc->fd;
664 sip->connecting = FALSE;
666 sipe_canwrite_cb_ssl(gc, gsc, PURPLE_INPUT_WRITE);
668 /* If there is more to write now, we need to register a handler */
669 if (sip->txbuf->bufused > 0)
670 purple_ssl_input_add(gsc, sipe_canwrite_cb_ssl, gc);
672 conn = connection_create(sip, gsc->fd);
673 purple_ssl_input_add(sip->gsc, sipe_input_cb_ssl, gc);
677 static void sendlater(PurpleConnection *gc, const char *buf)
679 struct sipe_account_data *sip = gc->proto_data;
681 if (!sip->connecting) {
682 purple_debug_info("sipe", "connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport);
683 if (sip->use_ssl){
684 sip->gsc = purple_ssl_connect(sip->account,sip->realhostname, sip->realport, send_later_cb_ssl, sipe_ssl_connect_failure, sip->gc);
686 else{
687 if (purple_proxy_connect(gc, sip->account, sip->realhostname, sip->realport, send_later_cb, gc) == NULL) {
688 purple_connection_error(gc, _("Couldn't create socket"));
691 sip->connecting = TRUE;
694 if (purple_circ_buffer_get_max_read(sip->txbuf) > 0)
695 purple_circ_buffer_append(sip->txbuf, "\r\n", 2);
697 purple_circ_buffer_append(sip->txbuf, buf, strlen(buf));
700 static void sendout_pkt(PurpleConnection *gc, const char *buf)
702 struct sipe_account_data *sip = gc->proto_data;
703 time_t currtime = time(NULL);
704 int writelen = strlen(buf);
706 purple_debug(PURPLE_DEBUG_MISC, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime), buf);
707 if (sip->udp) {
708 if (sendto(sip->fd, buf, writelen, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)) < writelen) {
709 purple_debug_info("sipe", "could not send packet\n");
711 } else {
712 int ret;
713 if (sip->fd < 0) {
714 sendlater(gc, buf);
715 return;
718 if (sip->tx_handler) {
719 ret = -1;
720 errno = EAGAIN;
721 } else{
722 if (sip->gsc){
723 ret = purple_ssl_write(sip->gsc, buf, writelen);
724 }else{
725 ret = write(sip->fd, buf, writelen);
729 if (ret < 0 && errno == EAGAIN)
730 ret = 0;
731 else if (ret <= 0) { /* XXX: When does this happen legitimately? */
732 sendlater(gc, buf);
733 return;
736 if (ret < writelen) {
737 if (!sip->tx_handler){
738 if (sip->gsc){
739 purple_ssl_input_add(sip->gsc, sipe_canwrite_cb_ssl, gc);
741 else{
742 sip->tx_handler = purple_input_add(sip->fd,
743 PURPLE_INPUT_WRITE, sipe_canwrite_cb,
744 gc);
748 /* XXX: is it OK to do this? You might get part of a request sent
749 with part of another. */
750 if (sip->txbuf->bufused > 0)
751 purple_circ_buffer_append(sip->txbuf, "\r\n", 2);
753 purple_circ_buffer_append(sip->txbuf, buf + ret,
754 writelen - ret);
759 static int sipe_send_raw(PurpleConnection *gc, const char *buf, int len)
761 sendout_pkt(gc, buf);
762 return len;
765 static void sendout_sipmsg(struct sipe_account_data *sip, struct sipmsg *msg)
767 GSList *tmp = msg->headers;
768 gchar *name;
769 gchar *value;
770 GString *outstr = g_string_new("");
771 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n", msg->method, msg->target);
772 while (tmp) {
773 name = ((struct siphdrelement*) (tmp->data))->name;
774 value = ((struct siphdrelement*) (tmp->data))->value;
775 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
776 tmp = g_slist_next(tmp);
778 g_string_append_printf(outstr, "\r\n%s", msg->body ? msg->body : "");
779 sendout_pkt(sip->gc, outstr->str);
780 g_string_free(outstr, TRUE);
783 static void send_sip_response(PurpleConnection *gc, struct sipmsg *msg, int code,
784 const char *text, const char *body)
786 GSList *tmp = msg->headers;
787 gchar *name;
788 gchar *value;
789 GString *outstr = g_string_new("");
791 /* When sending the acknowlegements and errors, the content length from the original
792 message is still here, but there is no body; we need to make sure we're sending the
793 correct content length */
794 sipmsg_remove_header(msg, "Content-Length");
795 if (body) {
796 gchar len[12];
797 sprintf(len, "%" G_GSIZE_FORMAT , strlen(body));
798 sipmsg_add_header(msg, "Content-Length", len);
799 } else {
800 sipmsg_add_header(msg, "Content-Length", "0");
803 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
804 //gchar * mic = "MICTODO";
806 g_string_append_printf(outstr, "SIP/2.0 %d %s\r\n", code, text);
807 while (tmp) {
808 name = ((struct siphdrelement*) (tmp->data))->name;
809 value = ((struct siphdrelement*) (tmp->data))->value;
811 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
812 tmp = g_slist_next(tmp);
814 g_string_append_printf(outstr, "\r\n%s", body ? body : "");
815 sendout_pkt(gc, outstr->str);
816 g_string_free(outstr, TRUE);
819 static void transactions_remove(struct sipe_account_data *sip, struct transaction *trans)
821 if (trans->msg) sipmsg_free(trans->msg);
822 sip->transactions = g_slist_remove(sip->transactions, trans);
823 g_free(trans);
826 static void transactions_add_buf(struct sipe_account_data *sip, const struct sipmsg *msg, void *callback)
828 struct transaction *trans = g_new0(struct transaction, 1);
829 trans->time = time(NULL);
830 trans->msg = msg;
831 trans->cseq = sipmsg_find_header(trans->msg, "CSeq");
832 trans->callback = callback;
833 sip->transactions = g_slist_append(sip->transactions, trans);
836 static struct transaction *transactions_find(struct sipe_account_data *sip, struct sipmsg *msg)
838 struct transaction *trans;
839 GSList *transactions = sip->transactions;
840 gchar *cseq = sipmsg_find_header(msg, "CSeq");
842 while (transactions) {
843 trans = transactions->data;
844 if (!strcmp(trans->cseq, cseq)) {
845 return trans;
847 transactions = transactions->next;
850 return NULL;
853 static void send_sip_request(PurpleConnection *gc, const gchar *method,
854 const gchar *url, const gchar *to, const gchar *addheaders,
855 const gchar *body, struct sip_dialog *dialog, TransCallback tc)
857 struct sipe_account_data *sip = gc->proto_data;
858 char *callid = dialog ? g_strdup(dialog->callid) : gencallid();
859 const char *addh = "";
860 gchar *branch = genbranch();
861 gchar *tag = NULL;
862 char *buf;
863 struct sipmsg *msg;
864 gchar *ptmp;
866 if (!strcmp(method, "REGISTER")) {
867 if (sip->regcallid) {
868 g_free(callid);
869 callid = g_strdup(sip->regcallid);
871 else sip->regcallid = g_strdup(callid);
874 if (addheaders) addh = addheaders;
877 if (!dialog)
878 tag = gentag();
880 buf = g_strdup_printf("%s %s SIP/2.0\r\n"
881 "Via: SIP/2.0/%s %s:%d;branch=%s\r\n"
882 /* epid Identifies a unique endpoint for the user. Used by
883 * the server to determine the correct SA to use for
884 * signing an outgoing response.
885 * TODO: generate a random epid
886 * */
887 "From: <sip:%s>;tag=%s;epid=1234567890\r\n"
888 "To: <%s>%s%s\r\n"
889 "Max-Forwards: 70\r\n"
890 "CSeq: %d %s\r\n"
891 "User-Agent: Purple/" VERSION "\r\n"
892 "Call-ID: %s\r\n"
893 "%s"
894 "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n%s",
895 method,
896 url,
897 sip->use_ssl ? "TLS" : sip->udp ? "UDP" : "TCP",
898 purple_network_get_my_ip(-1),
899 sip->listenport,
900 branch,
901 sip->username,
902 dialog ? dialog->ourtag : tag,
904 dialog ? ";tag=" : "",
905 dialog ? dialog->theirtag : "",
906 ++sip->cseq,
907 method,
908 callid,
909 addh,
910 strlen(body),
911 body);
914 //printf ("parsing msg buf:\n%s\n\n", buf);
915 msg = sipmsg_parse_msg(buf);
917 g_free(buf);
918 g_free(tag);
919 g_free(branch);
920 g_free(callid);
922 if (purple_ntlm_authorized()) {
923 struct sipmsg_breakdown msgbd;
924 msgbd.msg = msg;
925 sipmsg_breakdown_parse(&msgbd, sip->registrar.realm, sip->registrar.target);
926 gchar * signature_input_str = sipmsg_breakdown_get_string(&msgbd);
928 printf ("Have signature_input_str: %s\n", signature_input_str);
929 if (signature_input_str != NULL) {
930 msg->signature = purple_ntlm_signature_make (signature_input_str, 0, NULL);
932 msg->signature = NULL;
934 g_free(signature_input_str);
935 sipmsg_breakdown_free(&msgbd);
936 } else {
937 printf ("registrar ntlm_key is null. proxies? %i\n", sip->proxy.ntlm_key == NULL);
940 if (sip->registrar.type && !strcmp(method, "REGISTER")) {
941 buf = auth_header_without_newline(sip, &sip->registrar, msg, FALSE);
942 printf("1.for sig %s got auth buf %s\n", msg->signature, buf);
943 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
944 sipmsg_add_header(msg, "Authorization", buf);
945 } else {
946 sipmsg_add_header_pos(msg, "Proxy-Authorization", buf, 5);
947 //sipmsg_add_header_pos(msg, "Authorization", buf, 5);
949 g_free(buf);
950 } else if (!strcmp(method,"SUBSCRIBE") || !strcmp(method,"SERVICE") || !strcmp(method,"MESSAGE") || !strcmp(method,"INVITE") || !strcmp(method,"NOTIFY")) {
951 sip->registrar.nc=3;
952 sip->registrar.type=2;
954 buf = auth_header_without_newline(sip, &sip->registrar, msg, FALSE);
955 printf("2.for sig %s got auth buf %s\n", msg->signature, buf);
956 //buf = auth_header(sip, &sip->proxy, msg, FALSE);
957 sipmsg_add_header_pos(msg, "Proxy-Authorization", buf, 5);
958 //sipmsg_add_header(msg, "Authorization", buf);
959 g_free(buf);
963 buf = sipmsg_to_string (msg);
965 /* add to ongoing transactions */
967 transactions_add_buf(sip, msg, tc);
969 sendout_pkt(gc, buf);
972 static char *get_contact_register(struct sipe_account_data *sip)
974 return g_strdup_printf("<sip:%s:%d;transport=%s>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, BYE, CANCEL, NOTIFY, ACK, BENOTIFY\";proxy=replace", purple_network_get_my_ip(-1), sip->listenport, sip->use_ssl ? "tls" : sip->udp ? "udp" : "tcp");
977 static char *get_contact(struct sipe_account_data *sip)
979 //return g_strdup_printf("<sip:%s@%s:%d;maddr=%s;transport=%s>;proxy=replace", sip->username, sip->sipdomain, sip->listenport, sipe_network_get_local_system_ip() , sip->udp ? "udp" : "tcp");
980 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");
983 static void do_register_exp(struct sipe_account_data *sip, int expire)
985 char *uri = g_strdup_printf("sip:%s", sip->sipdomain);
986 char *to = g_strdup_printf("sip:%s", sip->username);
987 char *contact = get_contact_register(sip);
988 //char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire);
989 // 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);
990 //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);
991 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);
992 g_free(contact);
994 sip->registerstatus = 1;
996 if (expire) {
997 sip->reregister = time(NULL) + expire - 50;
998 } else {
999 sip->reregister = time(NULL) + 600;
1002 send_sip_request(sip->gc, "REGISTER", uri, to, hdr, "", NULL,
1003 process_register_response);
1005 g_free(hdr);
1006 g_free(uri);
1007 g_free(to);
1010 static void do_register(struct sipe_account_data *sip)
1012 do_register_exp(sip, sip->registerexpire);
1015 static gchar *parse_from(const gchar *hdr)
1017 gchar *from;
1018 const gchar *tmp, *tmp2 = hdr;
1020 if (!hdr) return NULL;
1021 purple_debug_info("sipe", "parsing address out of %s\n", hdr);
1022 tmp = strchr(hdr, '<');
1024 /* i hate the different SIP UA behaviours... */
1025 if (tmp) { /* sip address in <...> */
1026 tmp2 = tmp + 1;
1027 tmp = strchr(tmp2, '>');
1028 if (tmp) {
1029 from = g_strndup(tmp2, tmp - tmp2);
1030 } else {
1031 purple_debug_info("sipe", "found < without > in From\n");
1032 return NULL;
1034 } else {
1035 tmp = strchr(tmp2, ';');
1036 if (tmp) {
1037 from = g_strndup(tmp2, tmp - tmp2);
1038 } else {
1039 from = g_strdup(tmp2);
1042 purple_debug_info("sipe", "got %s\n", from);
1043 return from;
1046 static gboolean process_subscribe_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1048 gchar *to;
1050 if (msg->response == 200 || msg->response == 202) {
1051 return TRUE;
1054 to = parse_from(sipmsg_find_header(tc->msg, "To")); /* cant be NULL since it is our own msg */
1056 /* we can not subscribe -> user is offline (TODO unknown status?) */
1058 purple_prpl_got_user_status(sip->account, to, "offline", NULL);
1059 g_free(to);
1060 return TRUE;
1063 static void sipe_subscribe_to_name(struct sipe_account_data *sip, const char * buddy_name)
1065 gchar *to = strstr(buddy_name, "sip:") ? g_strdup(buddy_name) : g_strdup_printf("sip:%s", buddy_name);
1066 gchar *tmp = get_contact(sip);
1067 gchar *contact = g_strdup_printf(
1068 "Accept: application/pidf+xml, application/xpidf+xml\r\n"
1069 "Event: presence\r\n"
1070 "Contact: %s\r\n", tmp);
1071 g_free(tmp);
1073 /* subscribe to buddy presence
1074 * we dont need to know the status so we do not need a callback */
1076 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL,
1077 process_subscribe_response);
1079 g_free(to);
1080 g_free(contact);
1083 static void sipe_subscribe(struct sipe_account_data *sip, struct sipe_buddy *buddy)
1085 sipe_subscribe_to_name(sip, buddy->name);
1087 /* resubscribe before subscription expires */
1088 /* add some jitter */
1089 buddy->resubscribe = time(NULL)+1140+(rand()%50);
1092 static gboolean sipe_add_lcs_contacts(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1094 gchar *tmp;
1095 xmlnode *item, *group, *isc;
1096 const char *name_group, *group_id;
1097 PurpleBuddy *b;
1098 PurpleGroup *g = NULL;
1099 gchar **parts;
1100 gchar *apn;
1101 int ng = 0, i;
1102 struct sipe_buddy *bs;
1103 struct sipe_group *gr;
1104 int len = msg->bodylen;
1106 // Reserved to max 10 groups. TODO be dynamic
1107 gr = g_new0(struct sipe_group, 10);
1109 tmp = sipmsg_find_header(msg, "Event");
1110 if (tmp && !strncmp(tmp, "vnd-microsoft-roaming-contacts", 30)) {
1111 purple_debug_info("sipe", "sipe_add_lcs_contacts->%s-%d\n", msg->body, len);
1112 /*Convert the contact from XML to Purple Buddies*/
1113 isc = xmlnode_from_str(msg->body, len);
1115 /* TODO Find for all groups */
1116 for (group = xmlnode_get_child(isc, "group"); group; group = xmlnode_get_next_twin(group)) {
1117 name_group = xmlnode_get_attrib(group, "name");
1118 group_id = xmlnode_get_attrib(group, "id");
1120 if (!strncmp(name_group, "~", 1)){
1121 name_group=g_strdup("General");
1124 gr[ng].name_group = g_strdup(name_group);
1125 gr[ng].id = g_strdup(group_id);
1126 purple_debug_info("sipe", "name_group->%s\n", name_group);
1127 g = purple_find_group(name_group);
1129 if (!g) {
1130 g = purple_group_new(name_group);
1131 purple_blist_add_group(g, NULL);
1134 if (!g) {
1135 g = purple_find_group("General");
1136 if (!g) {
1137 g = purple_group_new("General");
1138 purple_blist_add_group(g, NULL);
1142 gr[ng].g = g;
1143 ng++;
1146 for (i = 0; i < ng;i++) {
1147 purple_debug_info("sipe", "id->%s\n", gr[i].id);
1148 purple_debug_info("sipe", "id->%s\n", gr[i].name_group);
1151 for (item = xmlnode_get_child(isc, "contact"); item; item = xmlnode_get_next_twin(item)) {
1152 const char *uri, *name, *groups;
1153 char *buddy_name;
1154 i = 0;
1155 uri = xmlnode_get_attrib(item, "uri");
1156 name = xmlnode_get_attrib(item, "name");
1157 groups = xmlnode_get_attrib(item, "groups");
1158 parts = g_strsplit(groups, " ", 0);
1159 purple_debug_info("sipe", "URI->%s,Groups->%s\n", uri, groups);
1160 if (parts[i]!=NULL){
1161 while (parts[i]) {
1162 purple_debug_info("sipe", "Groups->parts[i] %s\n", parts[i]);
1163 if (!strcmp(gr[i].id,parts[i])){
1164 purple_debug_info("sipe", "Found Groups->gr[i].id(%s),gr[i].name_group (%s)\n",gr[i].id,gr[i].name_group);
1166 buddy_name = g_strdup_printf("sip:%s", uri);
1168 //b = purple_find_buddy(sip->account, buddy_name);
1169 b = purple_find_buddy_in_group(sip->account, buddy_name, gr[i].g);
1170 if (!b){
1171 b = purple_buddy_new(sip->account, buddy_name, uri);
1173 g_free(buddy_name);
1175 //sipe_add_buddy(sip->gc, b , gr[i].g);
1176 purple_blist_add_buddy(b, NULL, gr[i].g, NULL);
1177 purple_blist_alias_buddy(b, uri);
1178 bs = g_new0(struct sipe_buddy, 1);
1179 bs->name = g_strdup(b->name);
1180 g_hash_table_insert(sip->buddies, bs->name, bs);
1182 i++;
1187 xmlnode_free(isc);
1189 return 0;
1192 static void sipe_subscribe_buddylist(struct sipe_account_data *sip)
1194 gchar *contact = "Event: vnd-microsoft-roaming-contacts\r\nAccept: application/vnd-microsoft-roaming-contacts+xml\r\nSupported: com.microsoft.autoextend\r\nSupported: ms-benotify\r\nProxy-Require: ms-benotify\r\nSupported: ms-piggyback-first-notify\r\n";
1195 gchar *to;
1196 gchar *tmp;
1197 //to = g_strdup_printf("sip:%s@%s", sip->username, sip->sipdomain);
1198 to = g_strdup_printf("sip:%s", sip->username);
1200 tmp = get_contact(sip);
1201 contact = g_strdup_printf("%sContact: %s\r\n", contact, tmp);
1202 g_free(tmp);
1203 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL, sipe_add_lcs_contacts);
1204 g_free(to);
1205 g_free(contact);
1208 static void sipe_invite(struct sipe_account_data *sip, const char *name)
1210 gchar *hdr;
1211 gchar *to;
1212 gchar *contact;
1213 gchar *body;
1215 if (strstr(name, "sip:")) {
1216 to = g_strdup(name);
1217 } else {
1218 to = g_strdup_printf("sip:%s", name);
1221 contact = get_contact(sip);
1222 hdr = g_strdup_printf(
1223 "Contact: %s\r\n"
1224 "Supported: com.microsoft.rtc-multiparty\r\n"
1225 "Roster-Manager:sip:%s\r\n"
1226 "Ms-Conversation-ID: AckwibOFOjjDdVR6S5e0xywMj6Kaww==\r\n" //temp
1227 "ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA; \r\n" //temp
1228 "Supported: ms-delayed-accept\r\n"
1229 "Supported: ms-renders-isf\r\n"
1230 "Supported: ms-renders-gif\r\n"
1231 "Supported: ms-renders-mime-alternative\r\n"
1232 "EndPoints: <sip:%s>, <%s>\r\n"
1233 "Content-Type: application/sdp\r\n",
1234 contact, sip->username, sip->username, to);
1236 body = g_strdup_printf(
1237 "v=0\r\n"
1238 "o=- 0 0 IN IP4 %s\r\n"
1239 "s=session\r\n"
1240 "c=IN IP4 %s\r\n"
1241 "t=0 0\r\n"
1242 "m=message %d sip null\r\n"
1243 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml",
1244 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), 5061);
1246 send_sip_request(sip->gc, "INVITE", to, to, hdr, body, NULL, NULL);
1248 g_free(to);
1249 g_free(body);
1250 g_free(hdr);
1251 g_free(contact);
1254 static void sipe_buddy_resub(char *name, struct sipe_buddy *buddy, struct sipe_account_data *sip)
1256 time_t curtime = time(NULL);
1257 if (buddy->resubscribe < curtime) {
1258 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_buddy_resub %s\n", name);
1259 sipe_subscribe(sip, buddy);
1263 static gboolean resend_timeout(struct sipe_account_data *sip)
1265 GSList *tmp = sip->transactions;
1266 time_t currtime = time(NULL);
1267 while (tmp) {
1268 struct transaction *trans = tmp->data;
1269 tmp = tmp->next;
1270 purple_debug_info("sipe", "have open transaction age: %d\n", currtime- trans->time);
1271 if ((currtime - trans->time > 5) && trans->retries >= 1) {
1272 /* TODO 408 */
1273 } else {
1274 if ((currtime - trans->time > 2) && trans->retries == 0) {
1275 trans->retries++;
1276 sendout_sipmsg(sip, trans->msg);
1280 return TRUE;
1283 static gboolean subscribe_timeout(struct sipe_account_data *sip)
1285 GSList *tmp;
1286 time_t curtime = time(NULL);
1287 /* register again if first registration expires */
1288 if (sip->reregister < curtime) {
1289 do_register(sip);
1291 /* check for every subscription if we need to resubscribe */
1292 //Fixxxer we need resub?
1293 g_hash_table_foreach(sip->buddies, (GHFunc)sipe_buddy_resub, (gpointer)sip);
1295 /* remove a timed out suscriber */
1297 tmp = sip->watcher;
1298 while (tmp) {
1299 struct sipe_watcher *watcher = tmp->data;
1300 if (watcher->expire < curtime) {
1301 watcher_remove(sip, watcher->name);
1302 tmp = sip->watcher;
1304 if (tmp) tmp = tmp->next;
1307 return TRUE;
1310 static void sipe_send_message(struct sipe_account_data *sip, const char *to, const char *msg, const char *type)
1312 gchar *hdr;
1313 gchar *fullto;
1314 gchar *tmp;
1315 if (strncmp("sip:", to, 4)) {
1316 fullto = g_strdup_printf("sip:%s", to);
1317 } else {
1318 fullto = g_strdup(to);
1320 if (type) {
1321 hdr = g_strdup_printf("Content-Type: %s\r\n", type);
1322 } else {
1323 hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1325 tmp = get_contact(sip);
1326 hdr = g_strdup_printf("Contact: %s\r\n%s", tmp, hdr);
1327 g_free(tmp);
1329 send_sip_request(sip->gc, "MESSAGE", fullto, fullto, hdr, msg, NULL, NULL);
1330 g_free(hdr);
1331 g_free(fullto);
1334 static int sipe_im_send(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags)
1336 struct sipe_account_data *sip = gc->proto_data;
1337 char *to = g_strdup(who);
1338 char *text = purple_unescape_html(what);
1339 //sipe_send_message(sip, to, text, NULL);
1340 //sipe_invite(sip, to);
1341 purple_debug_info("sipe", "sending IMs not implemented\n");
1342 g_free(to);
1343 g_free(text);
1344 return 1;
1347 static void process_incoming_message(struct sipe_account_data *sip, struct sipmsg *msg)
1349 gchar *from;
1350 gchar *contenttype;
1351 gboolean found = FALSE;
1353 from = parse_from(sipmsg_find_header(msg, "From"));
1355 if (!from) return;
1357 purple_debug_info("sipe", "got message from %s: %s\n", from, msg->body);
1359 contenttype = sipmsg_find_header(msg, "Content-Type");
1360 if (!contenttype || !strncmp(contenttype, "text/plain", 10) || !strncmp(contenttype, "text/html", 9)) {
1361 serv_got_im(sip->gc, from, msg->body, 0, time(NULL));
1362 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1363 found = TRUE;
1365 if (!strncmp(contenttype, "application/im-iscomposing+xml", 30)) {
1366 xmlnode *isc = xmlnode_from_str(msg->body, msg->bodylen);
1367 xmlnode *state;
1368 gchar *statedata;
1370 if (!isc) {
1371 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1372 return;
1375 state = xmlnode_get_child(isc, "state");
1377 if (!state) {
1378 purple_debug_info("sipe", "process_incoming_message: no state found\n");
1379 xmlnode_free(isc);
1380 return;
1383 statedata = xmlnode_get_data(state);
1384 if (statedata) {
1385 if (strstr(statedata, "active")) serv_got_typing(sip->gc, from, 0, PURPLE_TYPING);
1386 else serv_got_typing_stopped(sip->gc, from);
1388 g_free(statedata);
1390 xmlnode_free(isc);
1391 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1392 found = TRUE;
1394 if (!found) {
1395 purple_debug_info("sipe", "got unknown mime-type");
1396 send_sip_response(sip->gc, msg, 415, "Unsupported media type", NULL);
1398 g_free(from);
1401 static void process_incoming_invite(struct sipe_account_data *sip, struct sipmsg *msg)
1403 gchar *contact;
1404 contact = get_contact(sip);
1405 sipmsg_remove_header(msg, "Contact");
1406 sipmsg_add_header(msg, "Contact", contact);
1408 //sipmsg_remove_header(msg, "User-Agent");
1409 //sipmsg_add_header_pos(msg, "User-Agent", g_strdup_printf("Purple/" VERSION), 6);
1411 send_sip_response(sip->gc, msg, 200, "OK", g_strdup_printf(
1412 "v=0\r\n"
1413 "o=- 0 0 IN IP4 %s\r\n"
1414 "s=session\r\n"
1415 "c=IN IP4 %s\r\n"
1416 "t=0 0\r\n"
1417 "m=message %d sip sip:%s\r\n"
1418 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml",
1419 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
1420 //sip->realport, sip->username
1421 5061, sip->username));
1423 g_free(contact);
1426 gboolean process_register_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1428 gchar *tmp, krb5_token;
1429 const gchar *expires_header;
1430 int expires;
1432 expires_header = sipmsg_find_header(msg, "Expires");
1433 expires = expires_header != NULL ? strtol(expires_header, NULL, 10) : 0;
1434 purple_debug_info("sipe", "got response to REGISTER; expires = %d\n", expires);
1436 switch (msg->response) {
1437 case 200:
1438 if (expires == 0) {
1439 sip->registerstatus = 0;
1440 } else {
1441 sip->registerstatus = 3;
1442 purple_connection_set_state(sip->gc, PURPLE_CONNECTED);
1444 /* tell everybody we're online */
1445 send_publish (sip);
1447 /* get buddies from blist */
1448 sipe_get_buddies(sip->gc);
1449 subscribe_timeout(sip);
1451 //sipe_subscribe_to_name(sip, sip->username);
1453 tmp = sipmsg_find_header(msg, "Allow-Events");
1454 if (tmp && strstr(tmp, "vnd-microsoft-provisioning")){
1455 sipe_subscribe_buddylist(sip);
1458 // Should we remove the transaction here?
1459 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip->cseq);
1460 //transactions_remove(sip, tc);
1462 break;
1463 case 401:
1464 if (sip->registerstatus != 2) {
1465 purple_debug_info("sipe", "REGISTER retries %d\n", sip->registrar.retries);
1466 if (sip->registrar.retries > 3) {
1467 sip->gc->wants_to_die = TRUE;
1468 purple_connection_error(sip->gc, _("Wrong Password"));
1469 return TRUE;
1471 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
1472 tmp = sipmsg_find_auth_header(msg, "NTLM");
1473 } else {
1474 tmp = sipmsg_find_auth_header(msg, "Kerberos");
1476 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_register_response - Auth header: %s\r\n", tmp);
1477 fill_auth(sip, tmp, &sip->registrar);
1478 sip->registerstatus = 2;
1479 if (sip->account->disconnecting) {
1480 do_register_exp(sip, 0);
1481 } else {
1482 do_register(sip);
1485 break;
1487 return TRUE;
1490 static void process_incoming_notify(struct sipe_account_data *sip, struct sipmsg *msg)
1492 gchar *from;
1493 gchar *fromhdr;
1494 gchar *tmp2;
1495 xmlnode *pidf;
1496 xmlnode *basicstatus = NULL, *tuple, *status;
1497 gboolean isonline = FALSE;
1499 fromhdr = sipmsg_find_header(msg, "From");
1500 from = parse_from(fromhdr);
1501 if (!from) return;
1503 pidf = xmlnode_from_str(msg->body, msg->bodylen);
1505 if (!pidf) {
1506 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
1507 return;
1510 purple_debug_info("sipe", "process_incoming_notify: body(%s)\n",msg->body);
1512 if ((tuple = xmlnode_get_child(pidf, "tuple")))
1513 if ((status = xmlnode_get_child(tuple, "status")))
1514 basicstatus = xmlnode_get_child(status, "basic");
1516 if (!basicstatus) {
1517 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
1518 xmlnode_free(pidf);
1519 return;
1522 tmp2 = xmlnode_get_data(basicstatus);
1524 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n",tmp2);
1527 if (!tmp2) {
1528 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
1529 xmlnode_free(pidf);
1530 return;
1533 if (strstr(tmp2, "open")) {
1534 isonline = TRUE;
1537 g_free(tmp2);
1539 if (isonline) purple_prpl_got_user_status(sip->account, from, "available", NULL);
1540 else purple_prpl_got_user_status(sip->account, from, "offline", NULL);
1542 xmlnode_free(pidf);
1544 g_free(from);
1545 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1549 static gchar *find_tag(const gchar *hdr)
1551 return sipmsg_find_part_of_header (hdr, ";tag=", ";", NULL);
1554 static gchar* gen_xpidf(struct sipe_account_data *sip)
1556 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1557 "<presence>\r\n"
1558 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
1559 "<display name=\"sip:%s\"/>\r\n"
1560 "<atom id=\"1234\">\r\n"
1561 "<address uri=\"sip:%s\">\r\n"
1562 "<status status=\"%s\"/>\r\n"
1563 "</address>\r\n"
1564 "</atom>\r\n"
1565 "</presence>\r\n",
1566 sip->username,
1567 sip->username,
1568 sip->username,
1569 sip->status);
1570 return doc;
1575 static gchar* gen_pidf(struct sipe_account_data *sip)
1577 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1578 "<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"
1579 "<tuple id=\"0\">\r\n"
1580 "<status>\r\n"
1581 "<basic>open</basic>\r\n"
1582 "<ep:activities>\r\n"
1583 " <ep:activity>%s</ep:activity>\r\n"
1584 "</ep:activities>"
1585 "</status>\r\n"
1586 "</tuple>\r\n"
1587 "<ci:display-name>%s</ci:display-name>\r\n"
1588 "</presence>",
1589 sip->username,
1590 sip->status,
1591 sip->username);
1592 return doc;
1595 static void send_notify(struct sipe_account_data *sip, struct sipe_watcher *watcher)
1597 gchar *doc = watcher->needsxpidf ? gen_xpidf(sip) : gen_pidf(sip);
1598 gchar *hdr = watcher->needsxpidf ? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
1599 send_sip_request(sip->gc, "NOTIFY", watcher->name, watcher->name, hdr, doc, &watcher->dialog, NULL);
1600 g_free(doc);
1603 static gboolean process_service_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc)
1605 if (msg->response != 200 && msg->response != 408) {
1606 /* never send again */
1607 sip->republish = -1;
1609 return TRUE;
1612 static void send_publish(struct sipe_account_data *sip)
1614 gchar *uri = g_strdup_printf("sip:%s", sip->username);
1615 gchar *doc = g_strdup_printf(
1616 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\"><publications uri=\"%s\"><publication categoryName=\"device\" instance=\"1617359818\" container=\"2\" version=\"0\" expireType=\"endpoint\"><device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"BB44F8D5-1540-547D-9ECE-6486D33DC804\"><capabilities preferred=\"false\" uri=\"%s\"><text capture=\"true\" render=\"true\" publish=\"false\"/><gifInk capture=\"false\" render=\"true\" publish=\"false\"/><isfInk capture=\"false\" render=\"true\" publish=\"false\"/></capabilities><timezone>%s</timezone><machineName>%s</machineName></device></publication><publication categoryName=\"state\" instance=\"906391356\" container=\"2\" version=\"0\" expireType=\"endpoint\"><state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\"><availability>3500</availability><endpointLocation></endpointLocation></state></publication><publication categoryName=\"state\" instance=\"906391356\" container=\"3\" version=\"0\" expireType=\"endpoint\"><state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\"><availability>3500</availability><endpointLocation></endpointLocation></state></publication></publications></publish>",
1617 uri, uri,
1618 "00:00:00-05:00", // TODO timezone
1619 "PC" // TODO machine name
1622 gchar *tmp = get_contact(sip);
1623 gchar *hdr = g_strdup_printf("Contact: %s\r\nContent-Type: application/msrtc-category-publish+xml\r\n", tmp);
1624 g_free(tmp);
1626 send_sip_request(sip->gc, "SERVICE", uri, uri, hdr, doc, NULL, process_service_response);
1627 //sip->republish = time(NULL) + 500;
1629 g_free(hdr);
1630 g_free(uri);
1631 g_free(doc);
1634 static void send_service(struct sipe_account_data *sip)
1636 //gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->sipdomain);
1637 gchar *uri = g_strdup_printf("sip:%s", sip->username);
1638 //gchar *doc = gen_pidf(sip);
1640 gchar *doc = gen_pidf(sip);
1641 gchar *hdr = g_strdup("Event: presence\r\nContent-Type: application/pidf+xml\r\n");
1643 //gchar *hdr = g_strdup("Content-Type: application/SOAP+xml\r\n");
1644 gchar *tmp = get_contact(sip);
1645 hdr = g_strdup_printf("Contact: %s\r\n%s", tmp, hdr);
1646 g_free(tmp);
1647 send_sip_request(sip->gc, "SERVICE", uri, uri,
1648 hdr,
1649 doc, NULL, process_service_response);
1650 sip->republish = time(NULL) + 500;
1651 g_free(hdr);
1652 g_free(uri);
1653 g_free(doc);
1656 static void process_incoming_subscribe(struct sipe_account_data *sip, struct sipmsg *msg)
1658 const char *from_hdr = sipmsg_find_header(msg, "From");
1659 gchar *from = parse_from(from_hdr);
1660 gchar *theirtag = find_tag(from_hdr);
1661 gchar *ourtag = find_tag(sipmsg_find_header(msg, "To"));
1662 gboolean tagadded = FALSE;
1663 gchar *callid = sipmsg_find_header(msg, "Call-ID");
1664 gchar *expire = sipmsg_find_header(msg, "Expire");
1665 // gchar *ms-received-port =find_received_port(sipmsg_find_header(msg, "Contact"));
1666 gchar *tmp;
1667 struct sipe_watcher *watcher = watcher_find(sip, from);
1668 if (!ourtag) {
1669 tagadded = TRUE;
1670 ourtag = gentag();
1672 if (!watcher) { /* new subscription */
1673 gchar *acceptheader = sipmsg_find_header(msg, "Accept");
1674 gboolean needsxpidf = FALSE;
1675 if (!purple_privacy_check(sip->account, from)) {
1676 send_sip_response(sip->gc, msg, 202, "Ok", NULL);
1677 goto privend;
1679 if (acceptheader) {
1680 gchar *tmp = acceptheader;
1681 gboolean foundpidf = FALSE;
1682 gboolean foundxpidf = FALSE;
1683 while (tmp && tmp < acceptheader + strlen(acceptheader)) {
1684 gchar *tmp2 = strchr(tmp, ',');
1685 if (tmp2) *tmp2 = '\0';
1686 if (!strcmp("application/pidf+xml", tmp))
1687 foundpidf = TRUE;
1688 if (!strcmp("application/xpidf+xml", tmp))
1689 foundxpidf = TRUE;
1690 if (tmp2) {
1691 *tmp2 = ',';
1692 tmp = tmp2;
1693 while (*tmp == ' ') tmp++;
1694 } else
1695 tmp = 0;
1697 if (!foundpidf && foundxpidf) needsxpidf = TRUE;
1698 g_free(acceptheader);
1700 watcher = watcher_create(sip, from, callid, ourtag, theirtag, needsxpidf);
1702 if (tagadded) {
1703 gchar *to = g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg, "To"), ourtag);
1704 sipmsg_remove_header(msg, "To");
1705 sipmsg_add_header(msg, "To", to);
1706 g_free(to);
1708 if (expire)
1709 watcher->expire = time(NULL) + strtol(expire, NULL, 10);
1710 else
1711 watcher->expire = time(NULL) + 600;
1712 //Fixxxer
1713 sipmsg_remove_header(msg, "Contact");
1714 tmp = get_contact(sip);
1715 sipmsg_add_header(msg, "Contact", tmp);
1716 g_free(tmp);
1717 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);
1718 send_sip_response(sip->gc, msg, 200, "Ok", NULL);
1719 send_notify(sip, watcher);
1720 privend:
1721 g_free(from);
1722 g_free(theirtag);
1723 g_free(ourtag);
1724 g_free(callid);
1725 g_free(expire);
1728 static void process_input_message(struct sipe_account_data *sip, struct sipmsg *msg)
1730 gboolean found = FALSE;
1731 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg->response,msg->method);
1732 if (msg->response == 0) { /* request */
1733 if (!strcmp(msg->method, "MESSAGE")) {
1734 process_incoming_message(sip, msg);
1735 found = TRUE;
1736 } else if (!strcmp(msg->method, "NOTIFY")) {
1737 purple_debug_info("sipe","send->process_incoming_notify\n");
1738 process_incoming_notify(sip, msg);
1739 found = TRUE;
1740 } else if (!strcmp(msg->method, "SUBSCRIBE")) {
1741 purple_debug_info("sipe","send->process_incoming_subscribe\n");
1742 process_incoming_subscribe(sip, msg);
1743 found = TRUE;
1744 } else if (!strcmp(msg->method, "INVITE")) {
1745 purple_debug_info("sipe","not calling unfinished send->process_incoming_invite\n");
1746 //process_incoming_invite(sip, msg);
1747 found = TRUE;
1748 } else if (!strcmp(msg->method, "INFO")) {
1749 // TODO implement this - keyboard activity
1750 found = TRUE;
1751 } else if (!strcmp(msg->method, "ACK")) {
1752 found = TRUE;
1753 } else {
1754 send_sip_response(sip->gc, msg, 501, "Not implemented", NULL);
1756 } else { /* response */
1757 struct transaction *trans = transactions_find(sip, msg);
1758 if (trans) {
1759 if (msg->response == 407) {
1760 gchar *resend, *auth, *ptmp;
1762 if (sip->proxy.retries > 30) return;
1763 sip->proxy.retries++;
1764 /* do proxy authentication */
1766 ptmp = sipmsg_find_header(msg, "Proxy-Authenticate");
1768 fill_auth(sip, ptmp, &sip->proxy);
1769 auth = auth_header(sip, &sip->proxy, trans->msg, TRUE);
1770 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
1771 sipmsg_add_header_pos(trans->msg, "Proxy-Authorization", auth, 5);
1772 g_free(auth);
1773 resend = sipmsg_to_string(trans->msg);
1774 /* resend request */
1775 sendout_pkt(sip->gc, resend);
1776 g_free(resend);
1777 } else {
1778 if (msg->response == 100) {
1779 /* ignore provisional response */
1780 purple_debug_info("sipe", "got trying response\n");
1781 } else {
1782 sip->proxy.retries = 0;
1783 if (!strcmp(trans->msg->method, "REGISTER")) {
1784 if (msg->response == 401) sip->registrar.retries++;
1785 else sip->registrar.retries = 0;
1786 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip->cseq);
1787 } else {
1788 if (msg->response == 401) {
1789 gchar *resend, *auth, *ptmp;
1791 if (sip->registrar.retries > 4) return;
1792 sip->registrar.retries++;
1794 if (!purple_account_get_bool(sip->account, "krb5", FALSE)) {
1795 ptmp = sipmsg_find_auth_header(msg, "NTLM");
1796 } else {
1797 ptmp = sipmsg_find_auth_header(msg, "Kerberos");
1800 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - Auth header: %s\r\n", ptmp);
1802 fill_auth(sip, ptmp, &sip->registrar);
1803 auth = auth_header(sip, &sip->registrar, trans->msg, TRUE);
1804 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
1805 sipmsg_add_header(trans->msg, "Proxy-Authorization", auth);
1807 //sipmsg_remove_header(trans->msg, "Authorization");
1808 //sipmsg_add_header(trans->msg, "Authorization", auth);
1809 g_free(auth);
1810 resend = sipmsg_to_string(trans->msg);
1811 /* resend request */
1812 sendout_pkt(sip->gc, resend);
1813 g_free(resend);
1816 if (trans->callback) {
1817 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - we have a transaction callback\r\n");
1818 /* call the callback to process response*/
1819 (trans->callback)(sip, msg, trans);
1821 /* Not sure if this is needed or what needs to be done
1822 but transactions seem to be removed prematurely so
1823 this only removes them if the response is 200 OK */
1824 purple_debug(PURPLE_DEBUG_MISC, "sipe", "process_input_message - removing CSeq %d\r\n", sip->cseq);
1825 transactions_remove(sip, trans);
1829 found = TRUE;
1830 /* This is done because in an OCS2007 server trace the MS
1831 * Communicator client seems to reset the CSeq after an OK */
1833 //sip->cseq=1;
1834 } else {
1835 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received response to unknown transaction");
1838 if (!found) {
1839 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received a unknown sip message with method %s and response %d\n", msg->method, msg->response);
1843 static void process_input(struct sipe_account_data *sip, struct sip_connection *conn)
1845 char *cur;
1846 char *dummy;
1847 struct sipmsg *msg;
1848 int restlen;
1849 cur = conn->inbuf;
1851 /* according to the RFC remove CRLF at the beginning */
1852 while (*cur == '\r' || *cur == '\n') {
1853 cur++;
1855 if (cur != conn->inbuf) {
1856 memmove(conn->inbuf, cur, conn->inbufused - (cur - conn->inbuf));
1857 conn->inbufused = strlen(conn->inbuf);
1860 /* Received a full Header? */
1861 if ((cur = strstr(conn->inbuf, "\r\n\r\n")) != NULL) {
1862 time_t currtime = time(NULL);
1863 cur += 2;
1864 cur[0] = '\0';
1865 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), conn->inbuf);
1866 msg = sipmsg_parse_header(conn->inbuf);
1867 cur[0] = '\r';
1868 cur += 2;
1869 restlen = conn->inbufused - (cur - conn->inbuf);
1870 if (restlen >= msg->bodylen) {
1871 dummy = g_malloc(msg->bodylen + 1);
1872 memcpy(dummy, cur, msg->bodylen);
1873 dummy[msg->bodylen] = '\0';
1874 msg->body = dummy;
1875 cur += msg->bodylen;
1876 memmove(conn->inbuf, cur, conn->inbuflen - (cur - conn->inbuf));
1877 conn->inbufused = strlen(conn->inbuf);
1878 } else {
1879 sipmsg_free(msg);
1880 return;
1883 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
1884 //printf ("\nWe think MIC for incoming should be: %s\n\n", mic);
1885 //g_free(mic);
1886 //if (purple_ntlm_authorized()) {
1887 if (1) {
1888 struct sipmsg_breakdown msgbd;
1889 msgbd.msg = msg;
1890 sipmsg_breakdown_parse(&msgbd, sip->registrar.realm, sip->registrar.target);
1891 gchar * signature_input_str = sipmsg_breakdown_get_string(&msgbd);
1893 printf ("Have signature_input_str for incoming msg: %s\n", signature_input_str);
1894 if (signature_input_str != NULL) {
1895 guint64 srand = g_ascii_strtoull (msgbd.rand, NULL, 16);
1896 printf ("Have srand = %ld\n", srand);
1897 msg->signature = purple_ntlm_signature_make (signature_input_str, srand, sipmsg_find_part_of_header(sipmsg_find_header(msg, "Authentication-Info"), "rspauth=\"", "\"", NULL));
1900 g_free(signature_input_str);
1901 sipmsg_breakdown_free(&msgbd);
1904 } else {
1905 printf ("registrar ntlm_key is null. proxies? %i\n", sip->proxy.ntlm_key == NULL);
1908 process_input_message(sip, msg);
1910 } else {
1911 purple_debug(PURPLE_DEBUG_MISC, "sipe", "received a incomplete sip msg: %s\n", conn->inbuf);
1915 static void sipe_udp_process(gpointer data, gint source, PurpleInputCondition con)
1917 PurpleConnection *gc = data;
1918 struct sipe_account_data *sip = gc->proto_data;
1919 struct sipmsg *msg;
1920 int len;
1921 time_t currtime;
1923 static char buffer[65536];
1924 if ((len = recv(source, buffer, sizeof(buffer) - 1, 0)) > 0) {
1925 buffer[len] = '\0';
1926 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), buffer);
1927 msg = sipmsg_parse_msg(buffer);
1928 if (msg) process_input_message(sip, msg);
1932 static void sipe_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
1934 PurpleConnection *gc = data;
1935 struct sipe_account_data *sip = gc->proto_data;
1936 struct sip_connection *conn = NULL;
1937 int len;
1938 static char buf[4096];
1940 /* TODO: It should be possible to make this check unnecessary */
1941 if (!PURPLE_CONNECTION_IS_VALID(gc)) {
1942 purple_ssl_close(gsc);
1943 return;
1946 conn = connection_find(sip, sip->gsc->fd);
1947 if (!conn) {
1948 purple_debug_error("sipe", "Connection not found!\n");
1949 purple_ssl_close(gsc);
1950 return;
1954 if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
1955 conn->inbuflen += SIMPLE_BUF_INC;
1956 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
1959 len = purple_ssl_read(gsc, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1);
1961 if (len < 0 && errno == EAGAIN) {
1962 /* Try again later */
1963 return;
1964 } else if (len < 0) {
1965 purple_debug_info("sipe", "sipe_input_cb_ssl: read error\n");
1966 connection_remove(sip, sip->gsc->fd);
1967 if (sip->fd == gsc->fd) sip->fd = -1;
1968 return;
1969 } else if (len == 0) {
1970 purple_connection_error(gc, _("Server has disconnected"));
1971 connection_remove(sip, sip->gsc->fd);
1972 if (sip->fd == gsc->fd) sip->fd = -1;
1973 return;
1976 conn->inbufused += len;
1977 conn->inbuf[conn->inbufused] = '\0';
1979 process_input(sip, conn);
1983 static void sipe_input_cb(gpointer data, gint source, PurpleInputCondition cond)
1985 PurpleConnection *gc = data;
1986 struct sipe_account_data *sip = gc->proto_data;
1987 int len;
1988 struct sip_connection *conn = connection_find(sip, source);
1989 if (!conn) {
1990 purple_debug_error("sipe", "Connection not found!\n");
1991 return;
1994 if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
1995 conn->inbuflen += SIMPLE_BUF_INC;
1996 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
1999 len = read(source, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1);
2001 if (len < 0 && errno == EAGAIN)
2002 return;
2003 else if (len <= 0) {
2004 purple_debug_info("sipe", "sipe_input_cb: read error\n");
2005 connection_remove(sip, source);
2006 if (sip->fd == source) sip->fd = -1;
2007 return;
2010 conn->inbufused += len;
2011 conn->inbuf[conn->inbufused] = '\0';
2013 process_input(sip, conn);
2016 /* Callback for new connections on incoming TCP port */
2017 static void sipe_newconn_cb(gpointer data, gint source, PurpleInputCondition cond)
2019 PurpleConnection *gc = data;
2020 struct sipe_account_data *sip = gc->proto_data;
2021 struct sip_connection *conn;
2023 int newfd = accept(source, NULL, NULL);
2025 conn = connection_create(sip, newfd);
2027 conn->inputhandler = purple_input_add(newfd, PURPLE_INPUT_READ, sipe_input_cb, gc);
2030 static void login_cb(gpointer data, gint source, const gchar *error_message)
2032 PurpleConnection *gc = data;
2033 struct sipe_account_data *sip;
2034 struct sip_connection *conn;
2036 if (!PURPLE_CONNECTION_IS_VALID(gc))
2038 if (source >= 0)
2039 close(source);
2040 return;
2043 if (source < 0) {
2044 purple_connection_error(gc, _("Could not connect"));
2045 return;
2048 sip = gc->proto_data;
2049 sip->fd = source;
2051 conn = connection_create(sip, source);
2053 sip->registertimeout = purple_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
2055 do_register(sip);
2057 conn->inputhandler = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_input_cb, gc);
2060 static void login_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
2062 PurpleConnection *gc = data;
2063 struct sipe_account_data *sip;
2064 struct sip_connection *conn;
2066 if (!PURPLE_CONNECTION_IS_VALID(gc))
2068 purple_ssl_close(gsc);
2069 return;
2072 sip = gc->proto_data;
2073 sip->fd = gsc->fd;
2074 conn = connection_create(sip, sip->fd);
2075 sip->listenport = purple_network_get_port_from_fd(sip->fd);
2076 sip->listenfd = sip->fd;
2077 sip->registertimeout = purple_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
2079 do_register(sip);
2081 purple_ssl_input_add(gsc, sipe_input_cb_ssl, gc);
2084 static guint sipe_ht_hash_nick(const char *nick)
2086 char *lc = g_utf8_strdown(nick, -1);
2087 guint bucket = g_str_hash(lc);
2088 g_free(lc);
2090 return bucket;
2093 static gboolean sipe_ht_equals_nick(const char *nick1, const char *nick2)
2095 return (purple_utf8_strcasecmp(nick1, nick2) == 0);
2098 static void sipe_udp_host_resolved_listen_cb(int listenfd, gpointer data)
2100 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2102 sip->listen_data = NULL;
2104 if (listenfd == -1) {
2105 purple_connection_error(sip->gc, _("Could not create listen socket"));
2106 return;
2109 sip->fd = listenfd;
2111 sip->listenport = purple_network_get_port_from_fd(sip->fd);
2112 sip->listenfd = sip->fd;
2114 sip->listenpa = purple_input_add(sip->fd, PURPLE_INPUT_READ, sipe_udp_process, sip->gc);
2116 sip->resendtimeout = purple_timeout_add(2500, (GSourceFunc) resend_timeout, sip);
2117 sip->registertimeout = purple_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
2118 do_register(sip);
2121 static void sipe_udp_host_resolved(GSList *hosts, gpointer data, const char *error_message)
2123 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2124 int addr_size;
2126 sip->query_data = NULL;
2128 if (!hosts || !hosts->data) {
2129 purple_connection_error(sip->gc, _("Couldn't resolve host"));
2130 return;
2133 addr_size = GPOINTER_TO_INT(hosts->data);
2134 hosts = g_slist_remove(hosts, hosts->data);
2135 memcpy(&(sip->serveraddr), hosts->data, addr_size);
2136 g_free(hosts->data);
2137 hosts = g_slist_remove(hosts, hosts->data);
2138 while (hosts) {
2139 hosts = g_slist_remove(hosts, hosts->data);
2140 g_free(hosts->data);
2141 hosts = g_slist_remove(hosts, hosts->data);
2144 /* create socket for incoming connections */
2145 sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_DGRAM,
2146 sipe_udp_host_resolved_listen_cb, sip);
2147 if (sip->listen_data == NULL) {
2148 purple_connection_error(sip->gc, _("Could not create listen socket"));
2149 return;
2153 static void sipe_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
2154 gpointer data)
2156 PurpleConnection *gc = data;
2157 struct sipe_account_data *sip;
2159 /* If the connection is already disconnected, we don't need to do anything else */
2160 if (!PURPLE_CONNECTION_IS_VALID(gc))
2161 return;
2163 sip = gc->proto_data;
2164 sip->gsc = NULL;
2166 switch(error) {
2167 case PURPLE_SSL_CONNECT_FAILED:
2168 purple_connection_error(gc, _("Connection Failed"));
2169 break;
2170 case PURPLE_SSL_HANDSHAKE_FAILED:
2171 purple_connection_error(gc, _("SSL Handshake Failed"));
2172 break;
2176 static void
2177 sipe_tcp_connect_listen_cb(int listenfd, gpointer data)
2179 struct sipe_account_data *sip = (struct sipe_account_data*) data;
2180 PurpleProxyConnectData *connect_data;
2182 sip->listen_data = NULL;
2184 sip->listenfd = listenfd;
2185 if (sip->listenfd == -1) {
2186 purple_connection_error(sip->gc, _("Could not create listen socket"));
2187 return;
2190 purple_debug_info("sipe", "listenfd: %d\n", sip->listenfd);
2191 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2192 sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2193 sip->listenpa = purple_input_add(sip->listenfd, PURPLE_INPUT_READ,
2194 sipe_newconn_cb, sip->gc);
2195 purple_debug_info("sipe", "connecting to %s port %d\n",
2196 sip->realhostname, sip->realport);
2197 /* open tcp connection to the server */
2198 connect_data = purple_proxy_connect(sip->gc, sip->account, sip->realhostname,
2199 sip->realport, login_cb, sip->gc);
2201 if (connect_data == NULL) {
2202 purple_connection_error(sip->gc, _("Couldn't create socket"));
2208 static void srvresolved(PurpleSrvResponse *resp, int results, gpointer data)
2210 struct sipe_account_data *sip;
2211 gchar *hostname;
2212 int port;
2214 sip = data;
2215 sip->srv_query_data = NULL;
2217 port = purple_account_get_int(sip->account, "port", 0);
2219 /* find the host to connect to */
2220 if (results) {
2221 hostname = g_strdup(resp->hostname);
2222 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - SRV hostname: %s\r\n", hostname);
2223 if (!port)
2224 port = resp->port;
2225 g_free(resp);
2226 } else {
2227 if (!purple_account_get_bool(sip->account, "useproxy", FALSE)) {
2228 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - using sipdomain\r\n");
2229 hostname = g_strdup(sip->sipdomain);
2230 } else {
2231 purple_debug(PURPLE_DEBUG_MISC, "sipe", "srvresolved - using specified SIP proxy\r\n");
2232 hostname = g_strdup(purple_account_get_string(sip->account, "proxy", sip->sipdomain));
2236 sip->realhostname = hostname;
2237 sip->realport = port;
2238 if (!sip->realport) sip->realport = 5060;
2240 /* TCP case */
2241 //if (!sip->udp) {
2242 // /* create socket for incoming connections */
2243 // sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_STREAM,
2244 // sipe_tcp_connect_listen_cb, sip);
2245 // if (sip->listen_data == NULL) {
2246 // purple_connection_error(sip->gc, _("Could not create listen socket"));
2247 // return;
2248 // }
2249 //} else { /* UDP */
2250 // purple_debug_info("sipe", "using udp with server %s and port %d\n", hostname, port);
2252 // sip->query_data = purple_dnsquery_a(hostname, port, sipe_udp_host_resolved, sip);
2253 // if (sip->query_data == NULL) {
2254 // purple_connection_error(sip->gc, _("Could not resolve hostname"));
2255 // }
2259 static void sipe_login(PurpleAccount *account)
2261 PurpleConnection *gc;
2262 struct sipe_account_data *sip;
2263 gchar **userserver;
2264 gchar *hosttoconnect;
2266 const char *username = purple_account_get_username(account);
2267 gc = purple_account_get_connection(account);
2269 if (strpbrk(username, " \t\v\r\n") != NULL) {
2270 gc->wants_to_die = TRUE;
2271 purple_connection_error(gc, _("SIP Exchange usernames may not contain whitespaces"));
2272 return;
2275 if (!purple_account_get_bool(account, "ssl", FALSE)){
2276 if (!purple_ssl_is_supported())
2278 gc->wants_to_die = TRUE;
2279 purple_connection_error(gc,
2280 _("SSL support is needed for SSL/TLS support. Please install a supported "
2281 "SSL library."));
2282 return;
2287 gc->proto_data = sip = g_new0(struct sipe_account_data, 1);
2288 sip->gc = gc;
2289 sip->account = account;
2290 sip->registerexpire = 900;
2291 sip->udp = purple_account_get_bool(account, "udp", FALSE);
2292 sip->use_ssl = purple_account_get_bool(account, "ssl", FALSE);
2294 purple_debug_info("sipe", "sip->use_ssl->%d\n", sip->use_ssl);
2296 /* TODO: is there a good default grow size? */
2297 if (!sip->udp)
2298 sip->txbuf = purple_circ_buffer_new(0);
2300 userserver = g_strsplit(username, "@", 2);
2301 purple_connection_set_display_name(gc, userserver[0]);
2302 sip->username = g_strdup(g_strjoin("@", userserver[0], userserver[1], NULL));
2303 sip->sipdomain = g_strdup(userserver[1]);
2304 sip->password = g_strdup(purple_connection_get_password(gc));
2305 g_strfreev(userserver);
2307 if (sip->use_ssl) {
2308 // Communicator queries _sipinternaltls._tcp.domain.com and uses that
2309 // information to connect to the OCS server.
2311 // XXX FIXME: eventually we should also query for sipexternaltls as well
2312 // if Pidgin is not on the local LAN
2313 // This doesn't quite work as advertised yet so make sure your have
2314 // your OCS FQDN in the proxy setting in the SIPE account settings
2316 sip->srv_query_data = purple_srv_resolve("sipinternaltls", "tcp", sip->sipdomain, srvresolved, sip);
2319 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_login - realhostname: %s\r\n", sip->realhostname);
2321 sip->buddies = g_hash_table_new((GHashFunc)sipe_ht_hash_nick, (GEqualFunc)sipe_ht_equals_nick);
2323 purple_connection_update_progress(gc, _("Connecting"), 1, 2);
2325 /* TODO: Set the status correctly. */
2326 sip->status = g_strdup("available");
2328 if (!purple_account_get_bool(account, "useproxy", FALSE)) {
2329 purple_debug(PURPLE_DEBUG_MISC, "sipe", "sipe_login - checking realhostname again: %s\r\n", sip->realhostname);
2330 hosttoconnect = g_strdup(sip->sipdomain);
2331 } else {
2332 hosttoconnect = g_strdup(purple_account_get_string(account, "proxy", sip->sipdomain));
2335 /*SSL*/
2336 purple_debug_info("sipe", "HosttoConnect->%s\n", hosttoconnect);
2338 if (sip->use_ssl){
2339 sip->gsc = purple_ssl_connect(account,hosttoconnect, purple_account_get_int(account, "port", 5061), login_cb_ssl, sipe_ssl_connect_failure, gc);
2341 else{
2342 sip->srv_query_data = purple_srv_resolve("sip",
2343 sip->udp ? "udp" : "tcp", hosttoconnect, srvresolved, sip);
2346 g_free(hosttoconnect);
2349 static void sipe_close(PurpleConnection *gc)
2351 struct sipe_account_data *sip = gc->proto_data;
2353 if (sip) {
2354 /* unregister */
2355 do_register_exp(sip, 0);
2356 connection_free_all(sip);
2358 if (sip->query_data != NULL)
2359 purple_dnsquery_destroy(sip->query_data);
2361 if (sip->srv_query_data != NULL)
2362 purple_srv_cancel(sip->srv_query_data);
2364 if (sip->listen_data != NULL)
2365 purple_network_listen_cancel(sip->listen_data);
2367 g_free(sip->sipdomain);
2368 g_free(sip->username);
2369 g_free(sip->password);
2370 g_free(sip->registrar.nonce);
2371 g_free(sip->registrar.opaque);
2372 g_free(sip->registrar.target);
2373 g_free(sip->registrar.realm);
2374 g_free(sip->registrar.digest_session_key);
2375 g_free(sip->proxy.nonce);
2376 g_free(sip->proxy.opaque);
2377 g_free(sip->proxy.target);
2378 g_free(sip->proxy.realm);
2379 g_free(sip->proxy.digest_session_key);
2380 if (sip->txbuf)
2381 purple_circ_buffer_destroy(sip->txbuf);
2382 g_free(sip->realhostname);
2383 if (sip->listenpa) purple_input_remove(sip->listenpa);
2384 if (sip->tx_handler) purple_input_remove(sip->tx_handler);
2385 if (sip->resendtimeout) purple_timeout_remove(sip->resendtimeout);
2386 if (sip->registertimeout) purple_timeout_remove(sip->registertimeout);
2388 g_free(gc->proto_data);
2389 gc->proto_data = NULL;
2392 /* not needed since privacy is checked for every subscribe */
2393 static void dummy_add_deny(PurpleConnection *gc, const char *name) {
2396 static void dummy_permit_deny(PurpleConnection *gc)
2400 static gboolean sipe_plugin_load(PurplePlugin *plugin)
2402 return TRUE;
2406 static gboolean sipe_plugin_unload(PurplePlugin *plugin)
2408 return TRUE;
2412 static void sipe_plugin_destroy(PurplePlugin *plugin)
2416 static PurplePlugin *my_protocol = NULL;
2418 static PurplePluginProtocolInfo prpl_info =
2421 NULL, /* user_splits */
2422 NULL, /* protocol_options */
2423 NO_BUDDY_ICONS, /* icon_spec */
2424 sipe_list_icon, /* list_icon */
2425 NULL, /* list_emblems */
2426 NULL, /* status_text */
2427 NULL, /* tooltip_text */
2428 sipe_status_types, /* away_states */
2429 NULL, /* blist_node_menu */
2430 NULL, /* chat_info */
2431 NULL, /* chat_info_defaults */
2432 sipe_login, /* login */
2433 sipe_close, /* close */
2434 sipe_im_send, /* send_im */
2435 NULL, /* set_info */
2436 // sipe_typing, /* send_typing */
2437 NULL, /* send_typing */
2438 NULL, /* get_info */
2439 sipe_set_status, /* set_status */
2440 NULL, /* set_idle */
2441 NULL, /* change_passwd */
2442 sipe_add_buddy, /* add_buddy */
2443 NULL, /* add_buddies */
2444 sipe_remove_buddy, /* remove_buddy */
2445 NULL, /* remove_buddies */
2446 dummy_add_deny, /* add_permit */
2447 dummy_add_deny, /* add_deny */
2448 dummy_add_deny, /* rem_permit */
2449 dummy_add_deny, /* rem_deny */
2450 dummy_permit_deny, /* set_permit_deny */
2451 NULL, /* join_chat */
2452 NULL, /* reject_chat */
2453 NULL, /* get_chat_name */
2454 NULL, /* chat_invite */
2455 NULL, /* chat_leave */
2456 NULL, /* chat_whisper */
2457 NULL, /* chat_send */
2458 sipe_keep_alive, /* keepalive */
2459 NULL, /* register_user */
2460 NULL, /* get_cb_info */
2461 NULL, /* get_cb_away */
2462 NULL, /* alias_buddy */
2463 NULL, /* group_buddy */
2464 NULL, /* rename_group */
2465 NULL, /* buddy_free */
2466 NULL, /* convo_closed */
2467 NULL, /* normalize */
2468 NULL, /* set_buddy_icon */
2469 NULL, /* remove_group */
2470 NULL, /* get_cb_real_name */
2471 NULL, /* set_chat_topic */
2472 NULL, /* find_blist_chat */
2473 NULL, /* roomlist_get_list */
2474 NULL, /* roomlist_cancel */
2475 NULL, /* roomlist_expand_category */
2476 NULL, /* can_receive_file */
2477 NULL, /* send_file */
2478 NULL, /* new_xfer */
2479 NULL, /* offline_message */
2480 NULL, /* whiteboard_prpl_ops */
2481 sipe_send_raw, /* send_raw */
2485 static PurplePluginInfo info = {
2486 PURPLE_PLUGIN_MAGIC,
2487 PURPLE_MAJOR_VERSION,
2488 PURPLE_MINOR_VERSION,
2489 PURPLE_PLUGIN_PROTOCOL, /**< type */
2490 NULL, /**< ui_requirement */
2491 0, /**< flags */
2492 NULL, /**< dependencies */
2493 PURPLE_PRIORITY_DEFAULT, /**< priority */
2494 "prpl-sipe", /**< id */
2495 "SIPE", /**< name */
2496 VERSION, /**< version */
2497 N_("SIP/SIMPLE Exchange Protocol Plugin"), /** summary */
2498 N_("The SIP/SIMPLE Exchange Protocol Plugin"), /** description */
2499 "Anibal Avelar <avelar@gmail.com>", /**< author */
2500 PURPLE_WEBSITE, /**< homepage */
2501 sipe_plugin_load, /**< load */
2502 sipe_plugin_unload, /**< unload */
2503 sipe_plugin_destroy, /**< destroy */
2504 NULL, /**< ui_info */
2505 &prpl_info, /**< extra_info */
2506 NULL,
2507 NULL,
2508 NULL,
2509 NULL,
2510 NULL,
2511 NULL
2514 static void init_plugin(PurplePlugin *plugin)
2516 PurpleAccountUserSplit *split;
2517 PurpleAccountOption *option;
2519 purple_plugin_register(plugin);
2521 //split = purple_account_user_split_new(_("Server"), "", '@');
2522 //prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
2523 option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2524 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2525 option = purple_account_option_string_new(_("Proxy Server"), "proxy", "");
2526 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2528 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
2529 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2531 option = purple_account_option_bool_new(_("Use SSL/TLS"), "ssl", FALSE);
2532 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,option);
2534 option = purple_account_option_bool_new(_("Use UDP"), "udp", FALSE);
2535 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2537 option = purple_account_option_int_new(_("Connect port"), "port", 5060);
2538 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2540 option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
2541 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2543 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
2544 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
2545 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2547 /*option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2548 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2549 option = purple_account_option_string_new(_("Proxy"), "proxy", "");
2550 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2551 option = purple_account_option_string_new(_("Auth User"), "authuser", "");
2552 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2553 option = purple_account_option_string_new(_("Auth Domain"), "authdomain", "");
2554 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2555 my_protocol = plugin;
2559 /* I had to redefined the function for it load, but works */
2560 gboolean purple_init_plugin(PurplePlugin *plugin){
2561 plugin->info = &(info);
2562 init_plugin((plugin));
2563 sipe_plugin_load((plugin));
2564 return purple_plugin_register(plugin);