SSL/TLS Support. Some bug were fixed too.
[siplcs.git] / src / sipe.c
blob0e5d3b76c8e12a2dcdc9b33078b2278e1055b174
1 /**
2 * @file sip-exchange.c
4 * gaim
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 #include "sip-internal.h"
30 #include "accountopt.h"
31 #include "blist.h"
32 #include "conversation.h"
33 #include "dnsquery.h"
34 #include "debug.h"
35 #include "notify.h"
36 #include "privacy.h"
37 #include "prpl.h"
38 #include "plugin.h"
39 #include "util.h"
40 #include "version.h"
41 #include "network.h"
42 #include "xmlnode.h"
44 #include "sipe.h"
45 #include "sip-ntlm.h"
47 #include "sipmsg.h"
48 #include "dnssrv.h"
50 #include "gaim-compat.h"
52 static char *gentag() {
53 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
56 static char *genbranch() {
57 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
58 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
59 rand() & 0xFFFF, rand() & 0xFFFF);
62 static char *gencallid() {
63 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
64 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
65 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
66 rand() & 0xFFFF, rand() & 0xFFFF);
69 static const char *sipe_list_icon(GaimAccount *a, GaimBuddy *b) {
70 return "sipe";
73 static void sipe_keep_alive(GaimConnection *gc) {
74 struct sipe_account_data *sip = gc->proto_data;
75 if(sip->udp) { /* in case of UDP send a packet only with a 0 byte to
76 remain in the NAT table */
77 gchar buf[2] = {0, 0};
78 gaim_debug_info("sipe", "sending keep alive\n");
79 sendto(sip->fd, buf, 1, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in));
81 return;
84 static gboolean process_register_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc);
86 static void sipe_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
87 static void sipe_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
88 gpointer data);
90 static void send_notify(struct sipe_account_data *sip, struct sipe_watcher *);
92 static void send_service(struct sipe_account_data *sip);
94 static void do_notifies(struct sipe_account_data *sip) {
95 GSList *tmp = sip->watcher;
96 gaim_debug_info("sipe", "do_notifies()\n");
97 //if((sip->republish != -1) || sip->republish < time(NULL)) {
98 // if(gaim_account_get_bool(sip->account, "doservice", TRUE)) {
99 // send_service(sip);
100 // }
103 while(tmp) {
104 gaim_debug_info("sipe", "notifying %s\n", ((struct sipe_watcher*)tmp->data)->name);
105 send_notify(sip, tmp->data);
106 tmp = tmp->next;
110 static void sipe_set_status(GaimAccount *account, GaimStatus *status) {
111 GaimStatusPrimitive primitive = gaim_status_type_get_primitive(gaim_status_get_type(status));
112 struct sipe_account_data *sip = NULL;
114 if (!gaim_status_is_active(status))
115 return;
117 if (account->gc)
118 sip = account->gc->proto_data;
120 if (sip)
122 g_free(sip->status);
123 if (primitive == GAIM_STATUS_AVAILABLE)
124 sip->status = g_strdup("available");
125 else
126 sip->status = g_strdup("busy");
128 do_notifies(sip);
132 static struct sip_connection *connection_find(struct sipe_account_data *sip, int fd) {
133 struct sip_connection *ret = NULL;
134 GSList *entry = sip->openconns;
135 while(entry) {
136 ret = entry->data;
137 if(ret->fd == fd) return ret;
138 entry = entry->next;
140 return NULL;
143 static struct sipe_watcher *watcher_find(struct sipe_account_data *sip,
144 const gchar *name) {
145 struct sipe_watcher *watcher;
146 GSList *entry = sip->watcher;
147 while(entry) {
148 watcher = entry->data;
149 if(!strcmp(name, watcher->name)) return watcher;
150 entry = entry->next;
152 return NULL;
155 static struct sipe_watcher *watcher_create(struct sipe_account_data *sip,
156 const gchar *name, const gchar *callid, const gchar *ourtag,
157 const gchar *theirtag, gboolean needsxpidf) {
158 struct sipe_watcher *watcher = g_new0(struct sipe_watcher, 1);
159 watcher->name = g_strdup(name);
160 watcher->dialog.callid = g_strdup(callid);
161 watcher->dialog.ourtag = g_strdup(ourtag);
162 watcher->dialog.theirtag = g_strdup(theirtag);
163 watcher->needsxpidf = needsxpidf;
164 sip->watcher = g_slist_append(sip->watcher, watcher);
165 return watcher;
168 static void watcher_remove(struct sipe_account_data *sip, const gchar *name) {
169 struct sipe_watcher *watcher = watcher_find(sip, name);
170 sip->watcher = g_slist_remove(sip->watcher, watcher);
171 g_free(watcher->name);
172 g_free(watcher->dialog.callid);
173 g_free(watcher->dialog.ourtag);
174 g_free(watcher->dialog.theirtag);
175 g_free(watcher);
178 static struct sip_connection *connection_create(struct sipe_account_data *sip, int fd) {
179 struct sip_connection *ret = g_new0(struct sip_connection, 1);
180 ret->fd = fd;
181 sip->openconns = g_slist_append(sip->openconns, ret);
182 return ret;
185 static void connection_remove(struct sipe_account_data *sip, int fd) {
186 struct sip_connection *conn = connection_find(sip, fd);
187 sip->openconns = g_slist_remove(sip->openconns, conn);
188 if(conn->inputhandler) gaim_input_remove(conn->inputhandler);
189 g_free(conn->inbuf);
190 g_free(conn);
193 static void connection_free_all(struct sipe_account_data *sip) {
194 struct sip_connection *ret = NULL;
195 GSList *entry = sip->openconns;
196 while(entry) {
197 ret = entry->data;
198 connection_remove(sip, ret->fd);
199 entry = sip->openconns;
203 static void sipe_add_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group)
205 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
206 struct sipe_buddy *b;
207 if(strncmp("sip:", buddy->name, 4)) {
208 gchar *buf = g_strdup_printf("sip:%s", buddy->name);
209 gaim_blist_rename_buddy(buddy, buf);
210 g_free(buf);
212 if(!g_hash_table_lookup(sip->buddies, buddy->name)) {
213 b = g_new0(struct sipe_buddy, 1);
214 gaim_debug_info("sipe", "sipe_add_buddy %s\n", buddy->name);
215 b->name = g_strdup(buddy->name);
216 g_hash_table_insert(sip->buddies, b->name, b);
217 } else {
218 gaim_debug_info("sipe", "buddy %s already in internal list\n", buddy->name);
222 static void sipe_get_buddies(GaimConnection *gc) {
223 GaimBlistNode *gnode, *cnode, *bnode;
225 gaim_debug_info("sipe", "sipe_get_buddies\n");
227 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
228 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) continue;
229 for(cnode = gnode->child; cnode; cnode = cnode->next) {
230 if(!GAIM_BLIST_NODE_IS_CONTACT(cnode)) continue;
231 for(bnode = cnode->child; bnode; bnode = bnode->next) {
232 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) continue;
233 if(((GaimBuddy*)bnode)->account == gc->account)
234 sipe_add_buddy(gc, (GaimBuddy*)bnode, (GaimGroup *)gnode);
240 static void sipe_remove_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group)
242 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
243 struct sipe_buddy *b = g_hash_table_lookup(sip->buddies, buddy->name);
244 g_hash_table_remove(sip->buddies, buddy->name);
245 g_free(b->name);
246 g_free(b);
249 static GList *sipe_status_types(GaimAccount *acc) {
250 GaimStatusType *type;
251 GList *types = NULL;
253 type = gaim_status_type_new_with_attrs(
254 GAIM_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
255 "message", _("Message"), gaim_value_new(GAIM_TYPE_STRING),
256 NULL);
257 types = g_list_append(types, type);
259 type = gaim_status_type_new_full(
260 GAIM_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE);
261 types = g_list_append(types, type);
263 return types;
266 static gchar *auth_header(struct sipe_account_data *sip,
267 struct sip_auth *auth, const gchar *method, const gchar *target) {
268 gchar noncecount[9];
269 gchar *response;
270 gchar *ret;
271 gchar *tmp;
272 const char *authdomain;
273 const char *authuser;
275 authdomain = gaim_account_get_string(sip->account, "authdomain", "");
276 authuser = gaim_account_get_string(sip->account, "authuser", sip->username);
278 if(!authuser || strlen(authuser) < 1) {
279 authuser = sip->username;
282 if(auth->type == 1) { /* Digest */
283 sprintf(noncecount, "%08d", auth->nc++);
284 response = gaim_cipher_http_digest_calculate_response(
285 "md5", method, target, NULL, NULL,
286 auth->nonce, noncecount, NULL, auth->digest_session_key);
287 gaim_debug(GAIM_DEBUG_MISC, "sipe", "response %s\n", response);
289 ret = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"\r\n", authuser, auth->realm, auth->nonce, target, noncecount, response);
290 g_free(response);
291 return ret;
292 } else if(auth->type == 2) { /* NTLM */
293 if(auth->nc == 3 && auth->nonce) {
294 /* TODO: Don't hardcode "gaim" as the hostname */
295 ret = gaim_ntlm_gen_type3_sipe(authuser, sip->password, "gaim", authdomain, (const guint8 *)auth->nonce, &auth->flags);
296 tmp = g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"\r\n", auth->opaque, auth->realm, auth->target, ret);
297 g_free(ret);
298 return tmp;
300 tmp = g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"\r\n", auth->realm, auth->target);
301 return tmp;
304 sprintf(noncecount, "%08d", auth->nc++);
305 response = gaim_cipher_http_digest_calculate_response(
306 "md5", method, target, NULL, NULL,
307 auth->nonce, noncecount, NULL, auth->digest_session_key);
308 gaim_debug(GAIM_DEBUG_MISC, "sipe", "response %s\n", response);
310 ret = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"\r\n", authuser, auth->realm, auth->nonce, target, noncecount, response);
311 g_free(response);
312 return ret;
315 static char *parse_attribute(const char *attrname, const char *source) {
316 const char *tmp, *tmp2;
317 char *retval = NULL;
318 int len = strlen(attrname);
320 if(!strncmp(source, attrname, len)) {
321 tmp = source + len;
322 tmp2 = g_strstr_len(tmp, strlen(tmp), "\"");
323 if(tmp2)
324 retval = g_strndup(tmp, tmp2 - tmp);
325 else
326 retval = g_strdup(tmp);
329 return retval;
332 static void fill_auth(struct sipe_account_data *sip, gchar *hdr, struct sip_auth *auth) {
333 int i = 0;
334 const char *authuser;
335 char *tmp;
336 gchar **parts;
338 authuser = gaim_account_get_string(sip->account, "authuser", sip->username);
340 if(!authuser || strlen(authuser) < 1) {
341 authuser = sip->username;
344 if(!hdr) {
345 gaim_debug_error("sipe", "fill_auth: hdr==NULL\n");
346 return;
349 if(!g_strncasecmp(hdr, "NTLM", 4)) {
350 gaim_debug_info("sipe", "found NTLM\n");
351 auth->type = 2;
352 parts = g_strsplit(hdr+5, "\", ", 0);
353 i = 0;
354 while(parts[i]) {
355 gaim_debug_info("sipe", "parts[i] %s\n", parts[i]);
356 if((tmp = parse_attribute("gssapi-data=\"", parts[i]))) {
357 auth->nonce = g_memdup(gaim_ntlm_parse_type2_sipe(tmp, &auth->flags), 8);
358 g_free(tmp);
360 if((tmp = parse_attribute("targetname=\"",
361 parts[i]))) {
362 auth->target = tmp;
364 else if((tmp = parse_attribute("realm=\"",
365 parts[i]))) {
366 auth->realm = tmp;
368 else if((tmp = parse_attribute("opaque=\"", parts[i]))) {
369 auth->opaque = tmp;
371 i++;
373 g_strfreev(parts);
374 auth->nc = 1;
375 if(!strstr(hdr, "gssapi-data")) {
376 auth->nc = 1;
377 } else {
378 auth->nc = 3;
380 return;
383 auth->type = 1;
384 parts = g_strsplit(hdr, " ", 0);
385 while(parts[i]) {
386 if((tmp = parse_attribute("nonce=\"", parts[i]))) {
387 auth->nonce = tmp;
389 else if((tmp = parse_attribute("realm=\"", parts[i]))) {
390 auth->realm = tmp;
392 i++;
394 g_strfreev(parts);
396 gaim_debug(GAIM_DEBUG_MISC, "sipe", "nonce: %s realm: %s\n", auth->nonce ? auth->nonce : "(null)", auth->realm ? auth->realm : "(null)");
397 if(auth->realm) {
398 auth->digest_session_key = gaim_cipher_http_digest_calculate_session_key(
399 "md5", authuser, auth->realm, sip->password, auth->nonce, NULL);
401 auth->nc = 1;
405 static void sipe_canwrite_cb(gpointer data, gint source, GaimInputCondition cond) {
406 GaimConnection *gc = data;
407 struct sipe_account_data *sip = gc->proto_data;
408 gsize max_write;
409 gssize written;
411 max_write = gaim_circ_buffer_get_max_read(sip->txbuf);
413 if(max_write == 0) {
414 gaim_input_remove(sip->tx_handler);
415 sip->tx_handler = 0;
416 return;
419 written = write(sip->fd, sip->txbuf->outptr, max_write);
421 if(written < 0 && errno == EAGAIN)
422 written = 0;
423 else if(written <= 0) {
424 /*TODO: do we really want to disconnect on a failure to write?*/
425 gaim_connection_error(gc, _("Could not write"));
426 return;
429 gaim_circ_buffer_mark_read(sip->txbuf, written);
432 static void sipe_canwrite_cb_ssl(gpointer data, PurpleSslConnection *gsc, GaimInputCondition cond) {
433 GaimConnection *gc = data;
434 struct sipe_account_data *sip = gc->proto_data;
435 gsize max_write;
436 gssize written;
438 max_write = gaim_circ_buffer_get_max_read(sip->txbuf);
440 if(max_write == 0) {
441 gaim_input_remove(sip->tx_handler);
442 sip->tx_handler = 0;
443 return;
446 written = purple_ssl_write(sip->gsc, sip->txbuf->outptr, max_write);
448 if(written < 0 && errno == EAGAIN)
449 written = 0;
450 else if(written <= 0) {
451 /*TODO: do we really want to disconnect on a failure to write?*/
452 gaim_connection_error(gc, _("Could not write"));
453 return;
456 gaim_circ_buffer_mark_read(sip->txbuf, written);
459 static void sipe_input_cb(gpointer data, gint source, GaimInputCondition cond);
461 static void send_later_cb(gpointer data, gint source, const gchar *error) {
462 GaimConnection *gc = data;
463 struct sipe_account_data *sip;
464 struct sip_connection *conn;
466 if (!GAIM_CONNECTION_IS_VALID(gc))
468 if (source >= 0)
469 close(source);
470 return;
473 if(source < 0) {
474 gaim_connection_error(gc, _("Could not connect"));
475 return;
478 sip = gc->proto_data;
479 sip->fd = source;
480 sip->connecting = FALSE;
482 sipe_canwrite_cb(gc, sip->fd, GAIM_INPUT_WRITE);
484 /* If there is more to write now, we need to register a handler */
485 if(sip->txbuf->bufused > 0)
486 sip->tx_handler = gaim_input_add(sip->fd, GAIM_INPUT_WRITE,
487 sipe_canwrite_cb, gc);
489 conn = connection_create(sip, source);
490 conn->inputhandler = gaim_input_add(sip->fd, GAIM_INPUT_READ, sipe_input_cb, gc);
493 static void send_later_cb_ssl(gpointer data, PurpleSslConnection *gsc, GaimInputCondition cond) {
494 GaimConnection *gc = data;
495 struct sipe_account_data *sip;
496 struct sip_connection *conn;
498 if (!GAIM_CONNECTION_IS_VALID(gc))
500 purple_ssl_close(gsc);
501 return;
504 sip = gc->proto_data;
505 sip->fd = gsc->fd;
506 sip->connecting = FALSE;
508 sipe_canwrite_cb_ssl(gc, gsc, GAIM_INPUT_WRITE);
510 /* If there is more to write now, we need to register a handler */
511 if(sip->txbuf->bufused > 0)
512 purple_ssl_input_add(gsc, sipe_canwrite_cb_ssl, gc);
514 conn = connection_create(sip, gsc->fd);
515 purple_ssl_input_add(sip->gsc, sipe_input_cb_ssl, gc);
519 static void sendlater(GaimConnection *gc, const char *buf) {
520 struct sipe_account_data *sip = gc->proto_data;
522 if(!sip->connecting) {
523 gaim_debug_info("sipe", "connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport);
524 if(sip->use_ssl){
525 sip->gsc = purple_ssl_connect(sip->account,sip->realhostname, sip->realport, send_later_cb_ssl, sipe_ssl_connect_failure, sip->gc);
527 else{
528 if(gaim_proxy_connect(gc, sip->account, sip->realhostname, sip->realport, send_later_cb, gc) == NULL) {
529 gaim_connection_error(gc, _("Couldn't create socket"));
532 sip->connecting = TRUE;
535 if(gaim_circ_buffer_get_max_read(sip->txbuf) > 0)
536 gaim_circ_buffer_append(sip->txbuf, "\r\n", 2);
538 gaim_circ_buffer_append(sip->txbuf, buf, strlen(buf));
541 static void sendout_pkt(GaimConnection *gc, const char *buf) {
542 struct sipe_account_data *sip = gc->proto_data;
543 time_t currtime = time(NULL);
544 int writelen = strlen(buf);
546 gaim_debug(GAIM_DEBUG_MISC, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime), buf);
547 if(sip->udp) {
548 if(sendto(sip->fd, buf, writelen, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)) < writelen) {
549 gaim_debug_info("sipe", "could not send packet\n");
551 } else {
552 int ret;
553 if(sip->fd < 0) {
554 sendlater(gc, buf);
555 return;
558 if(sip->tx_handler) {
559 ret = -1;
560 errno = EAGAIN;
561 } else{
562 if(sip->gsc){
563 ret = purple_ssl_write(sip->gsc, buf, writelen);
564 }else{
565 ret = write(sip->fd, buf, writelen);
569 if (ret < 0 && errno == EAGAIN)
570 ret = 0;
571 else if(ret <= 0) { /* XXX: When does this happen legitimately? */
572 sendlater(gc, buf);
573 return;
576 if (ret < writelen) {
577 if(!sip->tx_handler){
578 if(sip->gsc){
579 purple_ssl_input_add(sip->gsc, sipe_canwrite_cb_ssl, gc);
581 else{
582 sip->tx_handler = gaim_input_add(sip->fd,
583 GAIM_INPUT_WRITE, sipe_canwrite_cb,
584 gc);
588 /* XXX: is it OK to do this? You might get part of a request sent
589 with part of another. */
590 if(sip->txbuf->bufused > 0)
591 gaim_circ_buffer_append(sip->txbuf, "\r\n", 2);
593 gaim_circ_buffer_append(sip->txbuf, buf + ret,
594 writelen - ret);
599 static int sipe_send_raw(GaimConnection *gc, const char *buf, int len)
601 sendout_pkt(gc, buf);
602 return len;
605 static void sendout_sipmsg(struct sipe_account_data *sip, struct sipmsg *msg) {
606 GSList *tmp = msg->headers;
607 gchar *name;
608 gchar *value;
609 GString *outstr = g_string_new("");
610 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n", msg->method, msg->target);
611 while(tmp) {
612 name = ((struct siphdrelement*) (tmp->data))->name;
613 value = ((struct siphdrelement*) (tmp->data))->value;
614 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
615 tmp = g_slist_next(tmp);
617 g_string_append_printf(outstr, "\r\n%s", msg->body ? msg->body : "");
618 sendout_pkt(sip->gc, outstr->str);
619 g_string_free(outstr, TRUE);
622 static void send_sip_response(GaimConnection *gc, struct sipmsg *msg, int code,
623 const char *text, const char *body) {
624 GSList *tmp = msg->headers;
625 gchar *name;
626 gchar *value;
627 GString *outstr = g_string_new("");
629 /* When sending the acknowlegements and errors, the content length from the original
630 message is still here, but there is no body; we need to make sure we're sending the
631 correct content length */
632 sipmsg_remove_header(msg, "Content-Length");
633 if(body) {
634 gchar len[12];
635 sprintf(len, "%" G_GSIZE_FORMAT , strlen(body));
636 sipmsg_add_header(msg, "Content-Length", len);
638 else
639 sipmsg_add_header(msg, "Content-Length", "0");
640 g_string_append_printf(outstr, "SIP/2.0 %d %s\r\n", code, text);
641 while(tmp) {
642 name = ((struct siphdrelement*) (tmp->data))->name;
643 value = ((struct siphdrelement*) (tmp->data))->value;
645 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
646 tmp = g_slist_next(tmp);
648 g_string_append_printf(outstr, "\r\n%s", body ? body : "");
649 sendout_pkt(gc, outstr->str);
650 g_string_free(outstr, TRUE);
653 static void transactions_remove(struct sipe_account_data *sip, struct transaction *trans) {
654 if(trans->msg) sipmsg_free(trans->msg);
655 sip->transactions = g_slist_remove(sip->transactions, trans);
656 g_free(trans);
659 static void transactions_add_buf(struct sipe_account_data *sip, const gchar *buf, void *callback) {
660 struct transaction *trans = g_new0(struct transaction, 1);
661 trans->time = time(NULL);
662 trans->msg = sipmsg_parse_msg(buf);
663 trans->cseq = sipmsg_find_header(trans->msg, "CSeq");
664 trans->callback = callback;
665 sip->transactions = g_slist_append(sip->transactions, trans);
668 static struct transaction *transactions_find(struct sipe_account_data *sip, struct sipmsg *msg) {
669 struct transaction *trans;
670 GSList *transactions = sip->transactions;
671 gchar *cseq = sipmsg_find_header(msg, "CSeq");
673 while(transactions) {
674 trans = transactions->data;
675 if(!strcmp(trans->cseq, cseq)) {
676 return trans;
678 transactions = transactions->next;
681 return NULL;
684 static void send_sip_request(GaimConnection *gc, const gchar *method,
685 const gchar *url, const gchar *to, const gchar *addheaders,
686 const gchar *body, struct sip_dialog *dialog, TransCallback tc) {
687 struct sipe_account_data *sip = gc->proto_data;
688 char *callid = dialog ? g_strdup(dialog->callid) : gencallid();
689 char *auth = NULL;
690 const char *addh = "";
691 gchar *branch = genbranch();
692 gchar *tag = NULL;
693 char *buf;
695 if(!strcmp(method, "REGISTER")) {
696 if(sip->regcallid) {
697 g_free(callid);
698 callid = g_strdup(sip->regcallid);
700 else sip->regcallid = g_strdup(callid);
703 if(addheaders) addh = addheaders;
704 if(sip->registrar.type && !strcmp(method, "REGISTER")) {
705 buf = auth_header(sip, &sip->registrar, method, url);
706 auth = g_strdup_printf("Authorization: %s", buf);
707 g_free(buf);
708 gaim_debug(GAIM_DEBUG_MISC, "sipe", "1 header %s", auth);
711 if(!strcmp(method,"SUBSCRIBE") || !strcmp(method,"SERVICE") || !strcmp(method,"MESSAGE") || !strcmp(method,"INVITE") || !strcmp(method,"NOTIFY")) {
712 buf = g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"\r\n", sip->registrar.realm, sip->registrar.target);
713 auth = g_strdup_printf("Proxy-Authorization: %s", buf);
714 gaim_debug(GAIM_DEBUG_MISC, "sipe", "3 header %s", auth);
715 g_free(buf);
718 if (!dialog)
719 tag = gentag();
721 buf = g_strdup_printf("%s %s SIP/2.0\r\n"
722 "Via: SIP/2.0/%s %s:%d;branch=%s\r\n"
723 /* Don't know what epid is, but LCS wants it */
724 "From: <sip:%s>;tag=%s;epid=1234567890\r\n"
725 "To: <%s>%s%s\r\n"
726 "Max-Forwards: 10\r\n"
727 "CSeq: %d %s\r\n"
728 "User-Agent: Gaim/" VERSION "\r\n"
729 "Call-ID: %s\r\n"
730 "%s%s"
731 "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n%s",
732 method,
733 url,
734 sip->udp ? "UDP" : "TCP",
735 sipe_network_get_local_system_ip(),
736 sip->listenport,
737 branch,
738 sip->username,
739 dialog ? dialog->ourtag : tag,
741 dialog ? ";tag=" : "",
742 dialog ? dialog->theirtag : "",
743 ++sip->cseq,
744 method,
745 callid,
746 auth ? auth : "",
747 addh,
748 strlen(body),
749 body);
751 g_free(tag);
752 g_free(auth);
753 g_free(branch);
754 g_free(callid);
756 /* add to ongoing transactions */
758 transactions_add_buf(sip, buf, tc);
760 sendout_pkt(gc, buf);
762 g_free(buf);
765 static char *get_contact_register(struct sipe_account_data *sip) {
766 return g_strdup_printf("<sip:%s:%d;transport=%s>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, BYE, CANCEL, NOTIFY, ACK, BENOTIFY\";proxy=replace", sipe_network_get_local_system_ip(),sip->listenport, sip->udp ? "udp" : "tcp");
769 static char *get_contact(struct sipe_account_data *sip) {
770 //return g_strdup_printf("<sip:%s@%s:%d;maddr=%s;transport=%s>;proxy=replace", sip->username, sip->servername, sip->listenport, sipe_network_get_local_system_ip() , sip->udp ? "udp" : "tcp");
771 return g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace", sip->username, sip->listenport, sipe_network_get_local_system_ip() , sip->udp ? "udp" : "tcp");
774 static void do_register_exp(struct sipe_account_data *sip, int expire) {
775 char *uri = g_strdup_printf("sip:%s", sip->servername);
776 char *to = g_strdup_printf("sip:%s", sip->username);
777 char *contact = get_contact_register(sip);
778 //char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire);
779 // 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);
780 //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);
781 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);
782 g_free(contact);
784 sip->registerstatus = 1;
786 if(expire) {
787 sip->reregister = time(NULL) + expire - 50;
788 } else {
789 sip->reregister = time(NULL) + 600;
792 send_sip_request(sip->gc, "REGISTER", uri, to, hdr, "", NULL,
793 process_register_response);
795 g_free(hdr);
796 g_free(uri);
797 g_free(to);
800 static void do_register(struct sipe_account_data *sip) {
801 do_register_exp(sip, sip->registerexpire);
804 static gchar *parse_from(const gchar *hdr) {
805 gchar *from;
806 const gchar *tmp, *tmp2 = hdr;
808 if(!hdr) return NULL;
809 gaim_debug_info("sipe", "parsing address out of %s\n", hdr);
810 tmp = strchr(hdr, '<');
812 /* i hate the different SIP UA behaviours... */
813 if(tmp) { /* sip address in <...> */
814 tmp2 = tmp + 1;
815 tmp = strchr(tmp2, '>');
816 if(tmp) {
817 from = g_strndup(tmp2, tmp - tmp2);
818 } else {
819 gaim_debug_info("sipe", "found < without > in From\n");
820 return NULL;
822 } else {
823 tmp = strchr(tmp2, ';');
824 if(tmp) {
825 from = g_strndup(tmp2, tmp - tmp2);
826 } else {
827 from = g_strdup(tmp2);
830 gaim_debug_info("sipe", "got %s\n", from);
831 return from;
834 static gboolean process_subscribe_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc) {
835 gchar *to;
837 if(msg->response == 200 || msg->response == 202) {
838 gaim_debug_info("sipe", "Devolvio un response %d\n", msg->response);
839 return TRUE;
842 to = parse_from(sipmsg_find_header(tc->msg, "To")); /* cant be NULL since it is our own msg */
844 /* we can not subscribe -> user is offline (TODO unknown status?) */
846 gaim_prpl_got_user_status(sip->account, to, "offline", NULL);
847 g_free(to);
848 return TRUE;
851 static void sipe_subscribe(struct sipe_account_data *sip, struct sipe_buddy *buddy) {
852 gchar *contact ="Accept: application/pidf+xml, application/xpidf+xml\r\nEvent: presence\r\n";
853 gchar *to;
854 gchar *tmp;
856 if(strstr(buddy->name, "sip:"))
857 to = g_strdup(buddy->name);
858 else
859 to = g_strdup_printf("sip:%s", buddy->name);
861 tmp = get_contact(sip);
862 contact = g_strdup_printf("%sContact: %s\r\n", contact, tmp);
863 g_free(tmp);
865 /* subscribe to buddy presence
866 * we dont need to know the status so we do not need a callback */
868 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL,
869 process_subscribe_response);
871 g_free(to);
872 g_free(contact);
874 /* resubscribe before subscription expires */
875 /* add some jitter */
876 buddy->resubscribe = time(NULL)+1140+(rand()%50);
879 static gboolean sipe_add_lcs_contacts(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc) {
880 gchar *tmp;
881 xmlnode *item, *group, *isc;
882 const char *name_group, *group_id;
883 GaimBuddy *b;
884 GaimGroup *g = NULL;
885 gchar **parts;
886 gchar *apn;
887 int ng = 0, i;
888 struct sipe_buddy *bs;
889 struct sipe_group *gr;
890 int len = msg->bodylen;
892 //Reserved to max 10 groups. ToDO be dynamic
893 gr = g_new0(struct sipe_group, 10);
895 tmp = sipmsg_find_header(msg, "Event");
896 if(tmp && !strncmp(tmp, "vnd-microsoft-roaming-contacts", 30)){
898 gaim_debug_info("sipe", "sipe_add_lcs_contacts->%s-%d\n", msg->body, len);
899 /*Convert the contact from XML to Gaim Buddies*/
900 isc = xmlnode_from_str(msg->body, len);
902 /* ToDo. Find for all groups */
903 //if ((group = xmlnode_get_child(isc, "group"))) {
904 for(group = xmlnode_get_child(isc, "group"); group; group = xmlnode_get_next_twin(group)) {
905 name_group = xmlnode_get_attrib(group, "name");
906 group_id = xmlnode_get_attrib(group, "id");
907 if(!strncmp(name_group, "~", 1)){
908 name_group=g_strdup("General");
910 gr[ng].name_group = g_strdup(name_group);
911 gr[ng].id = g_strdup(group_id);
912 gaim_debug_info("sipe", "name_group->%s\n", name_group);
913 g = gaim_find_group(name_group);
914 if(!g) {
915 g = gaim_group_new(name_group);
916 gaim_blist_add_group(g, NULL);
918 if (!g) {
919 g = gaim_find_group("General");
920 if(!g) {
921 g = gaim_group_new("General");
922 gaim_blist_add_group(g, NULL);
925 gr[ng].g = g;
926 ng++;
928 for(i = 0; i < ng;i++){
929 gaim_debug_info("sipe", "id->%s\n", gr[i].id);
930 gaim_debug_info("sipe", "id->%s\n", gr[i].name_group);
933 for(item = xmlnode_get_child(isc, "contact"); item; item = xmlnode_get_next_twin(item))
935 const char *uri, *name, *groups;
936 char *buddy_name;
937 i = 0;
938 uri = xmlnode_get_attrib(item, "uri");
939 name = xmlnode_get_attrib(item, "name");
940 groups = xmlnode_get_attrib(item, "groups");
941 parts = g_strsplit(groups, " ", 0);
942 gaim_debug_info("sipe", "URI->%s,Groups->%s\n", uri, groups);
944 while(parts[i]) {
945 gaim_debug_info("sipe", "Groups->parts[i] %s\n", parts[i]);
946 if(!strcmp(gr[i].id,parts[i])){
947 gaim_debug_info("sipe", "Found Groups->gr[i].id(%s),gr[i].name_group (%s)\n",gr[i].id,gr[i].name_group);
949 buddy_name = g_strdup_printf("sip:%s", uri);
951 //b = gaim_find_buddy(sip->account, buddy_name);
952 b = gaim_find_buddy_in_group(sip->account, buddy_name, gr[i].g);
953 if(!b){
954 b = gaim_buddy_new(sip->account, buddy_name, uri);
956 g_free(buddy_name);
958 //sipe_add_buddy(sip->gc, b , gr[i].g);
959 gaim_blist_add_buddy(b, NULL, gr[i].g, NULL);
960 gaim_blist_alias_buddy(b, uri);
961 bs = g_new0(struct sipe_buddy, 1);
962 bs->name = g_strdup(b->name);
963 g_hash_table_insert(sip->buddies, bs->name, bs);
965 i++;
968 xmlnode_free(isc);
970 return 0;
973 static void sipe_subscribe_buddylist(struct sipe_account_data *sip) {
974 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";
975 gchar *to;
976 gchar *tmp;
977 //to = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
978 to = g_strdup_printf("sip:%s", sip->username);
980 tmp = get_contact(sip);
981 contact = g_strdup_printf("%sContact: %s\r\n", contact, tmp);
982 g_free(tmp);
983 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL, sipe_add_lcs_contacts);
984 g_free(to);
985 g_free(contact);
988 static void sipe_invite(struct sipe_account_data *sip, struct sipe_buddy *buddy) {
989 gchar *contact = "Supported: com.microsoft.rtc-multiparty\r\n";
990 gchar *to;
991 gchar *tmp;
992 //to = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
994 if(strstr(buddy->name, "sip:"))
995 to = g_strdup(buddy->name);
996 else
997 to = g_strdup_printf("sip:%s", buddy->name);
999 tmp = get_contact(sip);
1000 contact = g_strdup_printf("%sContact: %s\r\n", contact, tmp);
1001 g_free(tmp);
1003 tmp = g_strdup_printf("Roster-Manager:sip:%s@%s\r\nEndPoints: <sip:%s@%s>, <sip:%s@%s>", sip->username, sip->servername,sip->username, sip->servername, buddy->name, sip->servername);
1004 contact = g_strdup_printf("%sContact: %s\r\n", contact, tmp);
1005 g_free(tmp);
1007 send_sip_request(sip->gc, "INVITE", to, to, contact, "", NULL, NULL);
1009 g_free(to);
1010 g_free(contact);
1013 static void sipe_buddy_resub(char *name, struct sipe_buddy *buddy, struct sipe_account_data *sip) {
1014 time_t curtime = time(NULL);
1015 gaim_debug_info("sipe", "buddy resub\n");
1016 if(buddy->resubscribe < curtime) {
1017 gaim_debug(GAIM_DEBUG_MISC, "sipe", "sipe_buddy_resub %s\n", name);
1018 sipe_subscribe(sip, buddy);
1022 static gboolean resend_timeout(struct sipe_account_data *sip) {
1023 GSList *tmp = sip->transactions;
1024 time_t currtime = time(NULL);
1025 while(tmp) {
1026 struct transaction *trans = tmp->data;
1027 tmp = tmp->next;
1028 gaim_debug_info("sipe", "have open transaction age: %d\n", currtime- trans->time);
1029 if((currtime - trans->time > 5) && trans->retries >= 1) {
1030 /* TODO 408 */
1031 } else {
1032 if((currtime - trans->time > 2) && trans->retries == 0) {
1033 trans->retries++;
1034 sendout_sipmsg(sip, trans->msg);
1038 return TRUE;
1041 static gboolean subscribe_timeout(struct sipe_account_data *sip) {
1042 GSList *tmp;
1043 time_t curtime = time(NULL);
1044 /* register again if first registration expires */
1045 if(sip->reregister < curtime) {
1046 do_register(sip);
1048 /* check for every subscription if we need to resubscribe */
1049 //Fixxxer we need resub?
1050 g_hash_table_foreach(sip->buddies, (GHFunc)sipe_buddy_resub, (gpointer)sip);
1052 /* remove a timed out suscriber */
1054 tmp = sip->watcher;
1055 while(tmp) {
1056 struct sipe_watcher *watcher = tmp->data;
1057 if(watcher->expire < curtime) {
1058 watcher_remove(sip, watcher->name);
1059 tmp = sip->watcher;
1061 if(tmp) tmp = tmp->next;
1064 return TRUE;
1067 static void sipe_send_message(struct sipe_account_data *sip, const char *to, const char *msg, const char *type) {
1068 gchar *hdr;
1069 gchar *fullto;
1070 gchar *tmp;
1071 if(strncmp("sip:", to, 4)) {
1072 fullto = g_strdup_printf("sip:%s", to);
1073 } else {
1074 fullto = g_strdup(to);
1076 if(type) {
1077 hdr = g_strdup_printf("Content-Type: %s\r\n", type);
1078 } else {
1079 hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1081 tmp = get_contact(sip);
1082 hdr = g_strdup_printf("Contact: %s\r\n%s", tmp, hdr);
1083 g_free(tmp);
1085 send_sip_request(sip->gc, "MESSAGE", fullto, fullto, hdr, msg, NULL, NULL);
1086 g_free(hdr);
1087 g_free(fullto);
1090 static int sipe_im_send(GaimConnection *gc, const char *who, const char *what, GaimMessageFlags flags) {
1091 struct sipe_account_data *sip = gc->proto_data;
1092 char *to = g_strdup(who);
1093 char *text = gaim_unescape_html(what);
1094 sipe_send_message(sip, to, text, NULL);
1095 g_free(to);
1096 g_free(text);
1097 return 1;
1100 static void process_incoming_message(struct sipe_account_data *sip, struct sipmsg *msg) {
1101 gchar *from;
1102 gchar *contenttype;
1103 gboolean found = FALSE;
1105 from = parse_from(sipmsg_find_header(msg, "From"));
1107 if(!from) return;
1109 gaim_debug_info("sipe", "got message from %s: %s\n", from, msg->body);
1111 contenttype = sipmsg_find_header(msg, "Content-Type");
1112 if(!contenttype || !strncmp(contenttype, "text/plain", 10) || !strncmp(contenttype, "text/html", 9)) {
1113 serv_got_im(sip->gc, from, msg->body, 0, time(NULL));
1114 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1115 found = TRUE;
1117 if(!strncmp(contenttype, "application/im-iscomposing+xml", 30)) {
1118 xmlnode *isc = xmlnode_from_str(msg->body, msg->bodylen);
1119 xmlnode *state;
1120 gchar *statedata;
1122 if(!isc) {
1123 gaim_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1124 return;
1127 state = xmlnode_get_child(isc, "state");
1129 if(!state) {
1130 gaim_debug_info("sipe", "process_incoming_message: no state found\n");
1131 xmlnode_free(isc);
1132 return;
1135 statedata = xmlnode_get_data(state);
1136 if(statedata) {
1137 if(strstr(statedata, "active")) serv_got_typing(sip->gc, from, 0, GAIM_TYPING);
1138 else serv_got_typing_stopped(sip->gc, from);
1140 g_free(statedata);
1142 xmlnode_free(isc);
1143 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1144 found = TRUE;
1146 if(!found) {
1147 gaim_debug_info("sipe", "got unknown mime-type");
1148 send_sip_response(sip->gc, msg, 415, "Unsupported media type", NULL);
1150 g_free(from);
1154 gboolean process_register_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc) {
1155 gchar *tmp;
1156 gaim_debug(GAIM_DEBUG_MISC, "sipe", "in process register response response: %d\n", msg->response);
1157 switch (msg->response) {
1158 case 200:
1159 sip->registerstatus = 3;
1160 gaim_connection_set_state(sip->gc, GAIM_CONNECTED);
1162 // if(sip->registerstatus < 3) { /* registered */
1164 //}
1166 /* get buddies from blist */
1167 sipe_get_buddies(sip->gc);
1169 subscribe_timeout(sip);
1171 tmp = sipmsg_find_header(msg, "Allow-Events");
1172 if(tmp && strstr(tmp, "vnd-microsoft-provisioning")){
1173 sipe_subscribe_buddylist(sip);
1176 break;
1177 case 401:
1178 if(sip->registerstatus != 2) {
1179 gaim_debug_info("sipe", "REGISTER retries %d\n", sip->registrar.retries);
1180 if(sip->registrar.retries > 3) {
1181 sip->gc->wants_to_die = TRUE;
1182 gaim_connection_error(sip->gc, _("Wrong Password"));
1183 return TRUE;
1185 tmp = sipmsg_find_header(msg, "WWW-Authenticate");
1186 fill_auth(sip, tmp, &sip->registrar);
1187 sip->registerstatus = 2;
1188 do_register(sip);
1190 break;
1192 return TRUE;
1195 static void process_incoming_notify(struct sipe_account_data *sip, struct sipmsg *msg) {
1196 gchar *from;
1197 gchar *fromhdr;
1198 gchar *tmp2;
1199 xmlnode *pidf;
1200 xmlnode *basicstatus = NULL, *tuple, *status;
1201 gboolean isonline = FALSE;
1203 fromhdr = sipmsg_find_header(msg, "From");
1204 from = parse_from(fromhdr);
1205 if(!from) return;
1207 pidf = xmlnode_from_str(msg->body, msg->bodylen);
1209 if(!pidf) {
1210 gaim_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
1211 return;
1214 gaim_debug_info("sipe", "process_incoming_notify: body(%s)\n",msg->body);
1216 if ((tuple = xmlnode_get_child(pidf, "tuple")))
1217 if ((status = xmlnode_get_child(tuple, "status")))
1218 basicstatus = xmlnode_get_child(status, "basic");
1220 if(!basicstatus) {
1221 gaim_debug_info("sipe", "process_incoming_notify: no basic found\n");
1222 xmlnode_free(pidf);
1223 return;
1226 tmp2 = xmlnode_get_data(basicstatus);
1228 gaim_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n",tmp2);
1231 if(!tmp2) {
1232 gaim_debug_info("sipe", "process_incoming_notify: no basic data found\n");
1233 xmlnode_free(pidf);
1234 return;
1237 if(strstr(tmp2, "open")) {
1238 isonline = TRUE;
1241 g_free(tmp2);
1243 if(isonline) gaim_prpl_got_user_status(sip->account, from, "available", NULL);
1244 else gaim_prpl_got_user_status(sip->account, from, "offline", NULL);
1246 xmlnode_free(pidf);
1248 g_free(from);
1249 send_sip_response(sip->gc, msg, 200, "OK", NULL);
1252 static gchar *find_tag(const gchar *hdr) {
1253 const gchar *tmp = strstr(hdr, ";tag="), *tmp2;
1255 if(!tmp) return NULL;
1256 tmp += 5;
1257 if((tmp2 = strchr(tmp, ';'))) {
1258 return g_strndup(tmp, tmp2 - tmp);
1260 return g_strdup(tmp);
1263 static gchar* gen_xpidf(struct sipe_account_data *sip) {
1264 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1265 "<presence>\n"
1266 "<presentity uri=\"sip:%s@%s;method=SUBSCRIBE\"/>\n"
1267 "<display name=\"sip:%s@%s\"/>\n"
1268 "<atom id=\"1234\">\n"
1269 "<address uri=\"sip:%s@%s\">\n"
1270 "<status status=\"%s\"/>\n"
1271 "</address>\n"
1272 "</atom>\n"
1273 "</presence>\n",
1274 sip->username,
1275 sip->servername,
1276 sip->username,
1277 sip->servername,
1278 sip->username,
1279 sip->servername,
1280 sip->status);
1281 return doc;
1286 static gchar* gen_pidf(struct sipe_account_data *sip) {
1287 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1288 "<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@%s\">\n"
1289 "<tuple id=\"0\">\n"
1290 "<status>\n"
1291 "<basic>open</basic>\n"
1292 "<ep:activities>\n"
1293 " <ep:activity>%s</ep:activity>\n"
1294 "</ep:activities>"
1295 "</status>\n"
1296 "</tuple>\n"
1297 "<ci:display-name>FixXxeR</ci:display-name>\n"
1298 "</presence>",
1299 sip->username,
1300 sip->servername,
1301 sip->status);
1302 return doc;
1305 static void send_notify(struct sipe_account_data *sip, struct sipe_watcher *watcher) {
1306 gchar *doc = watcher->needsxpidf ? gen_xpidf(sip) : gen_pidf(sip);
1307 gchar *hdr = watcher->needsxpidf ? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
1308 send_sip_request(sip->gc, "NOTIFY", watcher->name, watcher->name, hdr, doc, &watcher->dialog, NULL);
1309 g_free(doc);
1312 static gboolean process_service_response(struct sipe_account_data *sip, struct sipmsg *msg, struct transaction *tc) {
1313 if(msg->response != 200 && msg->response != 408) {
1314 /* never send again */
1315 sip->republish = -1;
1317 return TRUE;
1320 static void send_service(struct sipe_account_data *sip) {
1321 gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
1322 gchar *doc = gen_pidf(sip);
1323 gchar *hdr = g_strdup("Content-Type: application/SOAP+xml\r\n");
1324 gchar *tmp = get_contact(sip);
1325 hdr = g_strdup_printf("Contact: %s\r\n%s", tmp, hdr);
1326 g_free(tmp);
1327 send_sip_request(sip->gc, "SERVICE", uri, uri,
1328 hdr,
1329 doc, NULL, process_service_response);
1330 sip->republish = time(NULL) + 500;
1331 g_free(hdr);
1332 g_free(uri);
1333 g_free(doc);
1336 static void process_incoming_subscribe(struct sipe_account_data *sip, struct sipmsg *msg) {
1337 const char *from_hdr = sipmsg_find_header(msg, "From");
1338 gchar *from = parse_from(from_hdr);
1339 gchar *theirtag = find_tag(from_hdr);
1340 gchar *ourtag = find_tag(sipmsg_find_header(msg, "To"));
1341 gboolean tagadded = FALSE;
1342 gchar *callid = sipmsg_find_header(msg, "Call-ID");
1343 gchar *expire = sipmsg_find_header(msg, "Expire");
1344 // gchar *ms-received-port =find_received_port(sipmsg_find_header(msg, "Contact"));
1345 gchar *tmp;
1346 struct sipe_watcher *watcher = watcher_find(sip, from);
1347 if(!ourtag) {
1348 tagadded = TRUE;
1349 ourtag = gentag();
1351 if(!watcher) { /* new subscription */
1352 gchar *acceptheader = sipmsg_find_header(msg, "Accept");
1353 gboolean needsxpidf = FALSE;
1354 if(!gaim_privacy_check(sip->account, from)) {
1355 send_sip_response(sip->gc, msg, 202, "Ok", NULL);
1356 goto privend;
1358 if(acceptheader) {
1359 gchar *tmp = acceptheader;
1360 gboolean foundpidf = FALSE;
1361 gboolean foundxpidf = FALSE;
1362 while(tmp && tmp < acceptheader + strlen(acceptheader)) {
1363 gchar *tmp2 = strchr(tmp, ',');
1364 if(tmp2) *tmp2 = '\0';
1365 if(!strcmp("application/pidf+xml", tmp))
1366 foundpidf = TRUE;
1367 if(!strcmp("application/xpidf+xml", tmp))
1368 foundxpidf = TRUE;
1369 if(tmp2) {
1370 *tmp2 = ',';
1371 tmp = tmp2;
1372 while(*tmp == ' ') tmp++;
1373 } else
1374 tmp = 0;
1376 if(!foundpidf && foundxpidf) needsxpidf = TRUE;
1377 g_free(acceptheader);
1379 watcher = watcher_create(sip, from, callid, ourtag, theirtag, needsxpidf);
1381 if(tagadded) {
1382 gchar *to = g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg, "To"), ourtag);
1383 sipmsg_remove_header(msg, "To");
1384 sipmsg_add_header(msg, "To", to);
1385 g_free(to);
1387 if(expire)
1388 watcher->expire = time(NULL) + strtol(expire, NULL, 10);
1389 else
1390 watcher->expire = time(NULL) + 600;
1391 //Fixxxer
1392 sipmsg_remove_header(msg, "Contact");
1393 tmp = get_contact(sip);
1394 sipmsg_add_header(msg, "Contact", tmp);
1395 g_free(tmp);
1396 gaim_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);
1397 send_sip_response(sip->gc, msg, 200, "Ok", NULL);
1398 send_notify(sip, watcher);
1399 privend:
1400 g_free(from);
1401 g_free(theirtag);
1402 g_free(ourtag);
1403 g_free(callid);
1404 g_free(expire);
1407 static void process_input_message(struct sipe_account_data *sip, struct sipmsg *msg) {
1408 gboolean found = FALSE;
1409 gaim_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg->response,msg->method);
1410 if(msg->response == 0) { /* request */
1411 if(!strcmp(msg->method, "MESSAGE")) {
1412 process_incoming_message(sip, msg);
1413 found = TRUE;
1414 } else if(!strcmp(msg->method, "NOTIFY")) {
1415 gaim_debug_info("sipe","send->process_incoming_notify\n");
1416 process_incoming_notify(sip, msg);
1417 found = TRUE;
1418 } else if(!strcmp(msg->method, "SUBSCRIBE")) {
1419 gaim_debug_info("sipe","send->process_incoming_subscribe\n");
1420 process_incoming_subscribe(sip, msg);
1421 found = TRUE;
1422 } else {
1423 send_sip_response(sip->gc, msg, 501, "Not implemented", NULL);
1425 } else { /* response */
1426 struct transaction *trans = transactions_find(sip, msg);
1427 if(trans) {
1428 if(msg->response == 407) {
1429 gchar *resend, *auth, *ptmp;
1431 if(sip->proxy.retries > 30) return;
1432 sip->proxy.retries++;
1433 /* do proxy authentication */
1435 ptmp = sipmsg_find_header(msg, "Proxy-Authenticate");
1437 fill_auth(sip, ptmp, &sip->proxy);
1438 auth = auth_header(sip, &sip->proxy, trans->msg->method, trans->msg->target);
1439 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
1440 sipmsg_add_header(trans->msg, "Proxy-Authorization", auth);
1441 g_free(auth);
1442 resend = sipmsg_to_string(trans->msg);
1443 /* resend request */
1444 sendout_pkt(sip->gc, resend);
1445 g_free(resend);
1446 } else {
1447 if(msg->response == 100) {
1448 /* ignore provisional response */
1449 gaim_debug_info("sipe", "got trying response\n");
1450 } else {
1451 sip->proxy.retries = 0;
1452 if(!strcmp(trans->msg->method, "REGISTER")) {
1453 if(msg->response == 401) sip->registrar.retries++;
1454 else sip->registrar.retries = 0;
1455 gaim_debug_info("sipe", "RE-REGISTER\n");
1456 } else {
1457 if(msg->response == 401) {
1458 gchar *resend, *auth, *ptmp;
1460 if(sip->registrar.retries > 4) return;
1461 sip->registrar.retries++;
1463 ptmp = sipmsg_find_header(msg, "WWW-Authenticate");
1465 fill_auth(sip, ptmp, &sip->registrar);
1466 auth = auth_header(sip, &sip->registrar, trans->msg->method, trans->msg->target);
1467 sipmsg_remove_header(trans->msg, "Proxy-Authorization");
1468 sipmsg_add_header(trans->msg, "Proxy-Authorization", auth);
1470 //sipmsg_remove_header(trans->msg, "Authorization");
1471 //sipmsg_add_header(trans->msg, "Authorization", auth);
1472 g_free(auth);
1473 resend = sipmsg_to_string(trans->msg);
1474 /* resend request */
1475 sendout_pkt(sip->gc, resend);
1476 g_free(resend);
1479 if(trans->callback) {
1480 /* call the callback to process response*/
1481 (trans->callback)(sip, msg, trans);
1483 transactions_remove(sip, trans);
1486 found = TRUE;
1487 } else {
1488 gaim_debug(GAIM_DEBUG_MISC, "sipe", "received response to unknown transaction");
1491 if(!found) {
1492 gaim_debug(GAIM_DEBUG_MISC, "sipe", "received a unknown sip message with method %s and response %d\n", msg->method, msg->response);
1496 static void process_input(struct sipe_account_data *sip, struct sip_connection *conn)
1498 char *cur;
1499 char *dummy;
1500 struct sipmsg *msg;
1501 int restlen;
1502 cur = conn->inbuf;
1504 /* according to the RFC remove CRLF at the beginning */
1505 while(*cur == '\r' || *cur == '\n') {
1506 cur++;
1508 if(cur != conn->inbuf) {
1509 memmove(conn->inbuf, cur, conn->inbufused - (cur - conn->inbuf));
1510 conn->inbufused = strlen(conn->inbuf);
1513 /* Received a full Header? */
1514 if((cur = strstr(conn->inbuf, "\r\n\r\n")) != NULL) {
1515 time_t currtime = time(NULL);
1516 cur += 2;
1517 cur[0] = '\0';
1518 gaim_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), conn->inbuf);
1519 msg = sipmsg_parse_header(conn->inbuf);
1520 cur[0] = '\r';
1521 cur += 2;
1522 restlen = conn->inbufused - (cur - conn->inbuf);
1523 if(restlen >= msg->bodylen) {
1524 dummy = g_malloc(msg->bodylen + 1);
1525 memcpy(dummy, cur, msg->bodylen);
1526 dummy[msg->bodylen] = '\0';
1527 msg->body = dummy;
1528 cur += msg->bodylen;
1529 memmove(conn->inbuf, cur, conn->inbuflen - (cur - conn->inbuf));
1530 conn->inbufused = strlen(conn->inbuf);
1531 } else {
1532 sipmsg_free(msg);
1533 return;
1535 gaim_debug(GAIM_DEBUG_MISC, "sipe", "in process response response: %d\n", msg->response);
1536 process_input_message(sip, msg);
1537 } else {
1538 gaim_debug(GAIM_DEBUG_MISC, "sipe", "received a incomplete sip msg: %s\n", conn->inbuf);
1542 static void sipe_udp_process(gpointer data, gint source, GaimInputCondition con) {
1543 GaimConnection *gc = data;
1544 struct sipe_account_data *sip = gc->proto_data;
1545 struct sipmsg *msg;
1546 int len;
1547 time_t currtime;
1549 static char buffer[65536];
1550 if((len = recv(source, buffer, sizeof(buffer) - 1, 0)) > 0) {
1551 buffer[len] = '\0';
1552 gaim_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), buffer);
1553 msg = sipmsg_parse_msg(buffer);
1554 if(msg) process_input_message(sip, msg);
1558 static void sipe_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
1560 PurpleConnection *gc = data;
1561 struct sipe_account_data *sip = gc->proto_data;
1562 int len;
1563 static char buf[4096];
1565 /* TODO: It should be possible to make this check unnecessary */
1566 if(!PURPLE_CONNECTION_IS_VALID(gc)) {
1567 purple_ssl_close(gsc);
1568 return;
1571 struct sip_connection *conn = connection_find(sip, sip->gsc->fd);
1572 if(!conn) {
1573 gaim_debug_error("sipe", "Connection not found!\n");
1574 return;
1578 if(conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
1579 conn->inbuflen += SIMPLE_BUF_INC;
1580 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
1583 len = purple_ssl_read(gsc, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1);
1585 if (len < 0 && errno == EAGAIN) {
1586 /* Try again later */
1587 return;
1588 } else if (len < 0) {
1589 purple_debug_info("sipe", "sipe_input_cb_ssl: read error\n");
1590 connection_remove(sip, sip->gsc->fd);
1591 if(sip->fd == gsc->fd) sip->fd = -1;
1592 return;
1593 } else if (len == 0) {
1594 purple_connection_error(gc, _("Server has disconnected"));
1595 connection_remove(sip, sip->gsc->fd);
1596 if(sip->fd == gsc->fd) sip->fd = -1;
1597 return;
1600 conn->inbufused += len;
1601 conn->inbuf[conn->inbufused] = '\0';
1603 process_input(sip, conn);
1607 static void sipe_input_cb(gpointer data, gint source, GaimInputCondition cond)
1609 GaimConnection *gc = data;
1610 struct sipe_account_data *sip = gc->proto_data;
1611 int len;
1612 struct sip_connection *conn = connection_find(sip, source);
1613 if(!conn) {
1614 gaim_debug_error("sipe", "Connection not found!\n");
1615 return;
1618 if(conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
1619 conn->inbuflen += SIMPLE_BUF_INC;
1620 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
1623 len = read(source, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1);
1625 if(len < 0 && errno == EAGAIN)
1626 return;
1627 else if(len <= 0) {
1628 gaim_debug_info("sipe", "sipe_input_cb: read error\n");
1629 connection_remove(sip, source);
1630 if(sip->fd == source) sip->fd = -1;
1631 return;
1634 conn->inbufused += len;
1635 conn->inbuf[conn->inbufused] = '\0';
1637 process_input(sip, conn);
1640 /* Callback for new connections on incoming TCP port */
1641 static void sipe_newconn_cb(gpointer data, gint source, GaimInputCondition cond) {
1642 GaimConnection *gc = data;
1643 struct sipe_account_data *sip = gc->proto_data;
1644 struct sip_connection *conn;
1646 int newfd = accept(source, NULL, NULL);
1648 conn = connection_create(sip, newfd);
1650 conn->inputhandler = gaim_input_add(newfd, GAIM_INPUT_READ, sipe_input_cb, gc);
1653 static void login_cb(gpointer data, gint source, const gchar *error_message) {
1654 GaimConnection *gc = data;
1655 struct sipe_account_data *sip;
1656 struct sip_connection *conn;
1658 if (!GAIM_CONNECTION_IS_VALID(gc))
1660 if (source >= 0)
1661 close(source);
1662 return;
1665 if(source < 0) {
1666 gaim_connection_error(gc, _("Could not connect"));
1667 return;
1670 sip = gc->proto_data;
1671 sip->fd = source;
1673 conn = connection_create(sip, source);
1675 sip->registertimeout = gaim_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
1677 do_register(sip);
1679 conn->inputhandler = gaim_input_add(sip->fd, GAIM_INPUT_READ, sipe_input_cb, gc);
1682 static void login_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond) {
1683 GaimConnection *gc = data;
1684 struct sipe_account_data *sip;
1685 struct sip_connection *conn;
1687 if (!GAIM_CONNECTION_IS_VALID(gc))
1689 purple_ssl_close(gsc);
1690 return;
1693 sip = gc->proto_data;
1694 sip->fd = gsc->fd;
1695 conn = connection_create(sip, sip->fd);
1697 purple_ssl_input_add(gsc, sipe_input_cb_ssl, gc);
1700 static guint sipe_ht_hash_nick(const char *nick) {
1701 char *lc = g_utf8_strdown(nick, -1);
1702 guint bucket = g_str_hash(lc);
1703 g_free(lc);
1705 return bucket;
1708 static gboolean sipe_ht_equals_nick(const char *nick1, const char *nick2) {
1709 return (gaim_utf8_strcasecmp(nick1, nick2) == 0);
1712 static void sipe_udp_host_resolved_listen_cb(int listenfd, gpointer data) {
1713 struct sipe_account_data *sip = (struct sipe_account_data*) data;
1715 sip->listen_data = NULL;
1717 if(listenfd == -1) {
1718 gaim_connection_error(sip->gc, _("Could not create listen socket"));
1719 return;
1722 sip->fd = listenfd;
1724 sip->listenport = gaim_network_get_port_from_fd(sip->fd);
1725 sip->listenfd = sip->fd;
1727 sip->listenpa = gaim_input_add(sip->fd, GAIM_INPUT_READ, sipe_udp_process, sip->gc);
1729 sip->resendtimeout = gaim_timeout_add(2500, (GSourceFunc) resend_timeout, sip);
1730 sip->registertimeout = gaim_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
1731 do_register(sip);
1734 static void sipe_udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) {
1735 struct sipe_account_data *sip = (struct sipe_account_data*) data;
1736 int addr_size;
1738 sip->query_data = NULL;
1740 if (!hosts || !hosts->data) {
1741 gaim_connection_error(sip->gc, _("Couldn't resolve host"));
1742 return;
1745 addr_size = GPOINTER_TO_INT(hosts->data);
1746 hosts = g_slist_remove(hosts, hosts->data);
1747 memcpy(&(sip->serveraddr), hosts->data, addr_size);
1748 g_free(hosts->data);
1749 hosts = g_slist_remove(hosts, hosts->data);
1750 while(hosts) {
1751 hosts = g_slist_remove(hosts, hosts->data);
1752 g_free(hosts->data);
1753 hosts = g_slist_remove(hosts, hosts->data);
1756 /* create socket for incoming connections */
1757 sip->listen_data = gaim_network_listen_range(5060, 5160, SOCK_DGRAM,
1758 sipe_udp_host_resolved_listen_cb, sip);
1759 if (sip->listen_data == NULL) {
1760 gaim_connection_error(sip->gc, _("Could not create listen socket"));
1761 return;
1765 static void sipe_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
1766 gpointer data)
1768 PurpleConnection *gc = data;
1769 struct sipe_account_data *sip;
1771 /* If the connection is already disconnected, we don't need to do anything else */
1772 if(!PURPLE_CONNECTION_IS_VALID(gc))
1773 return;
1775 sip = gc->proto_data;
1776 sip->gsc = NULL;
1778 switch(error) {
1779 case PURPLE_SSL_CONNECT_FAILED:
1780 purple_connection_error(gc, _("Connection Failed"));
1781 break;
1782 case PURPLE_SSL_HANDSHAKE_FAILED:
1783 purple_connection_error(gc, _("SSL Handshake Failed"));
1784 break;
1788 static void
1789 sipe_tcp_connect_listen_cb(int listenfd, gpointer data) {
1790 struct sipe_account_data *sip = (struct sipe_account_data*) data;
1791 GaimProxyConnectData *connect_data;
1793 sip->listen_data = NULL;
1795 sip->listenfd = listenfd;
1796 if(sip->listenfd == -1) {
1797 gaim_connection_error(sip->gc, _("Could not create listen socket"));
1798 return;
1801 gaim_debug_info("sipe", "listenfd: %d\n", sip->listenfd);
1802 sip->listenport = gaim_network_get_port_from_fd(sip->listenfd);
1803 sip->listenpa = gaim_input_add(sip->listenfd, GAIM_INPUT_READ,
1804 sipe_newconn_cb, sip->gc);
1805 gaim_debug_info("sipe", "connecting to %s port %d\n",
1806 sip->realhostname, sip->realport);
1807 /* open tcp connection to the server */
1808 if(sip->use_ssl){
1809 sip->gsc = purple_ssl_connect(sip->account,sip->realhostname, sip->realport, login_cb_ssl, sipe_ssl_connect_failure, sip->gc);
1811 else{
1812 connect_data = gaim_proxy_connect(sip->gc, sip->account, sip->realhostname,
1813 sip->realport, login_cb, sip->gc);
1815 if(connect_data == NULL) {
1816 gaim_connection_error(sip->gc, _("Couldn't create socket"));
1822 static void srvresolved(GaimSrvResponse *resp, int results, gpointer data) {
1823 struct sipe_account_data *sip;
1824 gchar *hostname;
1825 int port;
1827 sip = data;
1828 sip->srv_query_data = NULL;
1830 port = gaim_account_get_int(sip->account, "port", 0);
1832 /* find the host to connect to */
1833 if(results) {
1834 hostname = g_strdup(resp->hostname);
1835 if(!port)
1836 port = resp->port;
1837 g_free(resp);
1838 } else {
1839 if(!gaim_account_get_bool(sip->account, "useproxy", FALSE)) {
1840 hostname = g_strdup(sip->servername);
1841 } else {
1842 hostname = g_strdup(gaim_account_get_string(sip->account, "proxy", sip->servername));
1846 sip->realhostname = hostname;
1847 sip->realport = port;
1848 if(!sip->realport) sip->realport = 5060;
1850 /* TCP case */
1851 if(!sip->udp) {
1852 /* create socket for incoming connections */
1853 sip->listen_data = gaim_network_listen_range(5060, 5160, SOCK_STREAM,
1854 sipe_tcp_connect_listen_cb, sip);
1855 if (sip->listen_data == NULL) {
1856 gaim_connection_error(sip->gc, _("Could not create listen socket"));
1857 return;
1859 } else { /* UDP */
1860 gaim_debug_info("sipe", "using udp with server %s and port %d\n", hostname, port);
1862 sip->query_data = gaim_dnsquery_a(hostname, port, sipe_udp_host_resolved, sip);
1863 if (sip->query_data == NULL) {
1864 gaim_connection_error(sip->gc, _("Could not resolve hostname"));
1869 static void sipe_login(GaimAccount *account)
1871 GaimConnection *gc;
1872 struct sipe_account_data *sip;
1873 gchar **userserver;
1874 gchar *hosttoconnect;
1876 const char *username = gaim_account_get_username(account);
1877 gc = gaim_account_get_connection(account);
1879 if (strpbrk(username, " \t\v\r\n") != NULL) {
1880 gc->wants_to_die = TRUE;
1881 gaim_connection_error(gc, _("SIP Exchange usernames may not contain whitespaces"));
1882 return;
1885 if(!gaim_account_get_bool(account, "ssl", FALSE)){
1886 if (!purple_ssl_is_supported())
1888 gc->wants_to_die = TRUE;
1889 purple_connection_error(gc,
1890 _("SSL support is needed for SSL/TLS support. Please install a supported "
1891 "SSL library."));
1892 return;
1897 gc->proto_data = sip = g_new0(struct sipe_account_data, 1);
1898 sip->gc = gc;
1899 sip->account = account;
1900 sip->registerexpire = 900;
1901 sip->udp = gaim_account_get_bool(account, "udp", FALSE);
1902 sip->use_ssl = gaim_account_get_bool(account, "ssl", FALSE);
1903 /* TODO: is there a good default grow size? */
1904 if(!sip->udp)
1905 sip->txbuf = gaim_circ_buffer_new(0);
1907 userserver = g_strsplit(username, "@", 2);
1908 gaim_connection_set_display_name(gc, userserver[0]);
1909 sip->username = g_strdup(g_strjoin("@", userserver[0], userserver[1], NULL));
1910 sip->servername = g_strdup(userserver[1]);
1911 sip->password = g_strdup(gaim_connection_get_password(gc));
1912 g_strfreev(userserver);
1914 sip->buddies = g_hash_table_new((GHashFunc)sipe_ht_hash_nick, (GEqualFunc)sipe_ht_equals_nick);
1916 gaim_connection_update_progress(gc, _("Connecting"), 1, 2);
1918 /* TODO: Set the status correctly. */
1919 sip->status = g_strdup("available");
1921 if(!gaim_account_get_bool(account, "useproxy", FALSE)) {
1922 hosttoconnect = g_strdup(sip->servername);
1923 } else {
1924 hosttoconnect = g_strdup(gaim_account_get_string(account, "proxy", sip->servername));
1928 gaim_debug_info("sipe", "HosttoConnect->%s\n", hosttoconnect);
1932 sip->srv_query_data = gaim_srv_resolve("sip",
1933 sip->udp ? "udp" : "tcp", hosttoconnect, srvresolved, sip);
1935 g_free(hosttoconnect);
1938 static void sipe_close(GaimConnection *gc)
1940 struct sipe_account_data *sip = gc->proto_data;
1942 if(sip) {
1943 /* unregister */
1944 do_register_exp(sip, 0);
1945 connection_free_all(sip);
1947 if (sip->query_data != NULL)
1948 gaim_dnsquery_destroy(sip->query_data);
1950 if (sip->srv_query_data != NULL)
1951 gaim_srv_cancel(sip->srv_query_data);
1953 if (sip->listen_data != NULL)
1954 gaim_network_listen_cancel(sip->listen_data);
1956 g_free(sip->servername);
1957 g_free(sip->username);
1958 g_free(sip->password);
1959 g_free(sip->registrar.nonce);
1960 g_free(sip->registrar.opaque);
1961 g_free(sip->registrar.target);
1962 g_free(sip->registrar.realm);
1963 g_free(sip->registrar.digest_session_key);
1964 g_free(sip->proxy.nonce);
1965 g_free(sip->proxy.opaque);
1966 g_free(sip->proxy.target);
1967 g_free(sip->proxy.realm);
1968 g_free(sip->proxy.digest_session_key);
1969 if(sip->txbuf)
1970 gaim_circ_buffer_destroy(sip->txbuf);
1971 g_free(sip->realhostname);
1972 if(sip->listenpa) gaim_input_remove(sip->listenpa);
1973 if(sip->tx_handler) gaim_input_remove(sip->tx_handler);
1974 if(sip->resendtimeout) gaim_timeout_remove(sip->resendtimeout);
1975 if(sip->registertimeout) gaim_timeout_remove(sip->registertimeout);
1977 g_free(gc->proto_data);
1978 gc->proto_data = NULL;
1981 /* not needed since privacy is checked for every subscribe */
1982 static void dummy_add_deny(GaimConnection *gc, const char *name) {
1985 static void dummy_permit_deny(GaimConnection *gc) {
1988 static gboolean sipe_plugin_load(PurplePlugin *plugin) {
1989 return TRUE;
1993 static gboolean sipe_plugin_unload(PurplePlugin *plugin) {
1994 return TRUE;
1998 static void sipe_plugin_destroy(PurplePlugin *plugin) {
2001 static PurplePlugin *my_protocol = NULL;
2003 static PurplePluginProtocolInfo prpl_info =
2006 NULL, /* user_splits */
2007 NULL, /* protocol_options */
2008 NO_BUDDY_ICONS, /* icon_spec */
2009 sipe_list_icon, /* list_icon */
2010 NULL, /* list_emblems */
2011 NULL, /* status_text */
2012 NULL, /* tooltip_text */
2013 sipe_status_types, /* away_states */
2014 NULL, /* blist_node_menu */
2015 NULL, /* chat_info */
2016 NULL, /* chat_info_defaults */
2017 sipe_login, /* login */
2018 sipe_close, /* close */
2019 sipe_im_send, /* send_im */
2020 NULL, /* set_info */
2021 // sipe_typing, /* send_typing */
2022 NULL, /* send_typing */
2023 NULL, /* get_info */
2024 sipe_set_status, /* set_status */
2025 NULL, /* set_idle */
2026 NULL, /* change_passwd */
2027 sipe_add_buddy, /* add_buddy */
2028 NULL, /* add_buddies */
2029 sipe_remove_buddy, /* remove_buddy */
2030 NULL, /* remove_buddies */
2031 dummy_add_deny, /* add_permit */
2032 dummy_add_deny, /* add_deny */
2033 dummy_add_deny, /* rem_permit */
2034 dummy_add_deny, /* rem_deny */
2035 dummy_permit_deny, /* set_permit_deny */
2036 NULL, /* join_chat */
2037 NULL, /* reject_chat */
2038 NULL, /* get_chat_name */
2039 NULL, /* chat_invite */
2040 NULL, /* chat_leave */
2041 NULL, /* chat_whisper */
2042 NULL, /* chat_send */
2043 sipe_keep_alive, /* keepalive */
2044 NULL, /* register_user */
2045 NULL, /* get_cb_info */
2046 NULL, /* get_cb_away */
2047 NULL, /* alias_buddy */
2048 NULL, /* group_buddy */
2049 NULL, /* rename_group */
2050 NULL, /* buddy_free */
2051 NULL, /* convo_closed */
2052 NULL, /* normalize */
2053 NULL, /* set_buddy_icon */
2054 NULL, /* remove_group */
2055 NULL, /* get_cb_real_name */
2056 NULL, /* set_chat_topic */
2057 NULL, /* find_blist_chat */
2058 NULL, /* roomlist_get_list */
2059 NULL, /* roomlist_cancel */
2060 NULL, /* roomlist_expand_category */
2061 NULL, /* can_receive_file */
2062 NULL, /* send_file */
2063 NULL, /* new_xfer */
2064 NULL, /* offline_message */
2065 NULL, /* whiteboard_prpl_ops */
2066 sipe_send_raw, /* send_raw */
2070 static GaimPluginInfo info = {
2071 PURPLE_PLUGIN_MAGIC,
2072 PURPLE_MAJOR_VERSION,
2073 PURPLE_MINOR_VERSION,
2074 PURPLE_PLUGIN_PROTOCOL, /**< type */
2075 NULL, /**< ui_requirement */
2076 0, /**< flags */
2077 NULL, /**< dependencies */
2078 PURPLE_PRIORITY_DEFAULT, /**< priority */
2079 "prpl-sipe", /**< id */
2080 "SIPE", /**< name */
2081 VERSION, /**< version */
2082 N_("SIP/SIMPLE Exchange Protocol Plugin"), /** summary */
2083 N_("The SIP/SIMPLE Exchange Protocol Plugin"), /** description */
2084 "Anibal Avelar <avelar@gmail.com>", /**< author */
2085 PURPLE_WEBSITE, /**< homepage */
2086 sipe_plugin_load, /**< load */
2087 sipe_plugin_unload, /**< unload */
2088 sipe_plugin_destroy, /**< destroy */
2089 NULL, /**< ui_info */
2090 &prpl_info, /**< extra_info */
2091 NULL,
2092 NULL,
2093 NULL,
2094 NULL,
2095 NULL,
2096 NULL
2099 static void init_plugin(PurplePlugin *plugin)
2101 PurpleAccountUserSplit *split;
2102 PurpleAccountOption *option;
2104 purple_plugin_register(plugin);
2106 //split = gaim_account_user_split_new(_("Server"), "", '@');
2107 //prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
2108 option = gaim_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2109 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2110 option = gaim_account_option_string_new(_("Proxy Server"), "proxy", "");
2111 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2113 /*option = gaim_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
2114 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2116 option = purple_account_option_bool_new(_("Use SSL/TLS"), "ssl", FALSE);
2117 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,option);
2119 option = gaim_account_option_bool_new(_("Use UDP"), "udp", FALSE);
2120 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2122 option = gaim_account_option_int_new(_("Connect port"), "port", 5060);
2123 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2125 /*option = gaim_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2126 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2127 option = gaim_account_option_string_new(_("Proxy"), "proxy", "");
2128 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2129 option = gaim_account_option_string_new(_("Auth User"), "authuser", "");
2130 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2131 option = gaim_account_option_string_new(_("Auth Domain"), "authdomain", "");
2132 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2133 my_protocol = plugin;
2137 /* I had to redefined the function for it load, but works */
2138 gboolean purple_init_plugin(PurplePlugin *plugin){
2139 plugin->info = &(info);
2140 init_plugin((plugin));
2141 sipe_plugin_load((plugin));
2142 return purple_plugin_register(plugin);