6 * Copyright (C) 2008 Novell, Inc.
7 * Copyright (C) 2007 Anibal Avelar "Fixxxer"<avelar@gmail.com>
8 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
11 * Thanks to Google's Summer of Code Program and the helpful mentors
14 * Session-based SIP MESSAGE documentation:
15 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #include "sip-internal.h"
38 #define _LIBC_INTERNAL_
44 #include "accountopt.h"
46 #include "conversation.h"
63 #include "sipe-sign.h"
68 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
71 static gchar
*get_epid()
73 return sipe_uuid_get_macaddr();
76 static char *genbranch()
78 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
79 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
80 rand() & 0xFFFF, rand() & 0xFFFF);
83 static char *gencallid()
85 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
86 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
87 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
88 rand() & 0xFFFF, rand() & 0xFFFF);
91 static gchar
*find_tag(const gchar
*hdr
)
93 gchar
* tag
= sipmsg_find_part_of_header (hdr
, "tag=", ";", NULL
);
95 // In case it's at the end and there's no trailing ;
96 tag
= sipmsg_find_part_of_header (hdr
, "tag=", NULL
, NULL
);
102 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
107 static void sipe_keep_alive(PurpleConnection
*gc
)
109 struct sipe_account_data
*sip
= gc
->proto_data
;
111 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
112 gchar buf
[2] = {0, 0};
113 purple_debug_info("sipe", "sending keep alive\n");
114 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
118 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
120 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
121 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
124 static void send_notify(struct sipe_account_data
*sip
, struct sipe_watcher
*);
126 static void send_service(struct sipe_account_data
*sip
);
127 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
);
128 static void send_publish(struct sipe_account_data
*sip
,struct sipmsg
*msg
);
130 static void do_notifies(struct sipe_account_data
*sip
)
132 GSList
*tmp
= sip
->watcher
;
133 purple_debug_info("sipe", "do_notifies()\n");
136 purple_debug_info("sipe", "notifying %s\n", ((struct sipe_watcher
*)tmp
->data
)->name
);
137 send_notify(sip
, tmp
->data
);
142 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
144 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(purple_status_get_type(status
));
145 struct sipe_account_data
*sip
= NULL
;
147 if (!purple_status_is_active(status
))
151 sip
= account
->gc
->proto_data
;
156 if (primitive
== PURPLE_STATUS_AWAY
)
157 sip
->status
= g_strdup("away");
158 else if (primitive
== PURPLE_STATUS_AVAILABLE
)
159 sip
->status
= g_strdup("available");
160 else if (primitive
== PURPLE_STATUS_UNAVAILABLE
)
161 sip
->status
= g_strdup("busy");
167 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
169 struct sip_connection
*ret
= NULL
;
170 GSList
*entry
= sip
->openconns
;
173 if (ret
->fd
== fd
) return ret
;
179 static struct sipe_watcher
*watcher_find(struct sipe_account_data
*sip
,
182 struct sipe_watcher
*watcher
;
183 GSList
*entry
= sip
->watcher
;
185 watcher
= entry
->data
;
186 if (!strcmp(name
, watcher
->name
)) return watcher
;
192 static struct sipe_watcher
*watcher_create(struct sipe_account_data
*sip
,
193 const gchar
*name
, const gchar
*callid
, const gchar
*ourtag
,
194 const gchar
*theirtag
, gboolean needsxpidf
)
196 struct sipe_watcher
*watcher
= g_new0(struct sipe_watcher
, 1);
197 watcher
->name
= g_strdup(name
);
198 watcher
->dialog
.callid
= g_strdup(callid
);
199 watcher
->dialog
.ourtag
= g_strdup(ourtag
);
200 watcher
->dialog
.theirtag
= g_strdup(theirtag
);
201 watcher
->needsxpidf
= needsxpidf
;
202 sip
->watcher
= g_slist_append(sip
->watcher
, watcher
);
203 printf("\n\nADDING WATCHER for %s\n\n", name
);
207 static void watcher_remove(struct sipe_account_data
*sip
, const gchar
*name
)
209 struct sipe_watcher
*watcher
= watcher_find(sip
, name
);
210 printf("\n\nREMOVING WATCHER for %s\n\n", name
);
211 sip
->watcher
= g_slist_remove(sip
->watcher
, watcher
);
212 g_free(watcher
->name
);
213 g_free(watcher
->dialog
.callid
);
214 g_free(watcher
->dialog
.ourtag
);
215 g_free(watcher
->dialog
.theirtag
);
219 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
221 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
223 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
227 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
229 struct sip_connection
*conn
= connection_find(sip
, fd
);
230 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
231 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
236 static void connection_free_all(struct sipe_account_data
*sip
)
238 struct sip_connection
*ret
= NULL
;
239 GSList
*entry
= sip
->openconns
;
242 connection_remove(sip
, ret
->fd
);
243 entry
= sip
->openconns
;
247 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
249 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
250 struct sipe_buddy
*b
;
251 if (strncmp("sip:", buddy
->name
, 4)) {
252 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
253 purple_blist_rename_buddy(buddy
, buf
);
256 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
257 b
= g_new0(struct sipe_buddy
, 1);
258 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
259 b
->name
= g_strdup(buddy
->name
);
260 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
262 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
266 static void sipe_get_buddies(PurpleConnection
*gc
)
268 PurpleBlistNode
*gnode
, *cnode
, *bnode
;
270 purple_debug_info("sipe", "sipe_get_buddies\n");
272 for (gnode
= purple_get_blist()->root
; gnode
; gnode
= gnode
->next
) {
273 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode
)) continue;
274 for (cnode
= gnode
->child
; cnode
; cnode
= cnode
->next
) {
275 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode
)) continue;
276 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
277 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode
)) continue;
278 if (((PurpleBuddy
*)bnode
)->account
== gc
->account
)
279 sipe_add_buddy(gc
, (PurpleBuddy
*)bnode
, (PurpleGroup
*)gnode
);
285 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
287 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
288 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
289 g_hash_table_remove(sip
->buddies
, buddy
->name
);
294 static GList
*sipe_status_types(PurpleAccount
*acc
)
296 PurpleStatusType
*type
;
300 type
= purple_status_type_new_with_attrs(
301 PURPLE_STATUS_AVAILABLE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
302 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
304 types
= g_list_append(types
, type
);
307 type
= purple_status_type_new_with_attrs(
308 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
309 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
311 types
= g_list_append(types
, type
);
314 type
= purple_status_type_new_with_attrs(
315 PURPLE_STATUS_UNAVAILABLE
, "busy", _("Busy"), TRUE
, TRUE
, FALSE
,
316 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
318 types
= g_list_append(types
, type
);
321 type
= purple_status_type_new_full(
322 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
323 types
= g_list_append(types
, type
);
328 //static struct sipe_krb5_auth krb5_auth;
329 static gchar
*auth_header_without_newline(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
, gboolean force_reauth
)
331 const gchar
*method
= msg
->method
;
332 const gchar
*target
= msg
->target
;
337 const char *authdomain
;
338 const char *authuser
;
339 const char *krb5_realm
;
341 gchar
*krb5_token
= NULL
;
343 authdomain
= purple_account_get_string(sip
->account
, "authdomain", "");
344 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
346 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
347 // and do error checking
349 // KRB realm should always be uppercase
350 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
352 if (sip
->realhostname
) {
353 host
= sip
->realhostname
;
354 } else if (purple_account_get_bool(sip
->account
, "use_proxy", TRUE
)) {
355 host
= purple_account_get_string(sip
->account
, "proxy", "");
357 host
= sip
->sipdomain
;
360 /*gboolean new_auth = krb5_auth.gss_context == NULL;
362 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
365 if (new_auth || force_reauth) {
366 krb5_token = krb5_auth.base64_token;
369 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
370 krb5_token = krb5_auth.base64_token;*/
372 if (!authuser
|| strlen(authuser
) < 1) {
373 authuser
= sip
->username
;
376 if (auth
->type
== 1) { /* Digest */
377 sprintf(noncecount
, "%08d", auth
->nc
++);
378 response
= purple_cipher_http_digest_calculate_response(
379 "md5", method
, target
, NULL
, NULL
,
380 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
381 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
383 ret
= g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"", authuser
, auth
->realm
, auth
->nonce
, target
, noncecount
, response
);
386 } else if (auth
->type
== 2) { /* NTLM */
387 // If we have a signature for the message, include that
388 if (msg
->signature
) {
389 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", crand=\"%s\", cnum=\"%s\", response=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, msg
->rand
, msg
->num
, msg
->signature
);
393 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
394 /* TODO: Don't hardcode "purple" as the hostname */
395 const gchar
* ntlm_key
;
396 gchar
* gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, "purple", authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
397 auth
->ntlm_key
= (gchar
*)ntlm_key
;
398 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
403 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
405 } else if (auth
->type
== 3) {
408 /*if (new_auth || force_reauth) {
409 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
411 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, krb5_token);
413 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
416 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
417 gchar * mic = "MICTODO";
418 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
419 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
420 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
421 //auth->opaque ? auth->opaque : "", auth->target);
422 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
427 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
430 sprintf(noncecount
, "%08d", auth
->nc
++);
431 response
= purple_cipher_http_digest_calculate_response(
432 "md5", method
, target
, NULL
, NULL
,
433 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
434 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
436 ret
= g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"", authuser
, auth
->realm
, auth
->nonce
, target
, noncecount
, response
);
441 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
, gboolean force_reauth
)
443 gchar
*with
, *without
;
445 without
= auth_header_without_newline(sip
, auth
, msg
, force_reauth
);
446 with
= g_strdup_printf("%s\r\n", without
);
452 static char *parse_attribute(const char *attrname
, const char *source
)
454 const char *tmp
, *tmp2
;
456 int len
= strlen(attrname
);
458 if (!strncmp(source
, attrname
, len
)) {
460 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
462 retval
= g_strndup(tmp
, tmp2
- tmp
);
464 retval
= g_strdup(tmp
);
470 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
473 const char *authuser
;
476 const char *krb5_realm
;
479 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
480 // and do error checking
482 // KRB realm should always be uppercase
483 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
485 if (sip->realhostname) {
486 host = sip->realhostname;
487 } else if (purple_account_get_bool(sip->account, "use_proxy", TRUE)) {
488 host = purple_account_get_string(sip->account, "proxy", "");
490 host = sip->sipdomain;
493 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
495 if (!authuser
|| strlen(authuser
) < 1) {
496 authuser
= sip
->username
;
500 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
504 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
506 parts
= g_strsplit(hdr
+5, "\", ", 0);
509 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
510 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
511 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
514 if ((tmp
= parse_attribute("targetname=\"",
518 else if ((tmp
= parse_attribute("realm=\"",
522 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
529 if (!strstr(hdr
, "gssapi-data")) {
537 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
538 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
540 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
541 parts
= g_strsplit(hdr
+9, "\", ", 0);
544 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
545 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
546 /*if (krb5_auth.gss_context == NULL) {
547 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
549 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
552 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
554 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
556 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
563 //if (!strstr(hdr, "gssapi-data")) {
572 parts
= g_strsplit(hdr
, " ", 0);
574 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
577 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
584 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
586 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
587 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
593 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
595 PurpleConnection
*gc
= data
;
596 struct sipe_account_data
*sip
= gc
->proto_data
;
600 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
602 if (max_write
== 0) {
603 purple_input_remove(sip
->tx_handler
);
608 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
610 if (written
< 0 && errno
== EAGAIN
)
612 else if (written
<= 0) {
613 /*TODO: do we really want to disconnect on a failure to write?*/
614 purple_connection_error(gc
, _("Could not write"));
618 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
621 static void sipe_canwrite_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
623 PurpleConnection
*gc
= data
;
624 struct sipe_account_data
*sip
= gc
->proto_data
;
628 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
630 if (max_write
== 0) {
631 purple_input_remove(sip
->tx_handler
);
636 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
638 if (written
< 0 && errno
== EAGAIN
)
640 else if (written
<= 0) {
641 /*TODO: do we really want to disconnect on a failure to write?*/
642 purple_connection_error(gc
, _("Could not write"));
646 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
649 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
651 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
653 PurpleConnection
*gc
= data
;
654 struct sipe_account_data
*sip
;
655 struct sip_connection
*conn
;
657 if (!PURPLE_CONNECTION_IS_VALID(gc
))
665 purple_connection_error(gc
, _("Could not connect"));
669 sip
= gc
->proto_data
;
671 sip
->connecting
= FALSE
;
673 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
675 /* If there is more to write now, we need to register a handler */
676 if (sip
->txbuf
->bufused
> 0)
677 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
,
678 sipe_canwrite_cb
, gc
);
680 conn
= connection_create(sip
, source
);
681 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
684 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
686 PurpleConnection
*gc
= data
;
687 struct sipe_account_data
*sip
;
688 struct sip_connection
*conn
;
690 if (!PURPLE_CONNECTION_IS_VALID(gc
))
692 if(gsc
) purple_ssl_close(gsc
);
696 sip
= gc
->proto_data
;
698 sip
->connecting
= FALSE
;
700 sipe_canwrite_cb_ssl(gc
, gsc
, PURPLE_INPUT_WRITE
);
702 /* If there is more to write now, we need to register a handler */
703 if (sip
->txbuf
->bufused
> 0)
704 purple_ssl_input_add(gsc
, sipe_canwrite_cb_ssl
, gc
);
706 conn
= connection_create(sip
, gsc
->fd
);
707 purple_ssl_input_add(sip
->gsc
, sipe_input_cb_ssl
, gc
);
711 static void sendlater(PurpleConnection
*gc
, const char *buf
)
713 struct sipe_account_data
*sip
= gc
->proto_data
;
715 if (!sip
->connecting
) {
716 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
718 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
721 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
722 purple_connection_error(gc
, _("Couldn't create socket"));
725 sip
->connecting
= TRUE
;
728 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
729 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
731 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
734 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
736 struct sipe_account_data
*sip
= gc
->proto_data
;
737 time_t currtime
= time(NULL
);
738 int writelen
= strlen(buf
);
740 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
742 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
743 purple_debug_info("sipe", "could not send packet\n");
752 if (sip
->tx_handler
) {
757 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
759 ret
= write(sip
->fd
, buf
, writelen
);
763 if (ret
< 0 && errno
== EAGAIN
)
765 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
770 if (ret
< writelen
) {
771 if (!sip
->tx_handler
){
773 purple_ssl_input_add(sip
->gsc
, sipe_canwrite_cb_ssl
, gc
);
776 sip
->tx_handler
= purple_input_add(sip
->fd
,
777 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
782 /* XXX: is it OK to do this? You might get part of a request sent
783 with part of another. */
784 if (sip
->txbuf
->bufused
> 0)
785 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
787 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
793 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
795 sendout_pkt(gc
, buf
);
799 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
801 GSList
*tmp
= msg
->headers
;
804 GString
*outstr
= g_string_new("");
805 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
807 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
808 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
809 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
810 tmp
= g_slist_next(tmp
);
812 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
813 sendout_pkt(sip
->gc
, outstr
->str
);
814 g_string_free(outstr
, TRUE
);
817 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
820 if (sip
->registrar
.ntlm_key
) {
821 struct sipmsg_breakdown msgbd
;
823 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
824 // TODO generate this
825 msgbd
.rand
= g_strdup("0878F41B");
826 sip
->registrar
.ntlm_num
++;
827 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
828 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
829 if (signature_input_str
!= NULL
) {
830 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
831 msg
->rand
= g_strdup(msgbd
.rand
);
832 msg
->num
= g_strdup(msgbd
.num
);
834 sipmsg_breakdown_free(&msgbd
);
837 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
838 buf
= auth_header_without_newline(sip
, &sip
->registrar
, msg
, FALSE
);
839 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
840 sipmsg_add_header(msg
, "Authorization", buf
);
842 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
843 //sipmsg_add_header_pos(msg, "Authorization", buf, 5);
846 } else if (!strcmp(method
,"SUBSCRIBE") || !strcmp(method
,"SERVICE") || !strcmp(method
,"MESSAGE") || !strcmp(method
,"INVITE") || !strcmp(method
, "ACK") || !strcmp(method
, "NOTIFY") || !strcmp(method
, "BYE") || !strcmp(method
, "INFO")) {
848 sip
->registrar
.type
=2;
850 buf
= auth_header_without_newline(sip
, &sip
->registrar
, msg
, FALSE
);
851 //buf = auth_header(sip, &sip->proxy, msg, FALSE);
852 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
853 //sipmsg_add_header(msg, "Authorization", buf);
856 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
860 static char *get_contact(struct sipe_account_data
*sip
)
862 return g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace", sip
->username
, sip
->listenport
, purple_network_get_my_ip(-1), sip
->use_ssl
? "tls" : sip
->udp
? "udp" : "tcp");
866 static char *get_contact_service(struct sipe_account_data
*sip
)
868 return g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip
->listenport
, sip
->use_ssl
? "tls" : sip
->udp
? "udp" : "tcp",generateUUIDfromEPID(get_epid()));
869 //return g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace", sip->username, sip->listenport, purple_network_get_my_ip(-1), sip->use_ssl ? "tls" : sip->udp ? "udp" : "tcp");
872 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
873 const char *text
, const char *body
)
875 GSList
*tmp
= msg
->headers
;
878 GString
*outstr
= g_string_new("");
879 struct sipe_account_data
*sip
= gc
->proto_data
;
882 contact
= get_contact(sip
);
883 sipmsg_remove_header(msg
, "Contact");
884 sipmsg_add_header(msg
, "Contact", contact
);
887 /* When sending the acknowlegements and errors, the content length from the original
888 message is still here, but there is no body; we need to make sure we're sending the
889 correct content length */
890 sipmsg_remove_header(msg
, "Content-Length");
893 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
894 sipmsg_add_header(msg
, "Content-Length", len
);
896 sipmsg_add_header(msg
, "Content-Length", "0");
899 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
900 //gchar * mic = "MICTODO";
901 msg
->response
= code
;
903 sipmsg_remove_header(msg
, "Authentication-Info");
904 sign_outgoing_message(msg
, sip
, msg
->method
);
906 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
908 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
909 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
911 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
912 tmp
= g_slist_next(tmp
);
914 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
915 sendout_pkt(gc
, outstr
->str
);
916 g_string_free(outstr
, TRUE
);
919 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
921 if (trans
->msg
) sipmsg_free(trans
->msg
);
922 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
926 static struct transaction
*
927 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
929 struct transaction
*trans
= g_new0(struct transaction
, 1);
930 trans
->time
= time(NULL
);
931 trans
->msg
= (struct sipmsg
*)msg
;
932 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
933 trans
->callback
= callback
;
934 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
938 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
940 struct transaction
*trans
;
941 GSList
*transactions
= sip
->transactions
;
942 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
944 while (transactions
) {
945 trans
= transactions
->data
;
946 if (!strcmp(trans
->cseq
, cseq
)) {
949 transactions
= transactions
->next
;
956 static struct transaction
*
957 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
958 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
959 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
961 struct sipe_account_data
*sip
= gc
->proto_data
;
962 const char *addh
= "";
966 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : gentag();
967 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
968 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
969 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
970 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
971 gchar
*useragent
= purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
972 if (!strcmp(method
, "REGISTER")) {
973 if (sip
->regcallid
) {
975 callid
= g_strdup(sip
->regcallid
);
977 sip
->regcallid
= g_strdup(callid
);
981 if (addheaders
) addh
= addheaders
;
983 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
984 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
985 "From: <sip:%s>;tag=%s;epid=%s\r\n"
986 "To: <%s>%s%s%s%s\r\n"
987 "Max-Forwards: 70\r\n"
992 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
994 dialog
&& dialog
->request
? dialog
->request
: url
,
995 sip
->use_ssl
? "TLS" : sip
->udp
? "UDP" : "TCP",
996 purple_network_get_my_ip(-1),
998 branch
? ";branch=" : "",
999 branch
? branch
: "",
1001 ourtag
? ourtag
: "",
1002 get_epid(), // TODO generate one per account/login
1004 theirtag
? ";tag=" : "",
1005 theirtag
? theirtag
: "",
1006 theirepid
? ";epid=" : "",
1007 theirepid
? theirepid
: "",
1008 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
1012 dialog
&& dialog
->route
? dialog
->route
: "",
1014 body
? strlen(body
) : 0,
1018 //printf ("parsing msg buf:\n%s\n\n", buf);
1019 msg
= sipmsg_parse_msg(buf
);
1028 sign_outgoing_message (msg
, sip
, method
);
1030 buf
= sipmsg_to_string (msg
);
1032 /* add to ongoing transactions */
1033 struct transaction
* trans
= transactions_add_buf(sip
, msg
, tc
);
1034 sendout_pkt(gc
, buf
);
1039 static char *get_contact_register(struct sipe_account_data
*sip
)
1041 return g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, BYE, CANCEL, NOTIFY, ACK, BENOTIFY\";proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip
->listenport
, sip
->use_ssl
? "tls" : sip
->udp
? "udp" : "tcp",generateUUIDfromEPID(get_epid()));
1044 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1046 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
1047 char *to
= g_strdup_printf("sip:%s", sip
->username
);
1048 char *contact
= get_contact_register(sip
);
1049 //char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire);
1050 // char *hdr = g_strdup_printf("Contact: %s\r\nEvent: registration\r\nAllow-Events: presence\r\nms-keep-alive: UAC;hop-hop=yes\r\nExpires: %d\r\n", contact,expire);
1051 //char *hdr = g_strdup_printf("Contact: %s\r\nSupported: com.microsoft.msrtc.presence, adhoclist\r\nms-keep-alive: UAC;hop-hop=yes\r\nEvent: registration\r\nAllow-Events: presence\r\n", contact);
1052 char *hdr
= g_strdup_printf("Contact: %s\r\nSupported: com.microsoft.msrtc.presence, gruu-10, adhoclist\r\nEvent: registration\r\nAllow-Events: presence\r\nms-keep-alive: UAC;hop-hop=yes\r\nExpires: %d\r\n", contact
,expire
);
1055 sip
->registerstatus
= 1;
1058 sip
->reregister
= time(NULL
) + expire
- 50;
1060 sip
->reregister
= time(NULL
) + 600;
1063 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1064 process_register_response
);
1071 static void do_register(struct sipe_account_data
*sip
)
1073 do_register_exp(sip
, sip
->registerexpire
);
1076 static gchar
*parse_from(const gchar
*hdr
)
1079 const gchar
*tmp
, *tmp2
= hdr
;
1081 if (!hdr
) return NULL
;
1082 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1083 tmp
= strchr(hdr
, '<');
1085 /* i hate the different SIP UA behaviours... */
1086 if (tmp
) { /* sip address in <...> */
1088 tmp
= strchr(tmp2
, '>');
1090 from
= g_strndup(tmp2
, tmp
- tmp2
);
1092 purple_debug_info("sipe", "found < without > in From\n");
1096 tmp
= strchr(tmp2
, ';');
1098 from
= g_strndup(tmp2
, tmp
- tmp2
);
1100 from
= g_strdup(tmp2
);
1103 purple_debug_info("sipe", "got %s\n", from
);
1107 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1111 if (msg
->response
== 200 || msg
->response
== 202) {
1115 to
= parse_from(sipmsg_find_header(tc
->msg
, "To")); /* cant be NULL since it is our own msg */
1117 /* we can not subscribe -> user is offline (TODO unknown status?) */
1119 purple_prpl_got_user_status(sip
->account
, to
, "offline", NULL
);
1124 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
)
1126 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1127 gchar
*tmp
= get_contact(sip
);
1128 gchar
*contact
= g_strdup_printf(
1129 "Accept: application/pidf+xml, application/xpidf+xml\r\n"
1130 "Event: presence\r\n"
1131 "Contact: %s\r\n", tmp
);
1134 /* subscribe to buddy presence */
1135 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, contact
, "", NULL
, process_subscribe_response
);
1141 static void sipe_subscribe(struct sipe_account_data
*sip
, struct sipe_buddy
*buddy
)
1143 sipe_subscribe_to_name(sip
, buddy
->name
);
1145 /* resubscribe before subscription expires */
1146 /* add some jitter */
1147 buddy
->resubscribe
= time(NULL
)+1140+(rand()%50);
1150 static gboolean
sipe_add_lcs_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1153 xmlnode
*item
, *group
, *isc
;
1154 const char *name_group
, *group_id
;
1156 PurpleGroup
*g
= NULL
;
1160 struct sipe_buddy
*bs
;
1161 struct sipe_group
*gr
;
1162 int len
= msg
->bodylen
;
1164 // Reserved to max 10 groups. TODO be dynamic
1165 gr
= g_new0(struct sipe_group
, 10);
1167 tmp
= sipmsg_find_header(msg
, "Event");
1168 if (tmp
&& !strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1169 purple_debug_info("sipe", "sipe_add_lcs_contacts->%s-%d\n", msg
->body
, len
);
1170 /*Convert the contact from XML to Purple Buddies*/
1171 isc
= xmlnode_from_str(msg
->body
, len
);
1173 /* TODO Find for all groups */
1174 for (group
= xmlnode_get_child(isc
, "group"); group
; group
= xmlnode_get_next_twin(group
)) {
1175 name_group
= xmlnode_get_attrib(group
, "name");
1176 group_id
= xmlnode_get_attrib(group
, "id");
1178 if (!strncmp(name_group
, "~", 1)){
1179 name_group
=g_strdup("General");
1182 gr
[ng
].name_group
= g_strdup(name_group
);
1183 gr
[ng
].id
= g_strdup(group_id
);
1184 purple_debug_info("sipe", "name_group->%s\n", name_group
);
1185 g
= purple_find_group(name_group
);
1188 g
= purple_group_new(name_group
);
1189 purple_blist_add_group(g
, NULL
);
1193 g
= purple_find_group("General");
1195 g
= purple_group_new("General");
1196 purple_blist_add_group(g
, NULL
);
1204 for (i
= 0; i
< ng
;i
++) {
1205 purple_debug_info("sipe", "id->%s\n", gr
[i
].id
);
1206 purple_debug_info("sipe", "id->%s\n", gr
[i
].name_group
);
1209 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1210 const char *uri
, *name
, *groups
;
1213 uri
= xmlnode_get_attrib(item
, "uri");
1214 name
= xmlnode_get_attrib(item
, "name");
1215 groups
= xmlnode_get_attrib(item
, "groups");
1216 parts
= g_strsplit(groups
, " ", 0);
1217 purple_debug_info("sipe", "URI->%s,Groups->%s\n", uri
, groups
);
1218 if (parts
[i
]!=NULL
){
1220 purple_debug_info("sipe", "Groups->parts[i] %s\n", parts
[i
]);
1221 if (!strcmp(gr
[i
].id
,parts
[i
])){
1222 purple_debug_info("sipe", "Found Groups->gr[i].id(%s),gr[i].name_group (%s)\n",gr
[i
].id
,gr
[i
].name_group
);
1224 buddy_name
= g_strdup_printf("sip:%s", uri
);
1226 //b = purple_find_buddy(sip->account, buddy_name);
1227 b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, gr
[i
].g
);
1229 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1233 //sipe_add_buddy(sip->gc, b , gr[i].g);
1234 purple_blist_add_buddy(b
, NULL
, gr
[i
].g
, NULL
);
1235 purple_blist_alias_buddy(b
, uri
);
1236 bs
= g_new0(struct sipe_buddy
, 1);
1237 bs
->name
= g_strdup(b
->name
);
1238 g_hash_table_insert(sip
->buddies
, bs
->name
, bs
);
1250 static void sipe_subscribe_buddylist(struct sipe_account_data
*sip
)
1252 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1253 gchar
*tmp
= get_contact(sip
);
1254 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-roaming-contacts\r\nAccept: application/vnd-microsoft-roaming-contacts+xml\r\nSupported: com.microsoft.autoextend\r\nSupported: ms-benotify\r\nProxy-Require: ms-benotify\r\nSupported: ms-piggyback-first-notify\r\nContact: %s\r\n", tmp
);
1257 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, sipe_add_lcs_contacts
);
1262 /* IM Session (INVITE and MESSAGE methods) */
1264 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
1266 if (sip
== NULL
|| who
== NULL
) {
1270 struct sip_im_session
*session
;
1271 GSList
*entry
= sip
->im_sessions
;
1273 session
= entry
->data
;
1274 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
1277 entry
= entry
->next
;
1282 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
1284 struct sip_im_session
*session
= find_im_session(sip
, who
);
1286 session
= g_new0(struct sip_im_session
, 1);
1287 session
->with
= g_strdup(who
);
1288 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
1293 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1295 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
1296 // TODO free session resources
1299 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
1305 if (strncmp("sip:", session
->with
, 4)) {
1306 fullto
= g_strdup_printf("sip:%s", session
->with
);
1308 fullto
= g_strdup(session
->with
);
1311 hdr
= g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1312 //hdr = g_strdup("Content-Type: text/rtf\r\n");
1313 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
1315 tmp
= get_contact(sip
);
1316 hdr
= g_strdup_printf("Contact: %s\r\n%s", tmp
, hdr
);
1319 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msg
, session
->outgoing_dialog
, NULL
);
1327 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
1329 GSList
*entry
= session
->outgoing_message_queue
;
1331 char *queued_msg
= entry
->data
;
1332 sipe_send_message(sip
, session
, queued_msg
);
1334 // Remove from the queue and free the string
1335 entry
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
1341 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1343 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
1344 struct sip_im_session
* session
= find_im_session(sip
, with
);
1348 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
1352 if (msg
->response
!= 200) {
1353 purple_debug_info("sipe", "process_invite_response: INVITE response not 200, ignoring\n");
1354 im_session_destroy(sip
, session
);
1358 struct sip_dialog
* dialog
= session
->outgoing_dialog
;
1360 purple_debug_info("sipe", "process_invite_response: session outgoign dialog is NULL\n");
1364 dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
1365 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "From"));
1366 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "To"));
1367 if (!dialog
->theirepid
) {
1368 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "To"), "epid=", ";", NULL
);
1370 if (!dialog
->theirepid
) {
1371 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "To"), "epid=", NULL
, NULL
);
1374 dialog
->request
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Record-Route"), "<", ">", NULL
);
1375 dialog
->route
= g_strdup_printf("Route: %s\r\n", sipmsg_find_header(msg
, "Contact"));
1378 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
1380 session
->outgoing_invite
= NULL
;
1382 sipe_im_process_queue(sip
, session
);
1388 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1395 if (strstr(session
->with
, "sip:")) {
1396 to
= g_strdup(session
->with
);
1398 to
= g_strdup_printf("sip:%s", session
->with
);
1401 // Setup the outgoing dialog w/ the epid from the incoming dialog (if any)
1402 struct sip_dialog
* dialog
= g_new0(struct sip_dialog
, 1);
1403 if (session
->incoming_dialog
) {
1404 printf("incoming dialog epid is %s\n", session
->incoming_dialog
->theirepid
);
1405 dialog
->theirepid
= session
->incoming_dialog
->theirepid
;
1407 printf("incoming dialog is NULL\n");
1409 session
->outgoing_dialog
= dialog
;
1411 contact
= get_contact(sip
);
1412 hdr
= g_strdup_printf(
1414 //"Supported: ms-conf-invite\r\n"
1415 //"Supported: ms-delayed-accept\r\n"
1416 //"Supported: ms-renders-isf\r\n"
1417 //"Supported: ms-renders-gif\r\n"
1418 //"Supported: ms-renders-mime-alternative\r\n"*/
1419 //"Supported: timer\r\n"
1420 //"Supported: ms-sender\r\n"
1421 //"Supported: ms-early-media\r\n"
1422 "Content-Type: application/sdp\r\n",
1423 contact
, sip
->username
, sip
->username
, to
);
1425 body
= g_strdup_printf(
1427 "o=- 0 0 IN IP4 %s\r\n"
1431 "m=message %d sip null\r\n"
1432 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1433 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), 5061);
1435 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
1436 to
, to
, hdr
, body
, session
->outgoing_dialog
, process_invite_response
);
1445 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1448 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->outgoing_dialog
, NULL
);
1449 im_session_destroy(sip
, session
);
1454 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
1456 struct sipe_account_data
*sip
= gc
->proto_data
;
1458 purple_debug_info("sipe", "conversation with %s closed\n", who
);
1459 im_session_close(sip
, find_im_session(sip
, who
));
1463 im_session_close_all (struct sipe_account_data
*sip
)
1465 GSList
*entry
= sip
->im_sessions
;
1467 im_session_close (sip
, entry
->data
);
1468 entry
= sip
->im_sessions
;
1472 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
1474 struct sipe_account_data
*sip
= gc
->proto_data
;
1475 char *to
= g_strdup(who
);
1476 char *text
= purple_unescape_html(what
);
1478 struct sip_im_session
* session
= find_or_create_im_session(sip
, who
);
1480 // Queue the message
1481 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, text
);
1483 if (session
->outgoing_dialog
&& session
->outgoing_dialog
->callid
) {
1484 sipe_im_process_queue(sip
, session
);
1485 } else if (!session
->outgoing_invite
) {
1486 // Need to send the INVITE to get the outgoing dialog setup
1487 sipe_invite(sip
, session
);
1495 /* End IM Session (INVITE and MESSAGE methods) */
1497 static void sipe_buddy_resub(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1499 time_t curtime
= time(NULL
);
1500 if (buddy
->resubscribe
< curtime
) {
1501 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_buddy_resub %s\n", name
);
1502 sipe_subscribe(sip
, buddy
);
1506 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
1508 GSList
*tmp
= sip
->transactions
;
1509 time_t currtime
= time(NULL
);
1511 struct transaction
*trans
= tmp
->data
;
1513 purple_debug_info("sipe", "have open transaction age: %d\n", currtime
- trans
->time
);
1514 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
1517 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
1519 sendout_sipmsg(sip
, trans
->msg
);
1526 static gboolean
subscribe_timeout(struct sipe_account_data
*sip
)
1529 time_t curtime
= time(NULL
);
1530 /* register again if first registration expires */
1531 if (sip
->reregister
< curtime
) {
1534 /* check for every subscription if we need to resubscribe */
1535 //Fixxxer we need resub?
1536 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_resub
, (gpointer
)sip
);
1538 /* remove a timed out suscriber */
1542 struct sipe_watcher
*watcher
= tmp
->data
;
1543 if (watcher
->expire
< curtime
) {
1544 watcher_remove(sip
, watcher
->name
);
1547 if (tmp
) tmp
= tmp
->next
;
1553 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1557 gboolean found
= FALSE
;
1559 from
= parse_from(sipmsg_find_header(msg
, "From"));
1563 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
1565 contenttype
= sipmsg_find_header(msg
, "Content-Type");
1566 if (!contenttype
|| !strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
1567 serv_got_im(sip
->gc
, from
, msg
->body
, 0, time(NULL
));
1568 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1571 if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
1572 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1577 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1581 state
= xmlnode_get_child(isc
, "state");
1584 purple_debug_info("sipe", "process_incoming_message: no state found\n");
1589 statedata
= xmlnode_get_data(state
);
1591 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
1592 else serv_got_typing_stopped(sip
->gc
, from
);
1597 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1601 purple_debug_info("sipe", "got unknown mime-type");
1602 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
1607 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1609 gchar
* from
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "<", ">", NULL
);
1610 struct sip_im_session
* session
= find_or_create_im_session (sip
, from
);
1612 struct sip_dialog
* dialog
= g_new0(struct sip_dialog
, 1);
1613 dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
1614 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
1615 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
1616 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
1617 printf("Created incoming dialog and set epid to %s\n", dialog
->theirepid
);
1619 session
->incoming_dialog
= dialog
;
1621 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
1625 send_sip_response(sip
->gc
, msg
, 200, "OK", g_strdup_printf(
1627 "o=- 0 0 IN IP4 %s\r\n"
1631 "m=message %d sip sip:%s\r\n"
1632 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1633 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
1634 //sip->realport, sip->username
1635 5061, sip
->username
));
1638 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1640 gchar
*tmp
, krb5_token
;
1641 const gchar
*expires_header
;
1644 expires_header
= sipmsg_find_header(msg
, "Expires");
1645 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
1646 purple_debug_info("sipe", "got response to REGISTER; expires = %d\n", expires
);
1648 switch (msg
->response
) {
1651 sip
->registerstatus
= 0;
1653 sip
->registerstatus
= 3;
1654 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
1656 /* tell everybody we're online */
1657 send_publish (sip
,msg
);
1659 /* get buddies from blist; Has a bug */
1660 /*sipe_get_buddies(sip->gc);*/
1661 subscribe_timeout(sip
);
1663 //sipe_subscribe_to_name(sip, sip->username);
1665 tmp
= sipmsg_find_header(msg
, "Allow-Events");
1666 if (tmp
&& strstr(tmp
, "vnd-microsoft-provisioning")){
1667 sipe_subscribe_buddylist(sip
);
1670 // Should we remove the transaction here?
1671 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
1672 transactions_remove(sip
, tc
);
1676 if (sip
->registerstatus
!= 2) {
1677 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
1678 if (sip
->registrar
.retries
> 3) {
1679 sip
->gc
->wants_to_die
= TRUE
;
1680 purple_connection_error(sip
->gc
, _("Wrong Password"));
1683 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
1684 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
1686 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
1688 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
1689 fill_auth(sip
, tmp
, &sip
->registrar
);
1690 sip
->registerstatus
= 2;
1691 if (sip
->account
->disconnecting
) {
1692 do_register_exp(sip
, 0);
1702 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1707 gchar
*activity
= g_strdup("available");
1709 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
1710 gboolean isonline
= FALSE
;
1712 fromhdr
= sipmsg_find_header(msg
, "From");
1713 from
= parse_from(fromhdr
);
1719 pidf
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1721 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
1725 purple_debug_info("sipe", "process_incoming_notify: body(%s)\n",msg
->body
);
1727 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
1728 if ((status
= xmlnode_get_child(tuple
, "status"))) {
1729 basicstatus
= xmlnode_get_child(status
, "basic");
1734 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
1739 tmp2
= xmlnode_get_data(basicstatus
);
1741 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
1746 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", tmp2
);
1747 if (strstr(tmp2
, "open")) {
1751 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
1752 if ((status
= xmlnode_get_child(tuple
, "status"))) {
1753 if (basicstatus
= xmlnode_get_child(status
, "activities")) {
1754 if (basicstatus
= xmlnode_get_child(basicstatus
, "activity")) {
1755 activity
= xmlnode_get_data(basicstatus
);
1760 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
1765 if (strstr(activity
, "busy")) {
1767 } else if (strstr(activity
, "away")) {
1771 status_id
= "available";
1776 status_id
= "available";
1779 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
1780 purple_prpl_got_user_status(sip
->account
, from
, status_id
, NULL
);
1782 purple_prpl_got_user_status(sip
->account
, from
, "offline", NULL
);
1790 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1794 static gchar
* gen_xpidf(struct sipe_account_data
*sip
)
1796 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1798 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
1799 "<display name=\"sip:%s\"/>\r\n"
1800 "<atom id=\"1234\">\r\n"
1801 "<address uri=\"sip:%s\">\r\n"
1802 "<status status=\"%s\"/>\r\n"
1815 static gchar
* gen_pidf(struct sipe_account_data
*sip
)
1817 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1818 "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" xmlns:ep=\"urn:ietf:params:xml:ns:pidf:status:rpid-status\" xmlns:ci=\"urn:ietf:params:xml:ns:pidf:cipid\" entity=\"sip:%s\">\r\n"
1819 "<tuple id=\"0\">\r\n"
1821 "<basic>open</basic>\r\n"
1822 "<ep:activities>\r\n"
1823 " <ep:activity>%s</ep:activity>\r\n"
1827 "<ci:display-name>%s</ci:display-name>\r\n"
1835 static void send_notify(struct sipe_account_data
*sip
, struct sipe_watcher
*watcher
)
1837 gchar
*doc
= watcher
->needsxpidf
? gen_xpidf(sip
) : gen_pidf(sip
);
1838 gchar
*hdr
= watcher
->needsxpidf
? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
1839 send_sip_request(sip
->gc
, "NOTIFY", watcher
->name
, watcher
->name
, hdr
, doc
, &watcher
->dialog
, NULL
);
1843 static gboolean
process_service_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1845 if (msg
->response
!= 200 && msg
->response
!= 408) {
1846 /* never send again */
1847 sip
->republish
= -1;
1852 static void send_publish(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1854 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
1855 gchar
*doc
= g_strdup_printf(
1856 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\"><publications uri=\"%s\"><publication categoryName=\"device\" instance=\"1617359818\" container=\"2\" version=\"0\" expireType=\"endpoint\"><device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"%s\"><capabilities preferred=\"false\" uri=\"%s\"><text capture=\"true\" render=\"true\" publish=\"false\"/><gifInk capture=\"false\" render=\"true\" publish=\"false\"/><isfInk capture=\"false\" render=\"true\" publish=\"false\"/></capabilities><timezone>%s</timezone><machineName>%s</machineName></device></publication><publication categoryName=\"state\" instance=\"906391356\" container=\"2\" version=\"0\" expireType=\"endpoint\"><state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\"><availability>3500</availability><endpointLocation></endpointLocation></state></publication><publication categoryName=\"state\" instance=\"906391356\" container=\"3\" version=\"0\" expireType=\"endpoint\"><state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\"><availability>3500</availability><endpointLocation></endpointLocation></state></publication></publications></publish>",
1857 uri
, generateUUIDfromEPID(get_epid()), uri
,
1858 "00:00:00-05:00", // TODO timezone
1859 "PC" // TODO machine name
1862 gchar
*tmp
= get_contact_service(sip
);
1864 gchar
*hdr
= g_strdup_printf("Contact: %s\r\nAccept: application/ms-location-profile-definition+xml\r\nContent-Type: application/msrtc-category-publish+xml\r\n", tmp
);
1867 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_service_response
);
1868 //sip->republish = time(NULL) + 500;
1874 static void send_service(struct sipe_account_data
*sip
)
1876 //gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->sipdomain);
1877 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
1878 //gchar *doc = gen_pidf(sip);
1880 gchar
*doc
= gen_pidf(sip
);
1881 gchar
*hdr
= g_strdup("Event: presence\r\nContent-Type: application/pidf+xml\r\n");
1883 //gchar *hdr = g_strdup("Content-Type: application/SOAP+xml\r\n");
1884 gchar
*tmp
= get_contact(sip
);
1885 hdr
= g_strdup_printf("Contact: %s\r\n%s; +sip.instance=\"<urn:uuid:%s>\"", tmp
, hdr
,generateUUIDfromEPID(get_epid()));
1887 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
,
1889 doc
, NULL
, process_service_response
);
1890 sip
->republish
= time(NULL
) + 500;
1896 static void process_incoming_subscribe(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1898 const char *from_hdr
= sipmsg_find_header(msg
, "From");
1899 gchar
*from
= parse_from(from_hdr
);
1900 gchar
*theirtag
= find_tag(from_hdr
);
1901 gchar
*ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
1902 gboolean tagadded
= FALSE
;
1903 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1904 gchar
*expire
= sipmsg_find_header(msg
, "Expire");
1905 // gchar *ms-received-port =find_received_port(sipmsg_find_header(msg, "Contact"));
1907 struct sipe_watcher
*watcher
= watcher_find(sip
, from
);
1912 if (!watcher
) { /* new subscription */
1913 gchar
*acceptheader
= sipmsg_find_header(msg
, "Accept");
1914 gboolean needsxpidf
= FALSE
;
1915 if (!purple_privacy_check(sip
->account
, from
)) {
1916 send_sip_response(sip
->gc
, msg
, 202, "Ok", NULL
);
1920 gchar
*tmp
= acceptheader
;
1921 gboolean foundpidf
= FALSE
;
1922 gboolean foundxpidf
= FALSE
;
1923 while (tmp
&& tmp
< acceptheader
+ strlen(acceptheader
)) {
1924 gchar
*tmp2
= strchr(tmp
, ',');
1925 if (tmp2
) *tmp2
= '\0';
1926 if (!strcmp("application/pidf+xml", tmp
))
1928 if (!strcmp("application/xpidf+xml", tmp
))
1933 while (*tmp
== ' ') tmp
++;
1937 if (!foundpidf
&& foundxpidf
) needsxpidf
= TRUE
;
1938 g_free(acceptheader
);
1940 watcher
= watcher_create(sip
, from
, callid
, ourtag
, theirtag
, needsxpidf
);
1943 gchar
*to
= g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg
, "To"), ourtag
);
1944 sipmsg_remove_header(msg
, "To");
1945 sipmsg_add_header(msg
, "To", to
);
1949 watcher
->expire
= time(NULL
) + strtol(expire
, NULL
, 10);
1951 watcher
->expire
= time(NULL
) + 600;
1953 sipmsg_remove_header(msg
, "Contact");
1954 tmp
= get_contact(sip
);
1955 sipmsg_add_header(msg
, "Contact", tmp
);
1957 purple_debug_info("sipe", "got subscribe: name %s ourtag %s theirtag %s callid %s\n", watcher
->name
, watcher
->dialog
.ourtag
, watcher
->dialog
.theirtag
, watcher
->dialog
.callid
);
1958 send_sip_response(sip
->gc
, msg
, 200, "Ok", NULL
);
1959 send_notify(sip
, watcher
);
1968 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1970 gboolean found
= FALSE
;
1971 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
1972 if (msg
->response
== 0) { /* request */
1973 if (!strcmp(msg
->method
, "MESSAGE")) {
1974 process_incoming_message(sip
, msg
);
1976 } else if (!strcmp(msg
->method
, "NOTIFY")) {
1977 purple_debug_info("sipe","send->process_incoming_notify\n");
1978 process_incoming_notify(sip
, msg
);
1980 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
1981 purple_debug_info("sipe","send->process_incoming_subscribe\n");
1982 process_incoming_subscribe(sip
, msg
);
1984 } else if (!strcmp(msg
->method
, "INVITE")) {
1985 process_incoming_invite(sip
, msg
);
1987 } else if (!strcmp(msg
->method
, "INFO")) {
1989 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
1991 serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
1993 printf("INFO body:\n%s\n", msg
->body
);
1994 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1996 } else if (!strcmp(msg
->method
, "ACK")) {
1997 // ACK's don't need any response
1998 //send_sip_response(sip->gc, msg, 200, "OK", NULL);
2000 } else if (!strcmp(msg
->method
, "BYE")) {
2001 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2003 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
2004 struct sip_im_session
* session
= find_im_session (sip
, from
);
2008 // TODO Let the user know the other user left the conversation?
2009 im_session_destroy(sip
, session
);
2014 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
2016 } else { /* response */
2017 struct transaction
*trans
= transactions_find(sip
, msg
);
2019 if (msg
->response
== 407) {
2020 gchar
*resend
, *auth
, *ptmp
;
2022 if (sip
->proxy
.retries
> 30) return;
2023 sip
->proxy
.retries
++;
2024 /* do proxy authentication */
2026 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
2028 fill_auth(sip
, ptmp
, &sip
->proxy
);
2029 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
, TRUE
);
2030 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
2031 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
2033 resend
= sipmsg_to_string(trans
->msg
);
2034 /* resend request */
2035 sendout_pkt(sip
->gc
, resend
);
2038 if (msg
->response
== 100) {
2039 /* ignore provisional response */
2040 purple_debug_info("sipe", "got trying response\n");
2042 sip
->proxy
.retries
= 0;
2043 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
2044 if (msg
->response
== 401) sip
->registrar
.retries
++;
2045 else sip
->registrar
.retries
= 0;
2046 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
2048 if (msg
->response
== 401) {
2049 gchar
*resend
, *auth
, *ptmp
;
2051 if (sip
->registrar
.retries
> 4) return;
2052 sip
->registrar
.retries
++;
2054 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
2055 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
2057 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
2060 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
2062 fill_auth(sip
, ptmp
, &sip
->registrar
);
2063 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
, TRUE
);
2064 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
2065 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
2067 //sipmsg_remove_header(trans->msg, "Authorization");
2068 //sipmsg_add_header(trans->msg, "Authorization", auth);
2070 resend
= sipmsg_to_string(trans
->msg
);
2071 /* resend request */
2072 sendout_pkt(sip
->gc
, resend
);
2077 gchar
*contact
= sipmsg_find_header(msg
, "Contact");
2078 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Get Server Contact Header:%s\r\n",contact
);
2079 msg
->contact
= contact
;
2082 if (trans
->callback
) {
2083 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
2084 /* call the callback to process response*/
2085 (trans
->callback
)(sip
, msg
, trans
);
2087 /* Not sure if this is needed or what needs to be done
2088 but transactions seem to be removed prematurely so
2089 this only removes them if the response is 200 OK */
2090 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
2091 /*Has a bug and it's unneccesary*/
2092 /*transactions_remove(sip, trans);*/
2098 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction");
2102 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
2106 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
2114 /* according to the RFC remove CRLF at the beginning */
2115 while (*cur
== '\r' || *cur
== '\n') {
2118 if (cur
!= conn
->inbuf
) {
2119 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
2120 conn
->inbufused
= strlen(conn
->inbuf
);
2123 /* Received a full Header? */
2124 if ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
) {
2125 time_t currtime
= time(NULL
);
2128 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
2129 msg
= sipmsg_parse_header(conn
->inbuf
);
2132 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
2133 if (restlen
>= msg
->bodylen
) {
2134 dummy
= g_malloc(msg
->bodylen
+ 1);
2135 memcpy(dummy
, cur
, msg
->bodylen
);
2136 dummy
[msg
->bodylen
] = '\0';
2138 cur
+= msg
->bodylen
;
2139 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
2140 conn
->inbufused
= strlen(conn
->inbuf
);
2146 // Verify the signature before processing it
2147 if (sip
->registrar
.ntlm_key
) {
2148 struct sipmsg_breakdown msgbd
;
2150 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
2151 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
2153 if (signature_input_str
!= NULL
) {
2154 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
2157 gchar
* rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
2158 if (signature
!= NULL
&& rspauth
!= NULL
) {
2159 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
2160 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
2161 process_input_message(sip
, msg
);
2163 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
2167 sipmsg_breakdown_free(&msgbd
);
2169 process_input_message(sip
, msg
);
2173 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a incomplete sip msg: %s\n", conn
->inbuf
);
2177 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
2179 PurpleConnection
*gc
= data
;
2180 struct sipe_account_data
*sip
= gc
->proto_data
;
2185 static char buffer
[65536];
2186 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
2188 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
2189 msg
= sipmsg_parse_msg(buffer
);
2190 if (msg
) process_input_message(sip
, msg
);
2194 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
2196 PurpleConnection
*gc
= data
;
2197 struct sipe_account_data
*sip
= gc
->proto_data
;
2198 struct sip_connection
*conn
= NULL
;
2200 static char buf
[4096];
2202 /* TODO: It should be possible to make this check unnecessary */
2203 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
2204 if (gsc
) purple_ssl_close(gsc
);
2209 conn
= connection_find(sip
, sip
->gsc
->fd
);
2211 purple_debug_error("sipe", "Connection not found!\n");
2212 if (sip
->gsc
) purple_ssl_close(sip
->gsc
);
2217 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
2218 conn
->inbuflen
+= SIMPLE_BUF_INC
;
2219 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
2222 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
2224 if (len
< 0 && errno
== EAGAIN
) {
2225 /* Try again later */
2227 } else if (len
< 0) {
2228 purple_debug_info("sipe", "sipe_input_cb_ssl: read error\n");
2230 connection_remove(sip
, sip
->gsc
->fd
);
2231 if (sip
->fd
== gsc
->fd
) sip
->fd
= -1;
2234 } else if (len
== 0) {
2235 purple_connection_error(gc
, _("Server has disconnected"));
2237 connection_remove(sip
, sip
->gsc
->fd
);
2238 if (sip
->fd
== gsc
->fd
) sip
->fd
= -1;
2243 conn
->inbufused
+= len
;
2244 conn
->inbuf
[conn
->inbufused
] = '\0';
2246 process_input(sip
, conn
);
2250 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
2252 PurpleConnection
*gc
= data
;
2253 struct sipe_account_data
*sip
= gc
->proto_data
;
2255 struct sip_connection
*conn
= connection_find(sip
, source
);
2257 purple_debug_error("sipe", "Connection not found!\n");
2261 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
2262 conn
->inbuflen
+= SIMPLE_BUF_INC
;
2263 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
2266 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
2268 if (len
< 0 && errno
== EAGAIN
)
2270 else if (len
<= 0) {
2271 purple_debug_info("sipe", "sipe_input_cb: read error\n");
2272 connection_remove(sip
, source
);
2273 if (sip
->fd
== source
) sip
->fd
= -1;
2277 conn
->inbufused
+= len
;
2278 conn
->inbuf
[conn
->inbufused
] = '\0';
2280 process_input(sip
, conn
);
2283 /* Callback for new connections on incoming TCP port */
2284 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
2286 PurpleConnection
*gc
= data
;
2287 struct sipe_account_data
*sip
= gc
->proto_data
;
2288 struct sip_connection
*conn
;
2290 int newfd
= accept(source
, NULL
, NULL
);
2292 conn
= connection_create(sip
, newfd
);
2294 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2297 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
2299 PurpleConnection
*gc
= data
;
2300 struct sipe_account_data
*sip
;
2301 struct sip_connection
*conn
;
2303 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2311 purple_connection_error(gc
, _("Could not connect"));
2315 sip
= gc
->proto_data
;
2318 conn
= connection_create(sip
, source
);
2320 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2324 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2327 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
2329 PurpleConnection
*gc
= data
;
2330 struct sipe_account_data
*sip
;
2331 struct sip_connection
*conn
;
2333 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2335 if (gsc
) purple_ssl_close(gsc
);
2339 sip
= gc
->proto_data
;
2341 conn
= connection_create(sip
, sip
->fd
);
2342 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
2343 sip
->listenfd
= sip
->fd
;
2344 sip
->registertimeout
= purple_timeout_add((rand()%100)+3*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2348 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
2351 static guint
sipe_ht_hash_nick(const char *nick
)
2353 char *lc
= g_utf8_strdown(nick
, -1);
2354 guint bucket
= g_str_hash(lc
);
2360 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
2362 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
2365 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
2367 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2369 sip
->listen_data
= NULL
;
2371 if (listenfd
== -1) {
2372 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2378 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
2379 sip
->listenfd
= sip
->fd
;
2381 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
2383 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
2384 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2388 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
2390 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2393 sip
->query_data
= NULL
;
2395 if (!hosts
|| !hosts
->data
) {
2396 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
2400 addr_size
= GPOINTER_TO_INT(hosts
->data
);
2401 hosts
= g_slist_remove(hosts
, hosts
->data
);
2402 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
2403 g_free(hosts
->data
);
2404 hosts
= g_slist_remove(hosts
, hosts
->data
);
2406 hosts
= g_slist_remove(hosts
, hosts
->data
);
2407 g_free(hosts
->data
);
2408 hosts
= g_slist_remove(hosts
, hosts
->data
);
2411 /* create socket for incoming connections */
2412 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
2413 sipe_udp_host_resolved_listen_cb
, sip
);
2414 if (sip
->listen_data
== NULL
) {
2415 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2420 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
2423 PurpleConnection
*gc
= data
;
2424 struct sipe_account_data
*sip
;
2426 /* If the connection is already disconnected, we don't need to do anything else */
2427 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2430 sip
= gc
->proto_data
;
2434 case PURPLE_SSL_CONNECT_FAILED
:
2435 purple_connection_error(gc
, _("Connection Failed"));
2437 case PURPLE_SSL_HANDSHAKE_FAILED
:
2438 purple_connection_error(gc
, _("SSL Handshake Failed"));
2444 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
2446 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2447 PurpleProxyConnectData
*connect_data
;
2449 sip
->listen_data
= NULL
;
2451 sip
->listenfd
= listenfd
;
2452 if (sip
->listenfd
== -1) {
2453 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2457 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
2458 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2459 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
2460 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
2461 sipe_newconn_cb
, sip
->gc
);
2462 purple_debug_info("sipe", "connecting to %s port %d\n",
2463 sip
->realhostname
, sip
->realport
);
2464 /* open tcp connection to the server */
2465 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
2466 sip
->realport
, login_cb
, sip
->gc
);
2468 if (connect_data
== NULL
) {
2469 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
2475 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
2477 struct sipe_account_data
*sip
;
2482 sip
->srv_query_data
= NULL
;
2484 port
= purple_account_get_int(sip
->account
, "port", 0);
2486 /* find the host to connect to */
2488 hostname
= g_strdup(resp
->hostname
);
2489 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s\r\n", hostname
);
2494 if (!purple_account_get_bool(sip
->account
, "useproxy", FALSE
)) {
2495 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - using sipdomain\r\n");
2496 hostname
= g_strdup(sip
->sipdomain
);
2498 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - using specified SIP proxy\r\n");
2499 hostname
= g_strdup(purple_account_get_string(sip
->account
, "proxy", sip
->sipdomain
));
2503 sip
->realhostname
= hostname
;
2504 sip
->realport
= port
;
2505 if (!sip
->realport
) sip
->realport
= 5060;
2509 // /* create socket for incoming connections */
2510 // sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_STREAM,
2511 // sipe_tcp_connect_listen_cb, sip);
2512 // if (sip->listen_data == NULL) {
2513 // purple_connection_error(sip->gc, _("Could not create listen socket"));
2516 //} else { /* UDP */
2517 // purple_debug_info("sipe", "using udp with server %s and port %d\n", hostname, port);
2519 // sip->query_data = purple_dnsquery_a(hostname, port, sipe_udp_host_resolved, sip);
2520 // if (sip->query_data == NULL) {
2521 // purple_connection_error(sip->gc, _("Could not resolve hostname"));
2526 static void sipe_login(PurpleAccount
*account
)
2528 PurpleConnection
*gc
;
2529 struct sipe_account_data
*sip
;
2531 gchar
*hosttoconnect
;
2533 const char *username
= purple_account_get_username(account
);
2534 gc
= purple_account_get_connection(account
);
2536 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
2537 gc
->wants_to_die
= TRUE
;
2538 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
2542 if (!purple_account_get_bool(account
, "ssl", FALSE
)){
2543 if (!purple_ssl_is_supported())
2545 gc
->wants_to_die
= TRUE
;
2546 purple_connection_error(gc
,
2547 _("SSL support is needed for SSL/TLS support. Please install a supported "
2554 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
2556 sip
->account
= account
;
2557 sip
->registerexpire
= 900;
2558 sip
->udp
= purple_account_get_bool(account
, "udp", FALSE
);
2559 sip
->use_ssl
= purple_account_get_bool(account
, "ssl", FALSE
);
2561 purple_debug_info("sipe", "sip->use_ssl->%d\n", sip
->use_ssl
);
2563 /* TODO: is there a good default grow size? */
2565 sip
->txbuf
= purple_circ_buffer_new(0);
2567 userserver
= g_strsplit(username
, "@", 2);
2568 purple_connection_set_display_name(gc
, userserver
[0]);
2569 sip
->username
= g_strdup(g_strjoin("@", userserver
[0], userserver
[1], NULL
));
2570 sip
->sipdomain
= g_strdup(userserver
[1]);
2571 sip
->password
= g_strdup(purple_connection_get_password(gc
));
2572 g_strfreev(userserver
);
2575 // Communicator queries _sipinternaltls._tcp.domain.com and uses that
2576 // information to connect to the OCS server.
2578 // XXX FIXME: eventually we should also query for sipexternaltls as well
2579 // if Pidgin is not on the local LAN
2580 // This doesn't quite work as advertised yet so make sure your have
2581 // your OCS FQDN in the proxy setting in the SIPE account settings
2583 sip
->srv_query_data
= purple_srv_resolve("sipinternaltls", "tcp", sip
->sipdomain
, srvresolved
, sip
);
2586 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - realhostname: %s\r\n", sip
->realhostname
);
2588 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
2590 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
2592 /* TODO: Set the status correctly. */
2593 sip
->status
= g_strdup("available");
2595 if (!purple_account_get_bool(account
, "useproxy", FALSE
)) {
2596 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - checking realhostname again: %s\r\n", sip
->realhostname
);
2597 hosttoconnect
= g_strdup(sip
->sipdomain
);
2599 hosttoconnect
= g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
));
2603 purple_debug_info("sipe", "HosttoConnect->%s\n", hosttoconnect
);
2606 sip
->gsc
= purple_ssl_connect(account
,hosttoconnect
, purple_account_get_int(account
, "port", 5061), login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
2609 sip
->srv_query_data
= purple_srv_resolve("sip",
2610 sip
->udp
? "udp" : "tcp", hosttoconnect
, srvresolved
, sip
);
2613 g_free(hosttoconnect
);
2616 static void sipe_close(PurpleConnection
*gc
)
2618 struct sipe_account_data
*sip
= gc
->proto_data
;
2621 /* leave all conversations */
2622 im_session_close_all(sip
);
2625 do_register_exp(sip
, 0);
2626 connection_free_all(sip
);
2628 if (sip
->query_data
!= NULL
)
2629 purple_dnsquery_destroy(sip
->query_data
);
2631 if (sip
->srv_query_data
!= NULL
)
2632 purple_srv_cancel(sip
->srv_query_data
);
2634 if (sip
->listen_data
!= NULL
)
2635 purple_network_listen_cancel(sip
->listen_data
);
2637 g_free(sip
->sipdomain
);
2638 g_free(sip
->username
);
2639 g_free(sip
->password
);
2640 g_free(sip
->registrar
.nonce
);
2641 g_free(sip
->registrar
.opaque
);
2642 g_free(sip
->registrar
.target
);
2643 g_free(sip
->registrar
.realm
);
2644 g_free(sip
->registrar
.digest_session_key
);
2645 g_free(sip
->proxy
.nonce
);
2646 g_free(sip
->proxy
.opaque
);
2647 g_free(sip
->proxy
.target
);
2648 g_free(sip
->proxy
.realm
);
2649 g_free(sip
->proxy
.digest_session_key
);
2651 purple_circ_buffer_destroy(sip
->txbuf
);
2652 g_free(sip
->realhostname
);
2653 if (sip
->listenpa
) purple_input_remove(sip
->listenpa
);
2654 if (sip
->tx_handler
) purple_input_remove(sip
->tx_handler
);
2655 if (sip
->resendtimeout
) purple_timeout_remove(sip
->resendtimeout
);
2656 if (sip
->registertimeout
) purple_timeout_remove(sip
->registertimeout
);
2658 g_free(gc
->proto_data
);
2659 gc
->proto_data
= NULL
;
2662 /* not needed since privacy is checked for every subscribe */
2663 static void dummy_add_deny(PurpleConnection
*gc
, const char *name
) {
2666 static void dummy_permit_deny(PurpleConnection
*gc
)
2670 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
2676 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
2682 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
2686 static PurplePlugin
*my_protocol
= NULL
;
2688 static PurplePluginProtocolInfo prpl_info
=
2691 NULL
, /* user_splits */
2692 NULL
, /* protocol_options */
2693 NO_BUDDY_ICONS
, /* icon_spec */
2694 sipe_list_icon
, /* list_icon */
2695 NULL
, /* list_emblems */
2696 NULL
, /* status_text */
2697 NULL
, /* tooltip_text */
2698 sipe_status_types
, /* away_states */
2699 NULL
, /* blist_node_menu */
2700 NULL
, /* chat_info */
2701 NULL
, /* chat_info_defaults */
2702 sipe_login
, /* login */
2703 sipe_close
, /* close */
2704 sipe_im_send
, /* send_im */
2705 NULL
, /* set_info */
2706 // sipe_typing, /* send_typing */
2707 NULL
, /* send_typing */
2708 NULL
, /* get_info */
2709 sipe_set_status
, /* set_status */
2710 NULL
, /* set_idle */
2711 NULL
, /* change_passwd */
2712 sipe_add_buddy
, /* add_buddy */
2713 NULL
, /* add_buddies */
2714 sipe_remove_buddy
, /* remove_buddy */
2715 NULL
, /* remove_buddies */
2716 dummy_add_deny
, /* add_permit */
2717 dummy_add_deny
, /* add_deny */
2718 dummy_add_deny
, /* rem_permit */
2719 dummy_add_deny
, /* rem_deny */
2720 dummy_permit_deny
, /* set_permit_deny */
2721 NULL
, /* join_chat */
2722 NULL
, /* reject_chat */
2723 NULL
, /* get_chat_name */
2724 NULL
, /* chat_invite */
2725 NULL
, /* chat_leave */
2726 NULL
, /* chat_whisper */
2727 NULL
, /* chat_send */
2728 sipe_keep_alive
, /* keepalive */
2729 NULL
, /* register_user */
2730 NULL
, /* get_cb_info */
2731 NULL
, /* get_cb_away */
2732 NULL
, /* alias_buddy */
2733 NULL
, /* group_buddy */
2734 NULL
, /* rename_group */
2735 NULL
, /* buddy_free */
2736 sipe_convo_closed
, /* convo_closed */
2737 purple_normalize_nocase
, /* normalize */
2738 NULL
, /* set_buddy_icon */
2739 NULL
, /* remove_group */
2740 NULL
, /* get_cb_real_name */
2741 NULL
, /* set_chat_topic */
2742 NULL
, /* find_blist_chat */
2743 NULL
, /* roomlist_get_list */
2744 NULL
, /* roomlist_cancel */
2745 NULL
, /* roomlist_expand_category */
2746 NULL
, /* can_receive_file */
2747 NULL
, /* send_file */
2748 NULL
, /* new_xfer */
2749 NULL
, /* offline_message */
2750 NULL
, /* whiteboard_prpl_ops */
2751 sipe_send_raw
, /* send_raw */
2755 static PurplePluginInfo info
= {
2756 PURPLE_PLUGIN_MAGIC
,
2757 PURPLE_MAJOR_VERSION
,
2758 PURPLE_MINOR_VERSION
,
2759 PURPLE_PLUGIN_PROTOCOL
, /**< type */
2760 NULL
, /**< ui_requirement */
2762 NULL
, /**< dependencies */
2763 PURPLE_PRIORITY_DEFAULT
, /**< priority */
2764 "prpl-sipe", /**< id */
2765 "Microsoft LCS/OCS", /**< name */
2766 VERSION
, /**< version */
2767 N_("SIP/SIMPLE Exchange Protocol Plugin"), /** summary */
2768 N_("The SIP/SIMPLE Exchange Protocol Plugin"), /** description */
2769 "Anibal Avelar <avelar@gmail.com>", /**< author */
2770 PURPLE_WEBSITE
, /**< homepage */
2771 sipe_plugin_load
, /**< load */
2772 sipe_plugin_unload
, /**< unload */
2773 sipe_plugin_destroy
, /**< destroy */
2774 NULL
, /**< ui_info */
2775 &prpl_info
, /**< extra_info */
2784 static void init_plugin(PurplePlugin
*plugin
)
2786 PurpleAccountUserSplit
*split
;
2787 PurpleAccountOption
*option
;
2789 purple_plugin_register(plugin
);
2791 //split = purple_account_user_split_new(_("Server"), "", '@');
2792 //prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
2793 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
2794 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2795 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
2796 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2798 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
2799 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2801 option
= purple_account_option_bool_new(_("Use SSL/TLS"), "ssl", FALSE
);
2802 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
,option
);
2804 option
= purple_account_option_string_new(_("UserAgent"), "useragent", "Purple/" VERSION
);
2805 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
,option
);
2807 option
= purple_account_option_bool_new(_("Use UDP"), "udp", FALSE
);
2808 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2810 option
= purple_account_option_int_new(_("Connect port"), "port", 5060);
2811 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2813 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
2814 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2816 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
2817 option
= purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
2818 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2820 /*option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2821 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2822 option = purple_account_option_string_new(_("Proxy"), "proxy", "");
2823 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2824 option
= purple_account_option_string_new(_("Auth User"), "authuser", "");
2825 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2826 option
= purple_account_option_string_new(_("Auth Domain"), "authdomain", "");
2827 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2828 my_protocol
= plugin
;
2832 /* I had to redefined the function for it load, but works */
2833 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
2834 plugin
->info
= &(info
);
2835 init_plugin((plugin
));
2836 sipe_plugin_load((plugin
));
2837 return purple_plugin_register(plugin
);