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 char *getuuid()
73 return g_strdup_printf("01010101"); //TODO Should be taken from the MAC ADDRESS
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
);
130 static void do_notifies(struct sipe_account_data
*sip
)
132 GSList
*tmp
= sip
->watcher
;
133 purple_debug_info("sipe", "do_notifies()\n");
134 //if ((sip->republish != -1) || sip->republish < time(NULL)) {
135 // if (purple_account_get_bool(sip->account, "doservice", TRUE)) {
136 // send_service(sip);
141 purple_debug_info("sipe", "notifying %s\n", ((struct sipe_watcher
*)tmp
->data
)->name
);
142 send_notify(sip
, tmp
->data
);
147 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
149 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(purple_status_get_type(status
));
150 struct sipe_account_data
*sip
= NULL
;
152 if (!purple_status_is_active(status
))
156 sip
= account
->gc
->proto_data
;
161 if (primitive
== PURPLE_STATUS_AVAILABLE
)
162 sip
->status
= g_strdup("available");
164 sip
->status
= g_strdup("busy");
170 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
172 struct sip_connection
*ret
= NULL
;
173 GSList
*entry
= sip
->openconns
;
176 if (ret
->fd
== fd
) return ret
;
182 static struct sipe_watcher
*watcher_find(struct sipe_account_data
*sip
,
185 struct sipe_watcher
*watcher
;
186 GSList
*entry
= sip
->watcher
;
188 watcher
= entry
->data
;
189 if (!strcmp(name
, watcher
->name
)) return watcher
;
195 static struct sipe_watcher
*watcher_create(struct sipe_account_data
*sip
,
196 const gchar
*name
, const gchar
*callid
, const gchar
*ourtag
,
197 const gchar
*theirtag
, gboolean needsxpidf
)
199 struct sipe_watcher
*watcher
= g_new0(struct sipe_watcher
, 1);
200 watcher
->name
= g_strdup(name
);
201 watcher
->dialog
.callid
= g_strdup(callid
);
202 watcher
->dialog
.ourtag
= g_strdup(ourtag
);
203 watcher
->dialog
.theirtag
= g_strdup(theirtag
);
204 watcher
->needsxpidf
= needsxpidf
;
205 sip
->watcher
= g_slist_append(sip
->watcher
, watcher
);
209 static void watcher_remove(struct sipe_account_data
*sip
, const gchar
*name
)
211 struct sipe_watcher
*watcher
= watcher_find(sip
, name
);
212 sip
->watcher
= g_slist_remove(sip
->watcher
, watcher
);
213 g_free(watcher
->name
);
214 g_free(watcher
->dialog
.callid
);
215 g_free(watcher
->dialog
.ourtag
);
216 g_free(watcher
->dialog
.theirtag
);
220 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
222 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
224 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
228 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
230 struct sip_connection
*conn
= connection_find(sip
, fd
);
231 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
232 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
237 static void connection_free_all(struct sipe_account_data
*sip
)
239 struct sip_connection
*ret
= NULL
;
240 GSList
*entry
= sip
->openconns
;
243 connection_remove(sip
, ret
->fd
);
244 entry
= sip
->openconns
;
248 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
250 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
251 struct sipe_buddy
*b
;
252 if (strncmp("sip:", buddy
->name
, 4)) {
253 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
254 purple_blist_rename_buddy(buddy
, buf
);
257 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
258 b
= g_new0(struct sipe_buddy
, 1);
259 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
260 b
->name
= g_strdup(buddy
->name
);
261 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
263 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
267 static void sipe_get_buddies(PurpleConnection
*gc
)
269 PurpleBlistNode
*gnode
, *cnode
, *bnode
;
271 purple_debug_info("sipe", "sipe_get_buddies\n");
273 for (gnode
= purple_get_blist()->root
; gnode
; gnode
= gnode
->next
) {
274 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode
)) continue;
275 for (cnode
= gnode
->child
; cnode
; cnode
= cnode
->next
) {
276 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode
)) continue;
277 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
278 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode
)) continue;
279 if (((PurpleBuddy
*)bnode
)->account
== gc
->account
)
280 sipe_add_buddy(gc
, (PurpleBuddy
*)bnode
, (PurpleGroup
*)gnode
);
286 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
288 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
289 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
290 g_hash_table_remove(sip
->buddies
, buddy
->name
);
295 static GList
*sipe_status_types(PurpleAccount
*acc
)
297 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
);
306 type
= purple_status_type_new_full(
307 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
308 types
= g_list_append(types
, type
);
313 //static struct sipe_krb5_auth krb5_auth;
314 static gchar
*auth_header_without_newline(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
, gboolean force_reauth
)
316 const gchar
*method
= msg
->method
;
317 const gchar
*target
= msg
->target
;
322 const char *authdomain
;
323 const char *authuser
;
324 const char *krb5_realm
;
326 gchar
*krb5_token
= NULL
;
328 authdomain
= purple_account_get_string(sip
->account
, "authdomain", "");
329 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
331 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
332 // and do error checking
334 // KRB realm should always be uppercase
335 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
337 if (sip
->realhostname
) {
338 host
= sip
->realhostname
;
339 } else if (purple_account_get_bool(sip
->account
, "use_proxy", TRUE
)) {
340 host
= purple_account_get_string(sip
->account
, "proxy", "");
342 host
= sip
->sipdomain
;
345 /*gboolean new_auth = krb5_auth.gss_context == NULL;
347 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
350 if (new_auth || force_reauth) {
351 krb5_token = krb5_auth.base64_token;
354 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
355 krb5_token = krb5_auth.base64_token;*/
357 if (!authuser
|| strlen(authuser
) < 1) {
358 authuser
= sip
->username
;
361 if (auth
->type
== 1) { /* Digest */
362 sprintf(noncecount
, "%08d", auth
->nc
++);
363 response
= purple_cipher_http_digest_calculate_response(
364 "md5", method
, target
, NULL
, NULL
,
365 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
366 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
368 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
);
371 } else if (auth
->type
== 2) { /* NTLM */
372 // If we have a signature for the message, include that
373 if (msg
->signature
) {
374 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
);
378 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
379 /* TODO: Don't hardcode "purple" as the hostname */
380 const gchar
* ntlm_key
;
381 gchar
* gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, "purple", authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
382 auth
->ntlm_key
= (gchar
*)ntlm_key
;
383 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
388 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
390 } else if (auth
->type
== 3) {
393 /*if (new_auth || force_reauth) {
394 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
396 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);
398 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
401 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
402 gchar * mic = "MICTODO";
403 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
404 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
405 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
406 //auth->opaque ? auth->opaque : "", auth->target);
407 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
412 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
415 sprintf(noncecount
, "%08d", auth
->nc
++);
416 response
= purple_cipher_http_digest_calculate_response(
417 "md5", method
, target
, NULL
, NULL
,
418 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
419 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
421 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
);
426 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
, gboolean force_reauth
)
428 gchar
*with
, *without
;
430 without
= auth_header_without_newline(sip
, auth
, msg
, force_reauth
);
431 with
= g_strdup_printf("%s\r\n", without
);
437 static char *parse_attribute(const char *attrname
, const char *source
)
439 const char *tmp
, *tmp2
;
441 int len
= strlen(attrname
);
443 if (!strncmp(source
, attrname
, len
)) {
445 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
447 retval
= g_strndup(tmp
, tmp2
- tmp
);
449 retval
= g_strdup(tmp
);
455 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
458 const char *authuser
;
461 const char *krb5_realm
;
464 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
465 // and do error checking
467 // KRB realm should always be uppercase
468 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
470 if (sip->realhostname) {
471 host = sip->realhostname;
472 } else if (purple_account_get_bool(sip->account, "use_proxy", TRUE)) {
473 host = purple_account_get_string(sip->account, "proxy", "");
475 host = sip->sipdomain;
478 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
480 if (!authuser
|| strlen(authuser
) < 1) {
481 authuser
= sip
->username
;
485 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
489 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
491 parts
= g_strsplit(hdr
+5, "\", ", 0);
494 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
495 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
496 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
499 if ((tmp
= parse_attribute("targetname=\"",
503 else if ((tmp
= parse_attribute("realm=\"",
507 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
514 if (!strstr(hdr
, "gssapi-data")) {
522 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
523 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
525 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
526 parts
= g_strsplit(hdr
+9, "\", ", 0);
529 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
530 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
531 /*if (krb5_auth.gss_context == NULL) {
532 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
534 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
537 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
539 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
541 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
548 //if (!strstr(hdr, "gssapi-data")) {
557 parts
= g_strsplit(hdr
, " ", 0);
559 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
562 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
569 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
571 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
572 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
578 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
580 PurpleConnection
*gc
= data
;
581 struct sipe_account_data
*sip
= gc
->proto_data
;
585 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
587 if (max_write
== 0) {
588 purple_input_remove(sip
->tx_handler
);
593 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
595 if (written
< 0 && errno
== EAGAIN
)
597 else if (written
<= 0) {
598 /*TODO: do we really want to disconnect on a failure to write?*/
599 purple_connection_error(gc
, _("Could not write"));
603 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
606 static void sipe_canwrite_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
608 PurpleConnection
*gc
= data
;
609 struct sipe_account_data
*sip
= gc
->proto_data
;
613 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
615 if (max_write
== 0) {
616 purple_input_remove(sip
->tx_handler
);
621 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
623 if (written
< 0 && errno
== EAGAIN
)
625 else if (written
<= 0) {
626 /*TODO: do we really want to disconnect on a failure to write?*/
627 purple_connection_error(gc
, _("Could not write"));
631 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
634 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
636 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
638 PurpleConnection
*gc
= data
;
639 struct sipe_account_data
*sip
;
640 struct sip_connection
*conn
;
642 if (!PURPLE_CONNECTION_IS_VALID(gc
))
650 purple_connection_error(gc
, _("Could not connect"));
654 sip
= gc
->proto_data
;
656 sip
->connecting
= FALSE
;
658 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
660 /* If there is more to write now, we need to register a handler */
661 if (sip
->txbuf
->bufused
> 0)
662 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
,
663 sipe_canwrite_cb
, gc
);
665 conn
= connection_create(sip
, source
);
666 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
669 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
671 PurpleConnection
*gc
= data
;
672 struct sipe_account_data
*sip
;
673 struct sip_connection
*conn
;
675 if (!PURPLE_CONNECTION_IS_VALID(gc
))
677 if(gsc
) purple_ssl_close(gsc
);
681 sip
= gc
->proto_data
;
683 sip
->connecting
= FALSE
;
685 sipe_canwrite_cb_ssl(gc
, gsc
, PURPLE_INPUT_WRITE
);
687 /* If there is more to write now, we need to register a handler */
688 if (sip
->txbuf
->bufused
> 0)
689 purple_ssl_input_add(gsc
, sipe_canwrite_cb_ssl
, gc
);
691 conn
= connection_create(sip
, gsc
->fd
);
692 purple_ssl_input_add(sip
->gsc
, sipe_input_cb_ssl
, gc
);
696 static void sendlater(PurpleConnection
*gc
, const char *buf
)
698 struct sipe_account_data
*sip
= gc
->proto_data
;
700 if (!sip
->connecting
) {
701 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
703 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
706 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
707 purple_connection_error(gc
, _("Couldn't create socket"));
710 sip
->connecting
= TRUE
;
713 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
714 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
716 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
719 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
721 struct sipe_account_data
*sip
= gc
->proto_data
;
722 time_t currtime
= time(NULL
);
723 int writelen
= strlen(buf
);
725 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
727 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
728 purple_debug_info("sipe", "could not send packet\n");
737 if (sip
->tx_handler
) {
742 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
744 ret
= write(sip
->fd
, buf
, writelen
);
748 if (ret
< 0 && errno
== EAGAIN
)
750 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
755 if (ret
< writelen
) {
756 if (!sip
->tx_handler
){
758 purple_ssl_input_add(sip
->gsc
, sipe_canwrite_cb_ssl
, gc
);
761 sip
->tx_handler
= purple_input_add(sip
->fd
,
762 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
767 /* XXX: is it OK to do this? You might get part of a request sent
768 with part of another. */
769 if (sip
->txbuf
->bufused
> 0)
770 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
772 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
778 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
780 sendout_pkt(gc
, buf
);
784 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
786 GSList
*tmp
= msg
->headers
;
789 GString
*outstr
= g_string_new("");
790 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
792 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
793 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
794 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
795 tmp
= g_slist_next(tmp
);
797 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
798 sendout_pkt(sip
->gc
, outstr
->str
);
799 g_string_free(outstr
, TRUE
);
802 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
805 if (sip
->registrar
.ntlm_key
) {
806 struct sipmsg_breakdown msgbd
;
808 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
809 // TODO generate this
810 msgbd
.rand
= g_strdup("0878F41B");
811 sip
->registrar
.ntlm_num
++;
812 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
813 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
814 if (signature_input_str
!= NULL
) {
815 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
816 msg
->rand
= g_strdup(msgbd
.rand
);
817 msg
->num
= g_strdup(msgbd
.num
);
819 sipmsg_breakdown_free(&msgbd
);
822 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
823 buf
= auth_header_without_newline(sip
, &sip
->registrar
, msg
, FALSE
);
824 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
825 sipmsg_add_header(msg
, "Authorization", buf
);
827 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
828 //sipmsg_add_header_pos(msg, "Authorization", buf, 5);
831 } else if (!strcmp(method
,"SUBSCRIBE") || !strcmp(method
,"SERVICE") || !strcmp(method
,"MESSAGE") || !strcmp(method
,"INVITE") || !strcmp(method
,"NOTIFY") || !strcmp(method
, "ACK") || !strcmp(method
, "BYE") || !strcmp(method
, "INFO")) {
833 sip
->registrar
.type
=2;
835 buf
= auth_header_without_newline(sip
, &sip
->registrar
, msg
, FALSE
);
836 //buf = auth_header(sip, &sip->proxy, msg, FALSE);
837 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
838 //sipmsg_add_header(msg, "Authorization", buf);
841 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
845 static char *get_contact(struct sipe_account_data
*sip
)
847 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");
852 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
853 const char *text
, const char *body
)
855 GSList
*tmp
= msg
->headers
;
858 GString
*outstr
= g_string_new("");
859 struct sipe_account_data
*sip
= gc
->proto_data
;
862 contact
= get_contact(sip
);
863 sipmsg_remove_header(msg
, "Contact");
864 sipmsg_add_header(msg
, "Contact", contact
);
867 /* When sending the acknowlegements and errors, the content length from the original
868 message is still here, but there is no body; we need to make sure we're sending the
869 correct content length */
870 sipmsg_remove_header(msg
, "Content-Length");
873 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
874 sipmsg_add_header(msg
, "Content-Length", len
);
876 sipmsg_add_header(msg
, "Content-Length", "0");
879 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
880 //gchar * mic = "MICTODO";
881 msg
->response
= code
;
883 sipmsg_remove_header(msg
, "Authentication-Info");
884 sign_outgoing_message(msg
, sip
, msg
->method
);
886 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
888 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
889 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
891 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
892 tmp
= g_slist_next(tmp
);
894 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
895 sendout_pkt(gc
, outstr
->str
);
896 g_string_free(outstr
, TRUE
);
899 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
901 if (trans
->msg
) sipmsg_free(trans
->msg
);
902 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
906 static struct transaction
*
907 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
909 struct transaction
*trans
= g_new0(struct transaction
, 1);
910 trans
->time
= time(NULL
);
911 trans
->msg
= (struct sipmsg
*)msg
;
912 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
913 trans
->callback
= callback
;
914 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
918 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
920 struct transaction
*trans
;
921 GSList
*transactions
= sip
->transactions
;
922 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
924 while (transactions
) {
925 trans
= transactions
->data
;
926 if (!strcmp(trans
->cseq
, cseq
)) {
929 transactions
= transactions
->next
;
936 static struct transaction
*
937 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
938 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
939 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
941 struct sipe_account_data
*sip
= gc
->proto_data
;
942 const char *addh
= "";
946 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : gentag();
947 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
948 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
949 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
950 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
951 gchar
*useragent
= purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
952 if (!strcmp(method
, "REGISTER")) {
953 if (sip
->regcallid
) {
955 callid
= g_strdup(sip
->regcallid
);
957 sip
->regcallid
= g_strdup(callid
);
961 if (addheaders
) addh
= addheaders
;
963 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
964 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
965 "From: <sip:%s>;tag=%s;epid=%s\r\n"
966 "To: <%s>%s%s%s%s\r\n"
967 "Max-Forwards: 70\r\n"
972 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
974 dialog
&& dialog
->request
? dialog
->request
: url
,
975 sip
->use_ssl
? "TLS" : sip
->udp
? "UDP" : "TCP",
976 purple_network_get_my_ip(-1),
978 branch
? ";branch=" : "",
979 branch
? branch
: "",
981 ourtag
? ourtag
: "",
982 getuuid(), // TODO generate one per account/login
984 theirtag
? ";tag=" : "",
985 theirtag
? theirtag
: "",
986 theirepid
? ";epid=" : "",
987 theirepid
? theirepid
: "",
988 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
992 dialog
&& dialog
->route
? dialog
->route
: "",
994 body
? strlen(body
) : 0,
998 //printf ("parsing msg buf:\n%s\n\n", buf);
999 msg
= sipmsg_parse_msg(buf
);
1008 sign_outgoing_message (msg
, sip
, method
);
1010 buf
= sipmsg_to_string (msg
);
1012 /* add to ongoing transactions */
1013 struct transaction
* trans
= transactions_add_buf(sip
, msg
, tc
);
1014 sendout_pkt(gc
, buf
);
1019 static char *get_contact_register(struct sipe_account_data
*sip
)
1021 return g_strdup_printf("<sip:%s:%d;transport=%s>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, BYE, CANCEL, NOTIFY, ACK, BENOTIFY\";proxy=replace; +sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip
->listenport
, sip
->use_ssl
? "tls" : sip
->udp
? "udp" : "tcp",generateUUIDfromEPID(getuuid()));
1024 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1026 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
1027 char *to
= g_strdup_printf("sip:%s", sip
->username
);
1028 char *contact
= get_contact_register(sip
);
1029 //char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire);
1030 // 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);
1031 //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);
1032 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
);
1035 sip
->registerstatus
= 1;
1038 sip
->reregister
= time(NULL
) + expire
- 50;
1040 sip
->reregister
= time(NULL
) + 600;
1043 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1044 process_register_response
);
1051 static void do_register(struct sipe_account_data
*sip
)
1053 do_register_exp(sip
, sip
->registerexpire
);
1056 static gchar
*parse_from(const gchar
*hdr
)
1059 const gchar
*tmp
, *tmp2
= hdr
;
1061 if (!hdr
) return NULL
;
1062 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1063 tmp
= strchr(hdr
, '<');
1065 /* i hate the different SIP UA behaviours... */
1066 if (tmp
) { /* sip address in <...> */
1068 tmp
= strchr(tmp2
, '>');
1070 from
= g_strndup(tmp2
, tmp
- tmp2
);
1072 purple_debug_info("sipe", "found < without > in From\n");
1076 tmp
= strchr(tmp2
, ';');
1078 from
= g_strndup(tmp2
, tmp
- tmp2
);
1080 from
= g_strdup(tmp2
);
1083 purple_debug_info("sipe", "got %s\n", from
);
1087 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1091 if (msg
->response
== 200 || msg
->response
== 202) {
1095 to
= parse_from(sipmsg_find_header(tc
->msg
, "To")); /* cant be NULL since it is our own msg */
1097 /* we can not subscribe -> user is offline (TODO unknown status?) */
1099 purple_prpl_got_user_status(sip
->account
, to
, "offline", NULL
);
1104 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
)
1106 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1107 gchar
*tmp
= get_contact(sip
);
1108 gchar
*contact
= g_strdup_printf(
1109 "Accept: application/pidf+xml, application/xpidf+xml\r\n"
1110 "Event: presence\r\n"
1111 "Contact: %s\r\n", tmp
);
1114 /* subscribe to buddy presence
1115 * we dont need to know the status so we do not need a callback */
1117 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, contact
, "", NULL
,
1118 process_subscribe_response
);
1124 static void sipe_subscribe(struct sipe_account_data
*sip
, struct sipe_buddy
*buddy
)
1126 sipe_subscribe_to_name(sip
, buddy
->name
);
1128 /* resubscribe before subscription expires */
1129 /* add some jitter */
1130 buddy
->resubscribe
= time(NULL
)+1140+(rand()%50);
1133 static gboolean
sipe_add_lcs_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1136 xmlnode
*item
, *group
, *isc
;
1137 const char *name_group
, *group_id
;
1139 PurpleGroup
*g
= NULL
;
1143 struct sipe_buddy
*bs
;
1144 struct sipe_group
*gr
;
1145 int len
= msg
->bodylen
;
1147 // Reserved to max 10 groups. TODO be dynamic
1148 gr
= g_new0(struct sipe_group
, 10);
1150 tmp
= sipmsg_find_header(msg
, "Event");
1151 if (tmp
&& !strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1152 purple_debug_info("sipe", "sipe_add_lcs_contacts->%s-%d\n", msg
->body
, len
);
1153 /*Convert the contact from XML to Purple Buddies*/
1154 isc
= xmlnode_from_str(msg
->body
, len
);
1156 /* TODO Find for all groups */
1157 for (group
= xmlnode_get_child(isc
, "group"); group
; group
= xmlnode_get_next_twin(group
)) {
1158 name_group
= xmlnode_get_attrib(group
, "name");
1159 group_id
= xmlnode_get_attrib(group
, "id");
1161 if (!strncmp(name_group
, "~", 1)){
1162 name_group
=g_strdup("General");
1165 gr
[ng
].name_group
= g_strdup(name_group
);
1166 gr
[ng
].id
= g_strdup(group_id
);
1167 purple_debug_info("sipe", "name_group->%s\n", name_group
);
1168 g
= purple_find_group(name_group
);
1171 g
= purple_group_new(name_group
);
1172 purple_blist_add_group(g
, NULL
);
1176 g
= purple_find_group("General");
1178 g
= purple_group_new("General");
1179 purple_blist_add_group(g
, NULL
);
1187 for (i
= 0; i
< ng
;i
++) {
1188 purple_debug_info("sipe", "id->%s\n", gr
[i
].id
);
1189 purple_debug_info("sipe", "id->%s\n", gr
[i
].name_group
);
1192 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1193 const char *uri
, *name
, *groups
;
1196 uri
= xmlnode_get_attrib(item
, "uri");
1197 name
= xmlnode_get_attrib(item
, "name");
1198 groups
= xmlnode_get_attrib(item
, "groups");
1199 parts
= g_strsplit(groups
, " ", 0);
1200 purple_debug_info("sipe", "URI->%s,Groups->%s\n", uri
, groups
);
1201 if (parts
[i
]!=NULL
){
1203 purple_debug_info("sipe", "Groups->parts[i] %s\n", parts
[i
]);
1204 if (!strcmp(gr
[i
].id
,parts
[i
])){
1205 purple_debug_info("sipe", "Found Groups->gr[i].id(%s),gr[i].name_group (%s)\n",gr
[i
].id
,gr
[i
].name_group
);
1207 buddy_name
= g_strdup_printf("sip:%s", uri
);
1209 //b = purple_find_buddy(sip->account, buddy_name);
1210 b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, gr
[i
].g
);
1212 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1216 //sipe_add_buddy(sip->gc, b , gr[i].g);
1217 purple_blist_add_buddy(b
, NULL
, gr
[i
].g
, NULL
);
1218 purple_blist_alias_buddy(b
, uri
);
1219 bs
= g_new0(struct sipe_buddy
, 1);
1220 bs
->name
= g_strdup(b
->name
);
1221 g_hash_table_insert(sip
->buddies
, bs
->name
, bs
);
1233 static void sipe_subscribe_buddylist(struct sipe_account_data
*sip
)
1235 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";
1236 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1237 gchar
*tmp
= get_contact(sip
);
1238 contact
= g_strdup_printf("%sContact: %s\r\n", contact
, tmp
);
1241 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, contact
, "", NULL
, sipe_add_lcs_contacts
);
1246 /* IM Session (INVITE and MESSAGE methods) */
1248 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
1250 if (sip
== NULL
|| who
== NULL
) {
1254 struct sip_im_session
*session
;
1255 GSList
*entry
= sip
->im_sessions
;
1257 session
= entry
->data
;
1258 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
1261 entry
= entry
->next
;
1266 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
1268 struct sip_im_session
*session
= find_im_session(sip
, who
);
1270 session
= g_new0(struct sip_im_session
, 1);
1271 session
->with
= g_strdup(who
);
1272 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
1277 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1279 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
1280 // TODO free session resources
1283 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
1289 if (strncmp("sip:", session
->with
, 4)) {
1290 fullto
= g_strdup_printf("sip:%s", session
->with
);
1292 fullto
= g_strdup(session
->with
);
1295 hdr
= g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1296 //hdr = g_strdup("Content-Type: text/rtf\r\n");
1297 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
1299 tmp
= get_contact(sip
);
1300 hdr
= g_strdup_printf("Contact: %s\r\n%s", tmp
, hdr
);
1303 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msg
, session
->outgoing_dialog
, NULL
);
1311 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
1313 GSList
*entry
= session
->outgoing_message_queue
;
1315 char *queued_msg
= entry
->data
;
1316 sipe_send_message(sip
, session
, queued_msg
);
1318 // Remove from the queue and free the string
1319 entry
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
1325 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1327 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
1328 struct sip_im_session
* session
= find_im_session(sip
, with
);
1332 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
1336 if (msg
->response
!= 200) {
1337 purple_debug_info("sipe", "process_invite_response: INVITE response not 200, ignoring\n");
1338 im_session_destroy(sip
, session
);
1342 struct sip_dialog
* dialog
= session
->outgoing_dialog
;
1344 purple_debug_info("sipe", "process_invite_response: session outgoign dialog is NULL\n");
1348 dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
1349 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "From"));
1350 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "To"));
1351 if (!dialog
->theirepid
) {
1352 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "To"), "epid=", ";", NULL
);
1354 if (!dialog
->theirepid
) {
1355 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "To"), "epid=", NULL
, NULL
);
1358 dialog
->request
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Record-Route"), "<", ">", NULL
);
1359 dialog
->route
= g_strdup_printf("Route: %s\r\n", sipmsg_find_header(msg
, "Contact"));
1362 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
1364 session
->outgoing_invite
= NULL
;
1366 sipe_im_process_queue(sip
, session
);
1372 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1379 if (strstr(session
->with
, "sip:")) {
1380 to
= g_strdup(session
->with
);
1382 to
= g_strdup_printf("sip:%s", session
->with
);
1385 // Setup the outgoing dialog w/ the epid from the incoming dialog (if any)
1386 struct sip_dialog
* dialog
= g_new0(struct sip_dialog
, 1);
1387 if (session
->incoming_dialog
) {
1388 printf("incoming dialog epid is %s\n", session
->incoming_dialog
->theirepid
);
1389 dialog
->theirepid
= session
->incoming_dialog
->theirepid
;
1391 printf("incoming dialog is NULL\n");
1393 session
->outgoing_dialog
= dialog
;
1395 contact
= get_contact(sip
);
1396 hdr
= g_strdup_printf(
1398 //"Supported: ms-conf-invite\r\n"
1399 //"Supported: ms-delayed-accept\r\n"
1400 //"Supported: ms-renders-isf\r\n"
1401 //"Supported: ms-renders-gif\r\n"
1402 //"Supported: ms-renders-mime-alternative\r\n"*/
1403 //"Supported: timer\r\n"
1404 //"Supported: ms-sender\r\n"
1405 //"Supported: ms-early-media\r\n"
1406 "Content-Type: application/sdp\r\n",
1407 contact
, sip
->username
, sip
->username
, to
);
1409 body
= g_strdup_printf(
1411 "o=- 0 0 IN IP4 %s\r\n"
1415 "m=message %d sip null\r\n"
1416 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1417 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), 5061);
1419 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
1420 to
, to
, hdr
, body
, session
->outgoing_dialog
, process_invite_response
);
1429 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1432 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->outgoing_dialog
, NULL
);
1433 im_session_destroy(sip
, session
);
1438 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
1440 struct sipe_account_data
*sip
= gc
->proto_data
;
1442 purple_debug_info("sipe", "conversation with %s closed\n", who
);
1443 im_session_close(sip
, find_im_session(sip
, who
));
1447 im_session_close_all (struct sipe_account_data
*sip
)
1449 GSList
*entry
= sip
->im_sessions
;
1451 im_session_close (sip
, entry
->data
);
1452 entry
= sip
->im_sessions
;
1456 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
1458 struct sipe_account_data
*sip
= gc
->proto_data
;
1459 char *to
= g_strdup(who
);
1460 char *text
= purple_unescape_html(what
);
1462 struct sip_im_session
* session
= find_or_create_im_session(sip
, who
);
1464 // Queue the message
1465 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, text
);
1467 if (session
->outgoing_dialog
&& session
->outgoing_dialog
->callid
) {
1468 sipe_im_process_queue(sip
, session
);
1469 } else if (!session
->outgoing_invite
) {
1470 // Need to send the INVITE to get the outgoing dialog setup
1471 sipe_invite(sip
, session
);
1479 /* End IM Session (INVITE and MESSAGE methods) */
1481 static void sipe_buddy_resub(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1483 time_t curtime
= time(NULL
);
1484 if (buddy
->resubscribe
< curtime
) {
1485 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_buddy_resub %s\n", name
);
1486 sipe_subscribe(sip
, buddy
);
1490 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
1492 GSList
*tmp
= sip
->transactions
;
1493 time_t currtime
= time(NULL
);
1495 struct transaction
*trans
= tmp
->data
;
1497 purple_debug_info("sipe", "have open transaction age: %d\n", currtime
- trans
->time
);
1498 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
1501 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
1503 sendout_sipmsg(sip
, trans
->msg
);
1510 static gboolean
subscribe_timeout(struct sipe_account_data
*sip
)
1513 time_t curtime
= time(NULL
);
1514 /* register again if first registration expires */
1515 if (sip
->reregister
< curtime
) {
1518 /* check for every subscription if we need to resubscribe */
1519 //Fixxxer we need resub?
1520 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_resub
, (gpointer
)sip
);
1522 /* remove a timed out suscriber */
1526 struct sipe_watcher
*watcher
= tmp
->data
;
1527 if (watcher
->expire
< curtime
) {
1528 watcher_remove(sip
, watcher
->name
);
1531 if (tmp
) tmp
= tmp
->next
;
1537 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1541 gboolean found
= FALSE
;
1543 from
= parse_from(sipmsg_find_header(msg
, "From"));
1547 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
1549 contenttype
= sipmsg_find_header(msg
, "Content-Type");
1550 if (!contenttype
|| !strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
1551 serv_got_im(sip
->gc
, from
, msg
->body
, 0, time(NULL
));
1552 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1555 if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
1556 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1561 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1565 state
= xmlnode_get_child(isc
, "state");
1568 purple_debug_info("sipe", "process_incoming_message: no state found\n");
1573 statedata
= xmlnode_get_data(state
);
1575 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
1576 else serv_got_typing_stopped(sip
->gc
, from
);
1581 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1585 purple_debug_info("sipe", "got unknown mime-type");
1586 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
1591 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1593 gchar
* from
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "<", ">", NULL
);
1594 struct sip_im_session
* session
= find_or_create_im_session (sip
, from
);
1596 struct sip_dialog
* dialog
= g_new0(struct sip_dialog
, 1);
1597 dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
1598 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
1599 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
1600 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
1601 printf("Created incoming dialog and set epid to %s\n", dialog
->theirepid
);
1603 session
->incoming_dialog
= dialog
;
1605 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
1609 send_sip_response(sip
->gc
, msg
, 200, "OK", g_strdup_printf(
1611 "o=- 0 0 IN IP4 %s\r\n"
1615 "m=message %d sip sip:%s\r\n"
1616 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1617 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
1618 //sip->realport, sip->username
1619 5061, sip
->username
));
1622 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1624 gchar
*tmp
, krb5_token
;
1625 const gchar
*expires_header
;
1628 expires_header
= sipmsg_find_header(msg
, "Expires");
1629 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
1630 purple_debug_info("sipe", "got response to REGISTER; expires = %d\n", expires
);
1632 switch (msg
->response
) {
1635 sip
->registerstatus
= 0;
1637 sip
->registerstatus
= 3;
1638 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
1640 /* tell everybody we're online */
1643 /* get buddies from blist; Has a bug */
1644 /*sipe_get_buddies(sip->gc);*/
1645 subscribe_timeout(sip
);
1647 //sipe_subscribe_to_name(sip, sip->username);
1649 tmp
= sipmsg_find_header(msg
, "Allow-Events");
1650 if (tmp
&& strstr(tmp
, "vnd-microsoft-provisioning")){
1651 sipe_subscribe_buddylist(sip
);
1654 // Should we remove the transaction here?
1655 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
1656 transactions_remove(sip
, tc
);
1660 if (sip
->registerstatus
!= 2) {
1661 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
1662 if (sip
->registrar
.retries
> 3) {
1663 sip
->gc
->wants_to_die
= TRUE
;
1664 purple_connection_error(sip
->gc
, _("Wrong Password"));
1667 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
1668 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
1670 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
1672 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
1673 fill_auth(sip
, tmp
, &sip
->registrar
);
1674 sip
->registerstatus
= 2;
1675 if (sip
->account
->disconnecting
) {
1676 do_register_exp(sip
, 0);
1686 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1692 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
1693 gboolean isonline
= FALSE
;
1695 fromhdr
= sipmsg_find_header(msg
, "From");
1696 from
= parse_from(fromhdr
);
1699 pidf
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1702 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
1706 purple_debug_info("sipe", "process_incoming_notify: body(%s)\n",msg
->body
);
1708 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
1709 if ((status
= xmlnode_get_child(tuple
, "status")))
1710 basicstatus
= xmlnode_get_child(status
, "basic");
1713 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
1718 tmp2
= xmlnode_get_data(basicstatus
);
1720 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n",tmp2
);
1724 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
1729 if (strstr(tmp2
, "open")) {
1735 if (isonline
) purple_prpl_got_user_status(sip
->account
, from
, "available", NULL
);
1736 else purple_prpl_got_user_status(sip
->account
, from
, "offline", NULL
);
1741 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1745 static gchar
* gen_xpidf(struct sipe_account_data
*sip
)
1747 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1749 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
1750 "<display name=\"sip:%s\"/>\r\n"
1751 "<atom id=\"1234\">\r\n"
1752 "<address uri=\"sip:%s\">\r\n"
1753 "<status status=\"%s\"/>\r\n"
1766 static gchar
* gen_pidf(struct sipe_account_data
*sip
)
1768 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1769 "<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"
1770 "<tuple id=\"0\">\r\n"
1772 "<basic>open</basic>\r\n"
1773 "<ep:activities>\r\n"
1774 " <ep:activity>%s</ep:activity>\r\n"
1778 "<ci:display-name>%s</ci:display-name>\r\n"
1786 static void send_notify(struct sipe_account_data
*sip
, struct sipe_watcher
*watcher
)
1788 gchar
*doc
= watcher
->needsxpidf
? gen_xpidf(sip
) : gen_pidf(sip
);
1789 gchar
*hdr
= watcher
->needsxpidf
? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
1790 send_sip_request(sip
->gc
, "NOTIFY", watcher
->name
, watcher
->name
, hdr
, doc
, &watcher
->dialog
, NULL
);
1794 static gboolean
process_service_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1796 if (msg
->response
!= 200 && msg
->response
!= 408) {
1797 /* never send again */
1798 sip
->republish
= -1;
1803 static void send_publish(struct sipe_account_data
*sip
)
1805 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
1806 gchar
*doc
= g_strdup_printf(
1807 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\"><publications uri=\"%s\"><publication categoryName=\"device\" instance=\"1617359818\" container=\"2\" version=\"0\" expireType=\"endpoint\"><device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"BB44F8D5-1540-547D-9ECE-6486D33DC804\"><capabilities preferred=\"false\" uri=\"%s\"><text capture=\"true\" render=\"true\" publish=\"false\"/><gifInk capture=\"false\" render=\"true\" publish=\"false\"/><isfInk capture=\"false\" render=\"true\" publish=\"false\"/></capabilities><timezone>%s</timezone><machineName>%s</machineName></device></publication><publication categoryName=\"state\" instance=\"906391356\" container=\"2\" version=\"0\" expireType=\"endpoint\"><state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\"><availability>3500</availability><endpointLocation></endpointLocation></state></publication><publication categoryName=\"state\" instance=\"906391356\" container=\"3\" version=\"0\" expireType=\"endpoint\"><state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\"><availability>3500</availability><endpointLocation></endpointLocation></state></publication></publications></publish>",
1809 "00:00:00-05:00", // TODO timezone
1810 "PC" // TODO machine name
1813 gchar
*tmp
= get_contact(sip
);
1814 gchar
*hdr
= g_strdup_printf("Contact: %s; +sip.instance=\"<urn:uuid:%s>\"\r\nContent-Type: application/msrtc-category-publish+xml\r\n", tmp
,generateUUIDfromEPID(getuuid()));
1817 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_service_response
);
1818 //sip->republish = time(NULL) + 500;
1825 static void send_service(struct sipe_account_data
*sip
)
1827 //gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->sipdomain);
1828 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
1829 //gchar *doc = gen_pidf(sip);
1831 gchar
*doc
= gen_pidf(sip
);
1832 gchar
*hdr
= g_strdup("Event: presence\r\nContent-Type: application/pidf+xml\r\n");
1834 //gchar *hdr = g_strdup("Content-Type: application/SOAP+xml\r\n");
1835 gchar
*tmp
= get_contact(sip
);
1836 hdr
= g_strdup_printf("Contact: %s\r\n%s; +sip.instance=\"<urn:uuid:%s>\"", tmp
, hdr
,generateUUIDfromEPID(getuuid()));
1838 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
,
1840 doc
, NULL
, process_service_response
);
1841 sip
->republish
= time(NULL
) + 500;
1847 static void process_incoming_subscribe(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1849 const char *from_hdr
= sipmsg_find_header(msg
, "From");
1850 gchar
*from
= parse_from(from_hdr
);
1851 gchar
*theirtag
= find_tag(from_hdr
);
1852 gchar
*ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
1853 gboolean tagadded
= FALSE
;
1854 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1855 gchar
*expire
= sipmsg_find_header(msg
, "Expire");
1856 // gchar *ms-received-port =find_received_port(sipmsg_find_header(msg, "Contact"));
1858 struct sipe_watcher
*watcher
= watcher_find(sip
, from
);
1863 if (!watcher
) { /* new subscription */
1864 gchar
*acceptheader
= sipmsg_find_header(msg
, "Accept");
1865 gboolean needsxpidf
= FALSE
;
1866 if (!purple_privacy_check(sip
->account
, from
)) {
1867 send_sip_response(sip
->gc
, msg
, 202, "Ok", NULL
);
1871 gchar
*tmp
= acceptheader
;
1872 gboolean foundpidf
= FALSE
;
1873 gboolean foundxpidf
= FALSE
;
1874 while (tmp
&& tmp
< acceptheader
+ strlen(acceptheader
)) {
1875 gchar
*tmp2
= strchr(tmp
, ',');
1876 if (tmp2
) *tmp2
= '\0';
1877 if (!strcmp("application/pidf+xml", tmp
))
1879 if (!strcmp("application/xpidf+xml", tmp
))
1884 while (*tmp
== ' ') tmp
++;
1888 if (!foundpidf
&& foundxpidf
) needsxpidf
= TRUE
;
1889 g_free(acceptheader
);
1891 watcher
= watcher_create(sip
, from
, callid
, ourtag
, theirtag
, needsxpidf
);
1894 gchar
*to
= g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg
, "To"), ourtag
);
1895 sipmsg_remove_header(msg
, "To");
1896 sipmsg_add_header(msg
, "To", to
);
1900 watcher
->expire
= time(NULL
) + strtol(expire
, NULL
, 10);
1902 watcher
->expire
= time(NULL
) + 600;
1904 sipmsg_remove_header(msg
, "Contact");
1905 tmp
= get_contact(sip
);
1906 sipmsg_add_header(msg
, "Contact", tmp
);
1908 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
);
1909 send_sip_response(sip
->gc
, msg
, 200, "Ok", NULL
);
1910 send_notify(sip
, watcher
);
1919 static void process_input_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1921 gboolean found
= FALSE
;
1922 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
1923 if (msg
->response
== 0) { /* request */
1924 if (!strcmp(msg
->method
, "MESSAGE")) {
1925 process_incoming_message(sip
, msg
);
1927 } else if (!strcmp(msg
->method
, "NOTIFY")) {
1928 purple_debug_info("sipe","send->process_incoming_notify\n");
1929 process_incoming_notify(sip
, msg
);
1931 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
1932 purple_debug_info("sipe","send->process_incoming_subscribe\n");
1933 process_incoming_subscribe(sip
, msg
);
1935 } else if (!strcmp(msg
->method
, "INVITE")) {
1936 process_incoming_invite(sip
, msg
);
1938 } else if (!strcmp(msg
->method
, "INFO")) {
1940 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
1942 serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
1944 printf("INFO body:\n%s\n", msg
->body
);
1945 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1947 } else if (!strcmp(msg
->method
, "ACK")) {
1948 // ACK's don't need any response
1949 //send_sip_response(sip->gc, msg, 200, "OK", NULL);
1951 } else if (!strcmp(msg
->method
, "BYE")) {
1952 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1954 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
1955 struct sip_im_session
* session
= find_im_session (sip
, from
);
1959 // TODO Let the user know the other user left the conversation?
1960 im_session_destroy(sip
, session
);
1965 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
1967 } else { /* response */
1968 struct transaction
*trans
= transactions_find(sip
, msg
);
1970 if (msg
->response
== 407) {
1971 gchar
*resend
, *auth
, *ptmp
;
1973 if (sip
->proxy
.retries
> 30) return;
1974 sip
->proxy
.retries
++;
1975 /* do proxy authentication */
1977 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
1979 fill_auth(sip
, ptmp
, &sip
->proxy
);
1980 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
, TRUE
);
1981 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
1982 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
1984 resend
= sipmsg_to_string(trans
->msg
);
1985 /* resend request */
1986 sendout_pkt(sip
->gc
, resend
);
1989 if (msg
->response
== 100) {
1990 /* ignore provisional response */
1991 purple_debug_info("sipe", "got trying response\n");
1993 sip
->proxy
.retries
= 0;
1994 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
1995 if (msg
->response
== 401) sip
->registrar
.retries
++;
1996 else sip
->registrar
.retries
= 0;
1997 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
1999 if (msg
->response
== 401) {
2000 gchar
*resend
, *auth
, *ptmp
;
2002 if (sip
->registrar
.retries
> 4) return;
2003 sip
->registrar
.retries
++;
2005 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
2006 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
2008 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
2011 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
2013 fill_auth(sip
, ptmp
, &sip
->registrar
);
2014 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
, TRUE
);
2015 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
2016 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
2018 //sipmsg_remove_header(trans->msg, "Authorization");
2019 //sipmsg_add_header(trans->msg, "Authorization", auth);
2021 resend
= sipmsg_to_string(trans
->msg
);
2022 /* resend request */
2023 sendout_pkt(sip
->gc
, resend
);
2027 if (trans
->callback
) {
2028 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
2029 /* call the callback to process response*/
2030 (trans
->callback
)(sip
, msg
, trans
);
2032 /* Not sure if this is needed or what needs to be done
2033 but transactions seem to be removed prematurely so
2034 this only removes them if the response is 200 OK */
2035 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
2036 /*Has a bug and it's unneccesary*/
2037 /*transactions_remove(sip, trans);*/
2042 /* This is done because in an OCS2007 server trace the MS
2043 * Communicator client seems to reset the CSeq after an OK */
2047 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction");
2051 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
2055 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
2063 /* according to the RFC remove CRLF at the beginning */
2064 while (*cur
== '\r' || *cur
== '\n') {
2067 if (cur
!= conn
->inbuf
) {
2068 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
2069 conn
->inbufused
= strlen(conn
->inbuf
);
2072 /* Received a full Header? */
2073 if ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
) {
2074 time_t currtime
= time(NULL
);
2077 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
2078 msg
= sipmsg_parse_header(conn
->inbuf
);
2081 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
2082 if (restlen
>= msg
->bodylen
) {
2083 dummy
= g_malloc(msg
->bodylen
+ 1);
2084 memcpy(dummy
, cur
, msg
->bodylen
);
2085 dummy
[msg
->bodylen
] = '\0';
2087 cur
+= msg
->bodylen
;
2088 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
2089 conn
->inbufused
= strlen(conn
->inbuf
);
2095 // Verify the signature before processing it
2096 if (sip
->registrar
.ntlm_key
) {
2097 struct sipmsg_breakdown msgbd
;
2099 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
2100 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
2102 if (signature_input_str
!= NULL
) {
2103 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
2106 gchar
* rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
2107 if (signature
!= NULL
&& rspauth
!= NULL
) {
2108 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
2109 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
2110 process_input_message(sip
, msg
);
2112 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
2116 sipmsg_breakdown_free(&msgbd
);
2118 process_input_message(sip
, msg
);
2122 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a incomplete sip msg: %s\n", conn
->inbuf
);
2126 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
2128 PurpleConnection
*gc
= data
;
2129 struct sipe_account_data
*sip
= gc
->proto_data
;
2134 static char buffer
[65536];
2135 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
2137 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
2138 msg
= sipmsg_parse_msg(buffer
);
2139 if (msg
) process_input_message(sip
, msg
);
2143 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
2145 PurpleConnection
*gc
= data
;
2146 struct sipe_account_data
*sip
= gc
->proto_data
;
2147 struct sip_connection
*conn
= NULL
;
2149 static char buf
[4096];
2151 /* TODO: It should be possible to make this check unnecessary */
2152 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
2153 if (gsc
) purple_ssl_close(gsc
);
2158 conn
= connection_find(sip
, sip
->gsc
->fd
);
2160 purple_debug_error("sipe", "Connection not found!\n");
2161 if (sip
->gsc
) purple_ssl_close(sip
->gsc
);
2166 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
2167 conn
->inbuflen
+= SIMPLE_BUF_INC
;
2168 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
2171 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
2173 if (len
< 0 && errno
== EAGAIN
) {
2174 /* Try again later */
2176 } else if (len
< 0) {
2177 purple_debug_info("sipe", "sipe_input_cb_ssl: read error\n");
2179 connection_remove(sip
, sip
->gsc
->fd
);
2180 if (sip
->fd
== gsc
->fd
) sip
->fd
= -1;
2183 } else if (len
== 0) {
2184 purple_connection_error(gc
, _("Server has disconnected"));
2186 connection_remove(sip
, sip
->gsc
->fd
);
2187 if (sip
->fd
== gsc
->fd
) sip
->fd
= -1;
2192 conn
->inbufused
+= len
;
2193 conn
->inbuf
[conn
->inbufused
] = '\0';
2195 process_input(sip
, conn
);
2199 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
2201 PurpleConnection
*gc
= data
;
2202 struct sipe_account_data
*sip
= gc
->proto_data
;
2204 struct sip_connection
*conn
= connection_find(sip
, source
);
2206 purple_debug_error("sipe", "Connection not found!\n");
2210 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
2211 conn
->inbuflen
+= SIMPLE_BUF_INC
;
2212 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
2215 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
2217 if (len
< 0 && errno
== EAGAIN
)
2219 else if (len
<= 0) {
2220 purple_debug_info("sipe", "sipe_input_cb: read error\n");
2221 connection_remove(sip
, source
);
2222 if (sip
->fd
== source
) sip
->fd
= -1;
2226 conn
->inbufused
+= len
;
2227 conn
->inbuf
[conn
->inbufused
] = '\0';
2229 process_input(sip
, conn
);
2232 /* Callback for new connections on incoming TCP port */
2233 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
2235 PurpleConnection
*gc
= data
;
2236 struct sipe_account_data
*sip
= gc
->proto_data
;
2237 struct sip_connection
*conn
;
2239 int newfd
= accept(source
, NULL
, NULL
);
2241 conn
= connection_create(sip
, newfd
);
2243 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2246 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
2248 PurpleConnection
*gc
= data
;
2249 struct sipe_account_data
*sip
;
2250 struct sip_connection
*conn
;
2252 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2260 purple_connection_error(gc
, _("Could not connect"));
2264 sip
= gc
->proto_data
;
2267 conn
= connection_create(sip
, source
);
2269 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2273 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2276 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
2278 PurpleConnection
*gc
= data
;
2279 struct sipe_account_data
*sip
;
2280 struct sip_connection
*conn
;
2282 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2284 if (gsc
) purple_ssl_close(gsc
);
2288 sip
= gc
->proto_data
;
2290 conn
= connection_create(sip
, sip
->fd
);
2291 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
2292 sip
->listenfd
= sip
->fd
;
2293 sip
->registertimeout
= purple_timeout_add((rand()%100)+3*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2297 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
2300 static guint
sipe_ht_hash_nick(const char *nick
)
2302 char *lc
= g_utf8_strdown(nick
, -1);
2303 guint bucket
= g_str_hash(lc
);
2309 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
2311 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
2314 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
2316 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2318 sip
->listen_data
= NULL
;
2320 if (listenfd
== -1) {
2321 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2327 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
2328 sip
->listenfd
= sip
->fd
;
2330 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
2332 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
2333 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2337 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
2339 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2342 sip
->query_data
= NULL
;
2344 if (!hosts
|| !hosts
->data
) {
2345 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
2349 addr_size
= GPOINTER_TO_INT(hosts
->data
);
2350 hosts
= g_slist_remove(hosts
, hosts
->data
);
2351 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
2352 g_free(hosts
->data
);
2353 hosts
= g_slist_remove(hosts
, hosts
->data
);
2355 hosts
= g_slist_remove(hosts
, hosts
->data
);
2356 g_free(hosts
->data
);
2357 hosts
= g_slist_remove(hosts
, hosts
->data
);
2360 /* create socket for incoming connections */
2361 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
2362 sipe_udp_host_resolved_listen_cb
, sip
);
2363 if (sip
->listen_data
== NULL
) {
2364 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2369 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
2372 PurpleConnection
*gc
= data
;
2373 struct sipe_account_data
*sip
;
2375 /* If the connection is already disconnected, we don't need to do anything else */
2376 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2379 sip
= gc
->proto_data
;
2383 case PURPLE_SSL_CONNECT_FAILED
:
2384 purple_connection_error(gc
, _("Connection Failed"));
2386 case PURPLE_SSL_HANDSHAKE_FAILED
:
2387 purple_connection_error(gc
, _("SSL Handshake Failed"));
2393 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
2395 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2396 PurpleProxyConnectData
*connect_data
;
2398 sip
->listen_data
= NULL
;
2400 sip
->listenfd
= listenfd
;
2401 if (sip
->listenfd
== -1) {
2402 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2406 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
2407 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2408 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
2409 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
2410 sipe_newconn_cb
, sip
->gc
);
2411 purple_debug_info("sipe", "connecting to %s port %d\n",
2412 sip
->realhostname
, sip
->realport
);
2413 /* open tcp connection to the server */
2414 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
2415 sip
->realport
, login_cb
, sip
->gc
);
2417 if (connect_data
== NULL
) {
2418 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
2424 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
2426 struct sipe_account_data
*sip
;
2431 sip
->srv_query_data
= NULL
;
2433 port
= purple_account_get_int(sip
->account
, "port", 0);
2435 /* find the host to connect to */
2437 hostname
= g_strdup(resp
->hostname
);
2438 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s\r\n", hostname
);
2443 if (!purple_account_get_bool(sip
->account
, "useproxy", FALSE
)) {
2444 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - using sipdomain\r\n");
2445 hostname
= g_strdup(sip
->sipdomain
);
2447 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - using specified SIP proxy\r\n");
2448 hostname
= g_strdup(purple_account_get_string(sip
->account
, "proxy", sip
->sipdomain
));
2452 sip
->realhostname
= hostname
;
2453 sip
->realport
= port
;
2454 if (!sip
->realport
) sip
->realport
= 5060;
2458 // /* create socket for incoming connections */
2459 // sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_STREAM,
2460 // sipe_tcp_connect_listen_cb, sip);
2461 // if (sip->listen_data == NULL) {
2462 // purple_connection_error(sip->gc, _("Could not create listen socket"));
2465 //} else { /* UDP */
2466 // purple_debug_info("sipe", "using udp with server %s and port %d\n", hostname, port);
2468 // sip->query_data = purple_dnsquery_a(hostname, port, sipe_udp_host_resolved, sip);
2469 // if (sip->query_data == NULL) {
2470 // purple_connection_error(sip->gc, _("Could not resolve hostname"));
2475 static void sipe_login(PurpleAccount
*account
)
2477 PurpleConnection
*gc
;
2478 struct sipe_account_data
*sip
;
2480 gchar
*hosttoconnect
;
2482 const char *username
= purple_account_get_username(account
);
2483 gc
= purple_account_get_connection(account
);
2485 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
2486 gc
->wants_to_die
= TRUE
;
2487 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
2491 if (!purple_account_get_bool(account
, "ssl", FALSE
)){
2492 if (!purple_ssl_is_supported())
2494 gc
->wants_to_die
= TRUE
;
2495 purple_connection_error(gc
,
2496 _("SSL support is needed for SSL/TLS support. Please install a supported "
2503 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
2505 sip
->account
= account
;
2506 sip
->registerexpire
= 900;
2507 sip
->udp
= purple_account_get_bool(account
, "udp", FALSE
);
2508 sip
->use_ssl
= purple_account_get_bool(account
, "ssl", FALSE
);
2510 purple_debug_info("sipe", "sip->use_ssl->%d\n", sip
->use_ssl
);
2512 /* TODO: is there a good default grow size? */
2514 sip
->txbuf
= purple_circ_buffer_new(0);
2516 userserver
= g_strsplit(username
, "@", 2);
2517 purple_connection_set_display_name(gc
, userserver
[0]);
2518 sip
->username
= g_strdup(g_strjoin("@", userserver
[0], userserver
[1], NULL
));
2519 sip
->sipdomain
= g_strdup(userserver
[1]);
2520 sip
->password
= g_strdup(purple_connection_get_password(gc
));
2521 g_strfreev(userserver
);
2524 // Communicator queries _sipinternaltls._tcp.domain.com and uses that
2525 // information to connect to the OCS server.
2527 // XXX FIXME: eventually we should also query for sipexternaltls as well
2528 // if Pidgin is not on the local LAN
2529 // This doesn't quite work as advertised yet so make sure your have
2530 // your OCS FQDN in the proxy setting in the SIPE account settings
2532 sip
->srv_query_data
= purple_srv_resolve("sipinternaltls", "tcp", sip
->sipdomain
, srvresolved
, sip
);
2535 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - realhostname: %s\r\n", sip
->realhostname
);
2537 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
2539 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
2541 /* TODO: Set the status correctly. */
2542 sip
->status
= g_strdup("available");
2544 if (!purple_account_get_bool(account
, "useproxy", FALSE
)) {
2545 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - checking realhostname again: %s\r\n", sip
->realhostname
);
2546 hosttoconnect
= g_strdup(sip
->sipdomain
);
2548 hosttoconnect
= g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
));
2552 purple_debug_info("sipe", "HosttoConnect->%s\n", hosttoconnect
);
2555 sip
->gsc
= purple_ssl_connect(account
,hosttoconnect
, purple_account_get_int(account
, "port", 5061), login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
2558 sip
->srv_query_data
= purple_srv_resolve("sip",
2559 sip
->udp
? "udp" : "tcp", hosttoconnect
, srvresolved
, sip
);
2562 g_free(hosttoconnect
);
2565 static void sipe_close(PurpleConnection
*gc
)
2567 struct sipe_account_data
*sip
= gc
->proto_data
;
2570 /* leave all conversations */
2571 im_session_close_all(sip
);
2574 do_register_exp(sip
, 0);
2575 connection_free_all(sip
);
2577 if (sip
->query_data
!= NULL
)
2578 purple_dnsquery_destroy(sip
->query_data
);
2580 if (sip
->srv_query_data
!= NULL
)
2581 purple_srv_cancel(sip
->srv_query_data
);
2583 if (sip
->listen_data
!= NULL
)
2584 purple_network_listen_cancel(sip
->listen_data
);
2586 g_free(sip
->sipdomain
);
2587 g_free(sip
->username
);
2588 g_free(sip
->password
);
2589 g_free(sip
->registrar
.nonce
);
2590 g_free(sip
->registrar
.opaque
);
2591 g_free(sip
->registrar
.target
);
2592 g_free(sip
->registrar
.realm
);
2593 g_free(sip
->registrar
.digest_session_key
);
2594 g_free(sip
->proxy
.nonce
);
2595 g_free(sip
->proxy
.opaque
);
2596 g_free(sip
->proxy
.target
);
2597 g_free(sip
->proxy
.realm
);
2598 g_free(sip
->proxy
.digest_session_key
);
2600 purple_circ_buffer_destroy(sip
->txbuf
);
2601 g_free(sip
->realhostname
);
2602 if (sip
->listenpa
) purple_input_remove(sip
->listenpa
);
2603 if (sip
->tx_handler
) purple_input_remove(sip
->tx_handler
);
2604 if (sip
->resendtimeout
) purple_timeout_remove(sip
->resendtimeout
);
2605 if (sip
->registertimeout
) purple_timeout_remove(sip
->registertimeout
);
2607 g_free(gc
->proto_data
);
2608 gc
->proto_data
= NULL
;
2611 /* not needed since privacy is checked for every subscribe */
2612 static void dummy_add_deny(PurpleConnection
*gc
, const char *name
) {
2615 static void dummy_permit_deny(PurpleConnection
*gc
)
2619 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
2625 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
2631 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
2635 static PurplePlugin
*my_protocol
= NULL
;
2637 static PurplePluginProtocolInfo prpl_info
=
2640 NULL
, /* user_splits */
2641 NULL
, /* protocol_options */
2642 NO_BUDDY_ICONS
, /* icon_spec */
2643 sipe_list_icon
, /* list_icon */
2644 NULL
, /* list_emblems */
2645 NULL
, /* status_text */
2646 NULL
, /* tooltip_text */
2647 sipe_status_types
, /* away_states */
2648 NULL
, /* blist_node_menu */
2649 NULL
, /* chat_info */
2650 NULL
, /* chat_info_defaults */
2651 sipe_login
, /* login */
2652 sipe_close
, /* close */
2653 sipe_im_send
, /* send_im */
2654 NULL
, /* set_info */
2655 // sipe_typing, /* send_typing */
2656 NULL
, /* send_typing */
2657 NULL
, /* get_info */
2658 sipe_set_status
, /* set_status */
2659 NULL
, /* set_idle */
2660 NULL
, /* change_passwd */
2661 sipe_add_buddy
, /* add_buddy */
2662 NULL
, /* add_buddies */
2663 sipe_remove_buddy
, /* remove_buddy */
2664 NULL
, /* remove_buddies */
2665 dummy_add_deny
, /* add_permit */
2666 dummy_add_deny
, /* add_deny */
2667 dummy_add_deny
, /* rem_permit */
2668 dummy_add_deny
, /* rem_deny */
2669 dummy_permit_deny
, /* set_permit_deny */
2670 NULL
, /* join_chat */
2671 NULL
, /* reject_chat */
2672 NULL
, /* get_chat_name */
2673 NULL
, /* chat_invite */
2674 NULL
, /* chat_leave */
2675 NULL
, /* chat_whisper */
2676 NULL
, /* chat_send */
2677 sipe_keep_alive
, /* keepalive */
2678 NULL
, /* register_user */
2679 NULL
, /* get_cb_info */
2680 NULL
, /* get_cb_away */
2681 NULL
, /* alias_buddy */
2682 NULL
, /* group_buddy */
2683 NULL
, /* rename_group */
2684 NULL
, /* buddy_free */
2685 sipe_convo_closed
, /* convo_closed */
2686 purple_normalize_nocase
, /* normalize */
2687 NULL
, /* set_buddy_icon */
2688 NULL
, /* remove_group */
2689 NULL
, /* get_cb_real_name */
2690 NULL
, /* set_chat_topic */
2691 NULL
, /* find_blist_chat */
2692 NULL
, /* roomlist_get_list */
2693 NULL
, /* roomlist_cancel */
2694 NULL
, /* roomlist_expand_category */
2695 NULL
, /* can_receive_file */
2696 NULL
, /* send_file */
2697 NULL
, /* new_xfer */
2698 NULL
, /* offline_message */
2699 NULL
, /* whiteboard_prpl_ops */
2700 sipe_send_raw
, /* send_raw */
2704 static PurplePluginInfo info
= {
2705 PURPLE_PLUGIN_MAGIC
,
2706 PURPLE_MAJOR_VERSION
,
2707 PURPLE_MINOR_VERSION
,
2708 PURPLE_PLUGIN_PROTOCOL
, /**< type */
2709 NULL
, /**< ui_requirement */
2711 NULL
, /**< dependencies */
2712 PURPLE_PRIORITY_DEFAULT
, /**< priority */
2713 "prpl-sipe", /**< id */
2714 "Microsoft LCS/OCS", /**< name */
2715 VERSION
, /**< version */
2716 N_("SIP/SIMPLE Exchange Protocol Plugin"), /** summary */
2717 N_("The SIP/SIMPLE Exchange Protocol Plugin"), /** description */
2718 "Anibal Avelar <avelar@gmail.com>", /**< author */
2719 PURPLE_WEBSITE
, /**< homepage */
2720 sipe_plugin_load
, /**< load */
2721 sipe_plugin_unload
, /**< unload */
2722 sipe_plugin_destroy
, /**< destroy */
2723 NULL
, /**< ui_info */
2724 &prpl_info
, /**< extra_info */
2733 static void init_plugin(PurplePlugin
*plugin
)
2735 PurpleAccountUserSplit
*split
;
2736 PurpleAccountOption
*option
;
2738 purple_plugin_register(plugin
);
2740 //split = purple_account_user_split_new(_("Server"), "", '@');
2741 //prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
2742 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
2743 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2744 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
2745 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2747 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
2748 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2750 option
= purple_account_option_bool_new(_("Use SSL/TLS"), "ssl", FALSE
);
2751 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
,option
);
2753 option
= purple_account_option_string_new(_("UserAgent"), "useragent", "Purple/" VERSION
);
2754 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
,option
);
2756 option
= purple_account_option_bool_new(_("Use UDP"), "udp", FALSE
);
2757 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2759 option
= purple_account_option_int_new(_("Connect port"), "port", 5060);
2760 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2762 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
2763 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2765 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
2766 option
= purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
2767 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2769 /*option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2770 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2771 option = purple_account_option_string_new(_("Proxy"), "proxy", "");
2772 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2773 option
= purple_account_option_string_new(_("Auth User"), "authuser", "");
2774 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2775 option
= purple_account_option_string_new(_("Auth Domain"), "authdomain", "");
2776 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2777 my_protocol
= plugin
;
2781 /* I had to redefined the function for it load, but works */
2782 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
2783 plugin
->info
= &(info
);
2784 init_plugin((plugin
));
2785 sipe_plugin_load((plugin
));
2786 return purple_plugin_register(plugin
);