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
77 static char *genbranch()
79 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
80 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
81 rand() & 0xFFFF, rand() & 0xFFFF);
84 static char *gencallid()
86 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
87 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
88 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
89 rand() & 0xFFFF, rand() & 0xFFFF);
92 static gchar
*find_tag(const gchar
*hdr
)
94 gchar
* tag
= sipmsg_find_part_of_header (hdr
, "tag=", ";", NULL
);
96 // In case it's at the end and there's no trailing ;
97 tag
= sipmsg_find_part_of_header (hdr
, "tag=", NULL
, NULL
);
103 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
108 static void sipe_keep_alive(PurpleConnection
*gc
)
110 struct sipe_account_data
*sip
= gc
->proto_data
;
112 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
113 gchar buf
[2] = {0, 0};
114 purple_debug_info("sipe", "sending keep alive\n");
115 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
119 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
121 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
122 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
125 static void send_notify(struct sipe_account_data
*sip
, struct sipe_watcher
*);
127 static void send_service(struct sipe_account_data
*sip
);
128 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
);
129 static void send_publish(struct sipe_account_data
*sip
);
131 static void do_notifies(struct sipe_account_data
*sip
)
133 GSList
*tmp
= sip
->watcher
;
134 purple_debug_info("sipe", "do_notifies()\n");
137 purple_debug_info("sipe", "notifying %s\n", ((struct sipe_watcher
*)tmp
->data
)->name
);
138 send_notify(sip
, tmp
->data
);
143 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
145 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(purple_status_get_type(status
));
146 struct sipe_account_data
*sip
= NULL
;
148 if (!purple_status_is_active(status
))
152 sip
= account
->gc
->proto_data
;
157 if (primitive
== PURPLE_STATUS_AVAILABLE
)
158 sip
->status
= g_strdup("available");
160 sip
->status
= g_strdup("busy");
166 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
168 struct sip_connection
*ret
= NULL
;
169 GSList
*entry
= sip
->openconns
;
172 if (ret
->fd
== fd
) return ret
;
178 static struct sipe_watcher
*watcher_find(struct sipe_account_data
*sip
,
181 struct sipe_watcher
*watcher
;
182 GSList
*entry
= sip
->watcher
;
184 watcher
= entry
->data
;
185 if (!strcmp(name
, watcher
->name
)) return watcher
;
191 static struct sipe_watcher
*watcher_create(struct sipe_account_data
*sip
,
192 const gchar
*name
, const gchar
*callid
, const gchar
*ourtag
,
193 const gchar
*theirtag
, gboolean needsxpidf
)
195 struct sipe_watcher
*watcher
= g_new0(struct sipe_watcher
, 1);
196 watcher
->name
= g_strdup(name
);
197 watcher
->dialog
.callid
= g_strdup(callid
);
198 watcher
->dialog
.ourtag
= g_strdup(ourtag
);
199 watcher
->dialog
.theirtag
= g_strdup(theirtag
);
200 watcher
->needsxpidf
= needsxpidf
;
201 sip
->watcher
= g_slist_append(sip
->watcher
, watcher
);
205 static void watcher_remove(struct sipe_account_data
*sip
, const gchar
*name
)
207 struct sipe_watcher
*watcher
= watcher_find(sip
, name
);
208 sip
->watcher
= g_slist_remove(sip
->watcher
, watcher
);
209 g_free(watcher
->name
);
210 g_free(watcher
->dialog
.callid
);
211 g_free(watcher
->dialog
.ourtag
);
212 g_free(watcher
->dialog
.theirtag
);
216 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
218 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
220 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
224 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
226 struct sip_connection
*conn
= connection_find(sip
, fd
);
227 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
228 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
233 static void connection_free_all(struct sipe_account_data
*sip
)
235 struct sip_connection
*ret
= NULL
;
236 GSList
*entry
= sip
->openconns
;
239 connection_remove(sip
, ret
->fd
);
240 entry
= sip
->openconns
;
244 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
246 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
247 struct sipe_buddy
*b
;
248 if (strncmp("sip:", buddy
->name
, 4)) {
249 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
250 purple_blist_rename_buddy(buddy
, buf
);
253 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
254 b
= g_new0(struct sipe_buddy
, 1);
255 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
256 b
->name
= g_strdup(buddy
->name
);
257 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
259 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
263 static void sipe_get_buddies(PurpleConnection
*gc
)
265 PurpleBlistNode
*gnode
, *cnode
, *bnode
;
267 purple_debug_info("sipe", "sipe_get_buddies\n");
269 for (gnode
= purple_get_blist()->root
; gnode
; gnode
= gnode
->next
) {
270 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode
)) continue;
271 for (cnode
= gnode
->child
; cnode
; cnode
= cnode
->next
) {
272 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode
)) continue;
273 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
274 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode
)) continue;
275 if (((PurpleBuddy
*)bnode
)->account
== gc
->account
)
276 sipe_add_buddy(gc
, (PurpleBuddy
*)bnode
, (PurpleGroup
*)gnode
);
282 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
284 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
285 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
286 g_hash_table_remove(sip
->buddies
, buddy
->name
);
291 static GList
*sipe_status_types(PurpleAccount
*acc
)
293 PurpleStatusType
*type
;
296 type
= purple_status_type_new_with_attrs(
297 PURPLE_STATUS_AVAILABLE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
298 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
300 types
= g_list_append(types
, type
);
302 type
= purple_status_type_new_full(
303 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
304 types
= g_list_append(types
, type
);
309 //static struct sipe_krb5_auth krb5_auth;
310 static gchar
*auth_header_without_newline(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
, gboolean force_reauth
)
312 const gchar
*method
= msg
->method
;
313 const gchar
*target
= msg
->target
;
318 const char *authdomain
;
319 const char *authuser
;
320 const char *krb5_realm
;
322 gchar
*krb5_token
= NULL
;
324 authdomain
= purple_account_get_string(sip
->account
, "authdomain", "");
325 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
327 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
328 // and do error checking
330 // KRB realm should always be uppercase
331 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
333 if (sip
->realhostname
) {
334 host
= sip
->realhostname
;
335 } else if (purple_account_get_bool(sip
->account
, "use_proxy", TRUE
)) {
336 host
= purple_account_get_string(sip
->account
, "proxy", "");
338 host
= sip
->sipdomain
;
341 /*gboolean new_auth = krb5_auth.gss_context == NULL;
343 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
346 if (new_auth || force_reauth) {
347 krb5_token = krb5_auth.base64_token;
350 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
351 krb5_token = krb5_auth.base64_token;*/
353 if (!authuser
|| strlen(authuser
) < 1) {
354 authuser
= sip
->username
;
357 if (auth
->type
== 1) { /* Digest */
358 sprintf(noncecount
, "%08d", auth
->nc
++);
359 response
= purple_cipher_http_digest_calculate_response(
360 "md5", method
, target
, NULL
, NULL
,
361 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
362 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
364 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
);
367 } else if (auth
->type
== 2) { /* NTLM */
368 // If we have a signature for the message, include that
369 if (msg
->signature
) {
370 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
);
374 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
375 /* TODO: Don't hardcode "purple" as the hostname */
376 const gchar
* ntlm_key
;
377 gchar
* gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, "purple", authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
378 auth
->ntlm_key
= (gchar
*)ntlm_key
;
379 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
384 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
386 } else if (auth
->type
== 3) {
389 /*if (new_auth || force_reauth) {
390 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
392 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);
394 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
397 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
398 gchar * mic = "MICTODO";
399 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
400 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
401 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
402 //auth->opaque ? auth->opaque : "", auth->target);
403 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
408 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
411 sprintf(noncecount
, "%08d", auth
->nc
++);
412 response
= purple_cipher_http_digest_calculate_response(
413 "md5", method
, target
, NULL
, NULL
,
414 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
415 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
417 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
);
422 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
, gboolean force_reauth
)
424 gchar
*with
, *without
;
426 without
= auth_header_without_newline(sip
, auth
, msg
, force_reauth
);
427 with
= g_strdup_printf("%s\r\n", without
);
433 static char *parse_attribute(const char *attrname
, const char *source
)
435 const char *tmp
, *tmp2
;
437 int len
= strlen(attrname
);
439 if (!strncmp(source
, attrname
, len
)) {
441 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
443 retval
= g_strndup(tmp
, tmp2
- tmp
);
445 retval
= g_strdup(tmp
);
451 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
454 const char *authuser
;
457 const char *krb5_realm
;
460 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
461 // and do error checking
463 // KRB realm should always be uppercase
464 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
466 if (sip->realhostname) {
467 host = sip->realhostname;
468 } else if (purple_account_get_bool(sip->account, "use_proxy", TRUE)) {
469 host = purple_account_get_string(sip->account, "proxy", "");
471 host = sip->sipdomain;
474 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
476 if (!authuser
|| strlen(authuser
) < 1) {
477 authuser
= sip
->username
;
481 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
485 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
487 parts
= g_strsplit(hdr
+5, "\", ", 0);
490 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
491 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
492 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
495 if ((tmp
= parse_attribute("targetname=\"",
499 else if ((tmp
= parse_attribute("realm=\"",
503 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
510 if (!strstr(hdr
, "gssapi-data")) {
518 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
519 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
521 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
522 parts
= g_strsplit(hdr
+9, "\", ", 0);
525 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
526 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
527 /*if (krb5_auth.gss_context == NULL) {
528 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
530 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
533 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
535 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
537 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
544 //if (!strstr(hdr, "gssapi-data")) {
553 parts
= g_strsplit(hdr
, " ", 0);
555 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
558 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
565 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
567 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
568 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
574 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
576 PurpleConnection
*gc
= data
;
577 struct sipe_account_data
*sip
= gc
->proto_data
;
581 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
583 if (max_write
== 0) {
584 purple_input_remove(sip
->tx_handler
);
589 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
591 if (written
< 0 && errno
== EAGAIN
)
593 else if (written
<= 0) {
594 /*TODO: do we really want to disconnect on a failure to write?*/
595 purple_connection_error(gc
, _("Could not write"));
599 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
602 static void sipe_canwrite_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
604 PurpleConnection
*gc
= data
;
605 struct sipe_account_data
*sip
= gc
->proto_data
;
609 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
611 if (max_write
== 0) {
612 purple_input_remove(sip
->tx_handler
);
617 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
619 if (written
< 0 && errno
== EAGAIN
)
621 else if (written
<= 0) {
622 /*TODO: do we really want to disconnect on a failure to write?*/
623 purple_connection_error(gc
, _("Could not write"));
627 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
630 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
632 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
634 PurpleConnection
*gc
= data
;
635 struct sipe_account_data
*sip
;
636 struct sip_connection
*conn
;
638 if (!PURPLE_CONNECTION_IS_VALID(gc
))
646 purple_connection_error(gc
, _("Could not connect"));
650 sip
= gc
->proto_data
;
652 sip
->connecting
= FALSE
;
654 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
656 /* If there is more to write now, we need to register a handler */
657 if (sip
->txbuf
->bufused
> 0)
658 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
,
659 sipe_canwrite_cb
, gc
);
661 conn
= connection_create(sip
, source
);
662 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
665 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
667 PurpleConnection
*gc
= data
;
668 struct sipe_account_data
*sip
;
669 struct sip_connection
*conn
;
671 if (!PURPLE_CONNECTION_IS_VALID(gc
))
673 if(gsc
) purple_ssl_close(gsc
);
677 sip
= gc
->proto_data
;
679 sip
->connecting
= FALSE
;
681 sipe_canwrite_cb_ssl(gc
, gsc
, PURPLE_INPUT_WRITE
);
683 /* If there is more to write now, we need to register a handler */
684 if (sip
->txbuf
->bufused
> 0)
685 purple_ssl_input_add(gsc
, sipe_canwrite_cb_ssl
, gc
);
687 conn
= connection_create(sip
, gsc
->fd
);
688 purple_ssl_input_add(sip
->gsc
, sipe_input_cb_ssl
, gc
);
692 static void sendlater(PurpleConnection
*gc
, const char *buf
)
694 struct sipe_account_data
*sip
= gc
->proto_data
;
696 if (!sip
->connecting
) {
697 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
699 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
702 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
703 purple_connection_error(gc
, _("Couldn't create socket"));
706 sip
->connecting
= TRUE
;
709 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
710 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
712 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
715 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
717 struct sipe_account_data
*sip
= gc
->proto_data
;
718 time_t currtime
= time(NULL
);
719 int writelen
= strlen(buf
);
721 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
723 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
724 purple_debug_info("sipe", "could not send packet\n");
733 if (sip
->tx_handler
) {
738 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
740 ret
= write(sip
->fd
, buf
, writelen
);
744 if (ret
< 0 && errno
== EAGAIN
)
746 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
751 if (ret
< writelen
) {
752 if (!sip
->tx_handler
){
754 purple_ssl_input_add(sip
->gsc
, sipe_canwrite_cb_ssl
, gc
);
757 sip
->tx_handler
= purple_input_add(sip
->fd
,
758 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
763 /* XXX: is it OK to do this? You might get part of a request sent
764 with part of another. */
765 if (sip
->txbuf
->bufused
> 0)
766 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
768 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
774 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
776 sendout_pkt(gc
, buf
);
780 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
782 GSList
*tmp
= msg
->headers
;
785 GString
*outstr
= g_string_new("");
786 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
788 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
789 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
790 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
791 tmp
= g_slist_next(tmp
);
793 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
794 sendout_pkt(sip
->gc
, outstr
->str
);
795 g_string_free(outstr
, TRUE
);
798 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
801 if (sip
->registrar
.ntlm_key
) {
802 struct sipmsg_breakdown msgbd
;
804 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
805 // TODO generate this
806 msgbd
.rand
= g_strdup("0878F41B");
807 sip
->registrar
.ntlm_num
++;
808 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
809 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
810 if (signature_input_str
!= NULL
) {
811 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
812 msg
->rand
= g_strdup(msgbd
.rand
);
813 msg
->num
= g_strdup(msgbd
.num
);
815 sipmsg_breakdown_free(&msgbd
);
818 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
819 buf
= auth_header_without_newline(sip
, &sip
->registrar
, msg
, FALSE
);
820 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
821 sipmsg_add_header(msg
, "Authorization", buf
);
823 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
824 //sipmsg_add_header_pos(msg, "Authorization", buf, 5);
827 } 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")) {
829 sip
->registrar
.type
=2;
831 buf
= auth_header_without_newline(sip
, &sip
->registrar
, msg
, FALSE
);
832 //buf = auth_header(sip, &sip->proxy, msg, FALSE);
833 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
834 //sipmsg_add_header(msg, "Authorization", buf);
837 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
841 static char *get_contact(struct sipe_account_data
*sip
)
843 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");
848 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
849 const char *text
, const char *body
)
851 GSList
*tmp
= msg
->headers
;
854 GString
*outstr
= g_string_new("");
855 struct sipe_account_data
*sip
= gc
->proto_data
;
858 contact
= get_contact(sip
);
859 sipmsg_remove_header(msg
, "Contact");
860 sipmsg_add_header(msg
, "Contact", contact
);
863 /* When sending the acknowlegements and errors, the content length from the original
864 message is still here, but there is no body; we need to make sure we're sending the
865 correct content length */
866 sipmsg_remove_header(msg
, "Content-Length");
869 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
870 sipmsg_add_header(msg
, "Content-Length", len
);
872 sipmsg_add_header(msg
, "Content-Length", "0");
875 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
876 //gchar * mic = "MICTODO";
877 msg
->response
= code
;
879 sipmsg_remove_header(msg
, "Authentication-Info");
880 sign_outgoing_message(msg
, sip
, msg
->method
);
882 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
884 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
885 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
887 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
888 tmp
= g_slist_next(tmp
);
890 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
891 sendout_pkt(gc
, outstr
->str
);
892 g_string_free(outstr
, TRUE
);
895 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
897 if (trans
->msg
) sipmsg_free(trans
->msg
);
898 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
902 static struct transaction
*
903 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
905 struct transaction
*trans
= g_new0(struct transaction
, 1);
906 trans
->time
= time(NULL
);
907 trans
->msg
= (struct sipmsg
*)msg
;
908 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
909 trans
->callback
= callback
;
910 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
914 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
916 struct transaction
*trans
;
917 GSList
*transactions
= sip
->transactions
;
918 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
920 while (transactions
) {
921 trans
= transactions
->data
;
922 if (!strcmp(trans
->cseq
, cseq
)) {
925 transactions
= transactions
->next
;
932 static struct transaction
*
933 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
934 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
935 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
937 struct sipe_account_data
*sip
= gc
->proto_data
;
938 const char *addh
= "";
942 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : gentag();
943 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
944 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
945 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
946 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
947 gchar
*useragent
= purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
948 if (!strcmp(method
, "REGISTER")) {
949 if (sip
->regcallid
) {
951 callid
= g_strdup(sip
->regcallid
);
953 sip
->regcallid
= g_strdup(callid
);
957 if (addheaders
) addh
= addheaders
;
959 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
960 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
961 "From: <sip:%s>;tag=%s;epid=%s\r\n"
962 "To: <%s>%s%s%s%s\r\n"
963 "Max-Forwards: 70\r\n"
968 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
970 dialog
&& dialog
->request
? dialog
->request
: url
,
971 sip
->use_ssl
? "TLS" : sip
->udp
? "UDP" : "TCP",
972 purple_network_get_my_ip(-1),
974 branch
? ";branch=" : "",
975 branch
? branch
: "",
977 ourtag
? ourtag
: "",
978 getuuid(), // TODO generate one per account/login
980 theirtag
? ";tag=" : "",
981 theirtag
? theirtag
: "",
982 theirepid
? ";epid=" : "",
983 theirepid
? theirepid
: "",
984 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
988 dialog
&& dialog
->route
? dialog
->route
: "",
990 body
? strlen(body
) : 0,
994 //printf ("parsing msg buf:\n%s\n\n", buf);
995 msg
= sipmsg_parse_msg(buf
);
1004 sign_outgoing_message (msg
, sip
, method
);
1006 buf
= sipmsg_to_string (msg
);
1008 /* add to ongoing transactions */
1009 struct transaction
* trans
= transactions_add_buf(sip
, msg
, tc
);
1010 sendout_pkt(gc
, buf
);
1015 static char *get_contact_register(struct sipe_account_data
*sip
)
1017 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()));
1020 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1022 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
1023 char *to
= g_strdup_printf("sip:%s", sip
->username
);
1024 char *contact
= get_contact_register(sip
);
1025 //char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire);
1026 // 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);
1027 //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);
1028 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 sip
->registerstatus
= 1;
1034 sip
->reregister
= time(NULL
) + expire
- 50;
1036 sip
->reregister
= time(NULL
) + 600;
1039 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1040 process_register_response
);
1047 static void do_register(struct sipe_account_data
*sip
)
1049 do_register_exp(sip
, sip
->registerexpire
);
1052 static gchar
*parse_from(const gchar
*hdr
)
1055 const gchar
*tmp
, *tmp2
= hdr
;
1057 if (!hdr
) return NULL
;
1058 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1059 tmp
= strchr(hdr
, '<');
1061 /* i hate the different SIP UA behaviours... */
1062 if (tmp
) { /* sip address in <...> */
1064 tmp
= strchr(tmp2
, '>');
1066 from
= g_strndup(tmp2
, tmp
- tmp2
);
1068 purple_debug_info("sipe", "found < without > in From\n");
1072 tmp
= strchr(tmp2
, ';');
1074 from
= g_strndup(tmp2
, tmp
- tmp2
);
1076 from
= g_strdup(tmp2
);
1079 purple_debug_info("sipe", "got %s\n", from
);
1083 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1087 if (msg
->response
== 200 || msg
->response
== 202) {
1091 to
= parse_from(sipmsg_find_header(tc
->msg
, "To")); /* cant be NULL since it is our own msg */
1093 /* we can not subscribe -> user is offline (TODO unknown status?) */
1095 purple_prpl_got_user_status(sip
->account
, to
, "offline", NULL
);
1100 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
)
1102 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1103 gchar
*tmp
= get_contact(sip
);
1104 gchar
*contact
= g_strdup_printf(
1105 "Accept: application/pidf+xml, application/xpidf+xml\r\n"
1106 "Event: presence\r\n"
1107 "Contact: %s\r\n", tmp
);
1110 /* subscribe to buddy presence
1111 * we dont need to know the status so we do not need a callback */
1113 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, contact
, "", NULL
,
1114 process_subscribe_response
);
1120 static void sipe_subscribe(struct sipe_account_data
*sip
, struct sipe_buddy
*buddy
)
1122 sipe_subscribe_to_name(sip
, buddy
->name
);
1124 /* resubscribe before subscription expires */
1125 /* add some jitter */
1126 buddy
->resubscribe
= time(NULL
)+1140+(rand()%50);
1129 static gboolean
sipe_add_lcs_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1132 xmlnode
*item
, *group
, *isc
;
1133 const char *name_group
, *group_id
;
1135 PurpleGroup
*g
= NULL
;
1139 struct sipe_buddy
*bs
;
1140 struct sipe_group
*gr
;
1141 int len
= msg
->bodylen
;
1143 // Reserved to max 10 groups. TODO be dynamic
1144 gr
= g_new0(struct sipe_group
, 10);
1146 tmp
= sipmsg_find_header(msg
, "Event");
1147 if (tmp
&& !strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1148 purple_debug_info("sipe", "sipe_add_lcs_contacts->%s-%d\n", msg
->body
, len
);
1149 /*Convert the contact from XML to Purple Buddies*/
1150 isc
= xmlnode_from_str(msg
->body
, len
);
1152 /* TODO Find for all groups */
1153 for (group
= xmlnode_get_child(isc
, "group"); group
; group
= xmlnode_get_next_twin(group
)) {
1154 name_group
= xmlnode_get_attrib(group
, "name");
1155 group_id
= xmlnode_get_attrib(group
, "id");
1157 if (!strncmp(name_group
, "~", 1)){
1158 name_group
=g_strdup("General");
1161 gr
[ng
].name_group
= g_strdup(name_group
);
1162 gr
[ng
].id
= g_strdup(group_id
);
1163 purple_debug_info("sipe", "name_group->%s\n", name_group
);
1164 g
= purple_find_group(name_group
);
1167 g
= purple_group_new(name_group
);
1168 purple_blist_add_group(g
, NULL
);
1172 g
= purple_find_group("General");
1174 g
= purple_group_new("General");
1175 purple_blist_add_group(g
, NULL
);
1183 for (i
= 0; i
< ng
;i
++) {
1184 purple_debug_info("sipe", "id->%s\n", gr
[i
].id
);
1185 purple_debug_info("sipe", "id->%s\n", gr
[i
].name_group
);
1188 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1189 const char *uri
, *name
, *groups
;
1192 uri
= xmlnode_get_attrib(item
, "uri");
1193 name
= xmlnode_get_attrib(item
, "name");
1194 groups
= xmlnode_get_attrib(item
, "groups");
1195 parts
= g_strsplit(groups
, " ", 0);
1196 purple_debug_info("sipe", "URI->%s,Groups->%s\n", uri
, groups
);
1197 if (parts
[i
]!=NULL
){
1199 purple_debug_info("sipe", "Groups->parts[i] %s\n", parts
[i
]);
1200 if (!strcmp(gr
[i
].id
,parts
[i
])){
1201 purple_debug_info("sipe", "Found Groups->gr[i].id(%s),gr[i].name_group (%s)\n",gr
[i
].id
,gr
[i
].name_group
);
1203 buddy_name
= g_strdup_printf("sip:%s", uri
);
1205 //b = purple_find_buddy(sip->account, buddy_name);
1206 b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, gr
[i
].g
);
1208 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1212 //sipe_add_buddy(sip->gc, b , gr[i].g);
1213 purple_blist_add_buddy(b
, NULL
, gr
[i
].g
, NULL
);
1214 purple_blist_alias_buddy(b
, uri
);
1215 bs
= g_new0(struct sipe_buddy
, 1);
1216 bs
->name
= g_strdup(b
->name
);
1217 g_hash_table_insert(sip
->buddies
, bs
->name
, bs
);
1229 static void sipe_subscribe_buddylist(struct sipe_account_data
*sip
)
1231 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";
1232 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1233 gchar
*tmp
= get_contact(sip
);
1234 contact
= g_strdup_printf("%sContact: %s\r\n", contact
, tmp
);
1237 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, contact
, "", NULL
, sipe_add_lcs_contacts
);
1242 /* IM Session (INVITE and MESSAGE methods) */
1244 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
1246 if (sip
== NULL
|| who
== NULL
) {
1250 struct sip_im_session
*session
;
1251 GSList
*entry
= sip
->im_sessions
;
1253 session
= entry
->data
;
1254 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
1257 entry
= entry
->next
;
1262 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
1264 struct sip_im_session
*session
= find_im_session(sip
, who
);
1266 session
= g_new0(struct sip_im_session
, 1);
1267 session
->with
= g_strdup(who
);
1268 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
1273 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1275 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
1276 // TODO free session resources
1279 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
1285 if (strncmp("sip:", session
->with
, 4)) {
1286 fullto
= g_strdup_printf("sip:%s", session
->with
);
1288 fullto
= g_strdup(session
->with
);
1291 hdr
= g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1292 //hdr = g_strdup("Content-Type: text/rtf\r\n");
1293 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
1295 tmp
= get_contact(sip
);
1296 hdr
= g_strdup_printf("Contact: %s\r\n%s", tmp
, hdr
);
1299 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msg
, session
->outgoing_dialog
, NULL
);
1307 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
1309 GSList
*entry
= session
->outgoing_message_queue
;
1311 char *queued_msg
= entry
->data
;
1312 sipe_send_message(sip
, session
, queued_msg
);
1314 // Remove from the queue and free the string
1315 entry
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
1321 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1323 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
1324 struct sip_im_session
* session
= find_im_session(sip
, with
);
1328 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
1332 if (msg
->response
!= 200) {
1333 purple_debug_info("sipe", "process_invite_response: INVITE response not 200, ignoring\n");
1334 im_session_destroy(sip
, session
);
1338 struct sip_dialog
* dialog
= session
->outgoing_dialog
;
1340 purple_debug_info("sipe", "process_invite_response: session outgoign dialog is NULL\n");
1344 dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
1345 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "From"));
1346 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "To"));
1347 if (!dialog
->theirepid
) {
1348 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "To"), "epid=", ";", NULL
);
1350 if (!dialog
->theirepid
) {
1351 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "To"), "epid=", NULL
, NULL
);
1354 dialog
->request
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Record-Route"), "<", ">", NULL
);
1355 dialog
->route
= g_strdup_printf("Route: %s\r\n", sipmsg_find_header(msg
, "Contact"));
1358 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
1360 session
->outgoing_invite
= NULL
;
1362 sipe_im_process_queue(sip
, session
);
1368 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1375 if (strstr(session
->with
, "sip:")) {
1376 to
= g_strdup(session
->with
);
1378 to
= g_strdup_printf("sip:%s", session
->with
);
1381 // Setup the outgoing dialog w/ the epid from the incoming dialog (if any)
1382 struct sip_dialog
* dialog
= g_new0(struct sip_dialog
, 1);
1383 if (session
->incoming_dialog
) {
1384 printf("incoming dialog epid is %s\n", session
->incoming_dialog
->theirepid
);
1385 dialog
->theirepid
= session
->incoming_dialog
->theirepid
;
1387 printf("incoming dialog is NULL\n");
1389 session
->outgoing_dialog
= dialog
;
1391 contact
= get_contact(sip
);
1392 hdr
= g_strdup_printf(
1394 //"Supported: ms-conf-invite\r\n"
1395 //"Supported: ms-delayed-accept\r\n"
1396 //"Supported: ms-renders-isf\r\n"
1397 //"Supported: ms-renders-gif\r\n"
1398 //"Supported: ms-renders-mime-alternative\r\n"*/
1399 //"Supported: timer\r\n"
1400 //"Supported: ms-sender\r\n"
1401 //"Supported: ms-early-media\r\n"
1402 "Content-Type: application/sdp\r\n",
1403 contact
, sip
->username
, sip
->username
, to
);
1405 body
= g_strdup_printf(
1407 "o=- 0 0 IN IP4 %s\r\n"
1411 "m=message %d sip null\r\n"
1412 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1413 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), 5061);
1415 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
1416 to
, to
, hdr
, body
, session
->outgoing_dialog
, process_invite_response
);
1425 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1428 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->outgoing_dialog
, NULL
);
1429 im_session_destroy(sip
, session
);
1434 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
1436 struct sipe_account_data
*sip
= gc
->proto_data
;
1438 purple_debug_info("sipe", "conversation with %s closed\n", who
);
1439 im_session_close(sip
, find_im_session(sip
, who
));
1443 im_session_close_all (struct sipe_account_data
*sip
)
1445 GSList
*entry
= sip
->im_sessions
;
1447 im_session_close (sip
, entry
->data
);
1448 entry
= sip
->im_sessions
;
1452 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
1454 struct sipe_account_data
*sip
= gc
->proto_data
;
1455 char *to
= g_strdup(who
);
1456 char *text
= purple_unescape_html(what
);
1458 struct sip_im_session
* session
= find_or_create_im_session(sip
, who
);
1460 // Queue the message
1461 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, text
);
1463 if (session
->outgoing_dialog
&& session
->outgoing_dialog
->callid
) {
1464 sipe_im_process_queue(sip
, session
);
1465 } else if (!session
->outgoing_invite
) {
1466 // Need to send the INVITE to get the outgoing dialog setup
1467 sipe_invite(sip
, session
);
1475 /* End IM Session (INVITE and MESSAGE methods) */
1477 static void sipe_buddy_resub(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1479 time_t curtime
= time(NULL
);
1480 if (buddy
->resubscribe
< curtime
) {
1481 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_buddy_resub %s\n", name
);
1482 sipe_subscribe(sip
, buddy
);
1486 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
1488 GSList
*tmp
= sip
->transactions
;
1489 time_t currtime
= time(NULL
);
1491 struct transaction
*trans
= tmp
->data
;
1493 purple_debug_info("sipe", "have open transaction age: %d\n", currtime
- trans
->time
);
1494 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
1497 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
1499 sendout_sipmsg(sip
, trans
->msg
);
1506 static gboolean
subscribe_timeout(struct sipe_account_data
*sip
)
1509 time_t curtime
= time(NULL
);
1510 /* register again if first registration expires */
1511 if (sip
->reregister
< curtime
) {
1514 /* check for every subscription if we need to resubscribe */
1515 //Fixxxer we need resub?
1516 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_resub
, (gpointer
)sip
);
1518 /* remove a timed out suscriber */
1522 struct sipe_watcher
*watcher
= tmp
->data
;
1523 if (watcher
->expire
< curtime
) {
1524 watcher_remove(sip
, watcher
->name
);
1527 if (tmp
) tmp
= tmp
->next
;
1533 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1537 gboolean found
= FALSE
;
1539 from
= parse_from(sipmsg_find_header(msg
, "From"));
1543 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
1545 contenttype
= sipmsg_find_header(msg
, "Content-Type");
1546 if (!contenttype
|| !strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
1547 serv_got_im(sip
->gc
, from
, msg
->body
, 0, time(NULL
));
1548 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1551 if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
1552 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1557 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1561 state
= xmlnode_get_child(isc
, "state");
1564 purple_debug_info("sipe", "process_incoming_message: no state found\n");
1569 statedata
= xmlnode_get_data(state
);
1571 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
1572 else serv_got_typing_stopped(sip
->gc
, from
);
1577 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1581 purple_debug_info("sipe", "got unknown mime-type");
1582 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
1587 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1589 gchar
* from
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "<", ">", NULL
);
1590 struct sip_im_session
* session
= find_or_create_im_session (sip
, from
);
1592 struct sip_dialog
* dialog
= g_new0(struct sip_dialog
, 1);
1593 dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
1594 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
1595 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
1596 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
1597 printf("Created incoming dialog and set epid to %s\n", dialog
->theirepid
);
1599 session
->incoming_dialog
= dialog
;
1601 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
1605 send_sip_response(sip
->gc
, msg
, 200, "OK", g_strdup_printf(
1607 "o=- 0 0 IN IP4 %s\r\n"
1611 "m=message %d sip sip:%s\r\n"
1612 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1613 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
1614 //sip->realport, sip->username
1615 5061, sip
->username
));
1618 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1620 gchar
*tmp
, krb5_token
;
1621 const gchar
*expires_header
;
1624 expires_header
= sipmsg_find_header(msg
, "Expires");
1625 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
1626 purple_debug_info("sipe", "got response to REGISTER; expires = %d\n", expires
);
1628 switch (msg
->response
) {
1631 sip
->registerstatus
= 0;
1633 sip
->registerstatus
= 3;
1634 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
1636 /* tell everybody we're online */
1639 /* get buddies from blist; Has a bug */
1640 /*sipe_get_buddies(sip->gc);*/
1641 subscribe_timeout(sip
);
1643 //sipe_subscribe_to_name(sip, sip->username);
1645 tmp
= sipmsg_find_header(msg
, "Allow-Events");
1646 if (tmp
&& strstr(tmp
, "vnd-microsoft-provisioning")){
1647 sipe_subscribe_buddylist(sip
);
1650 // Should we remove the transaction here?
1651 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
1652 transactions_remove(sip
, tc
);
1656 if (sip
->registerstatus
!= 2) {
1657 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
1658 if (sip
->registrar
.retries
> 3) {
1659 sip
->gc
->wants_to_die
= TRUE
;
1660 purple_connection_error(sip
->gc
, _("Wrong Password"));
1663 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
1664 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
1666 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
1668 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
1669 fill_auth(sip
, tmp
, &sip
->registrar
);
1670 sip
->registerstatus
= 2;
1671 if (sip
->account
->disconnecting
) {
1672 do_register_exp(sip
, 0);
1682 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1688 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
1689 gboolean isonline
= FALSE
;
1691 fromhdr
= sipmsg_find_header(msg
, "From");
1692 from
= parse_from(fromhdr
);
1695 pidf
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1698 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
1702 purple_debug_info("sipe", "process_incoming_notify: body(%s)\n",msg
->body
);
1704 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
1705 if ((status
= xmlnode_get_child(tuple
, "status")))
1706 basicstatus
= xmlnode_get_child(status
, "basic");
1709 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
1714 tmp2
= xmlnode_get_data(basicstatus
);
1716 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n",tmp2
);
1720 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
1725 if (strstr(tmp2
, "open")) {
1731 if (isonline
) purple_prpl_got_user_status(sip
->account
, from
, "available", NULL
);
1732 else purple_prpl_got_user_status(sip
->account
, from
, "offline", NULL
);
1737 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1741 static gchar
* gen_xpidf(struct sipe_account_data
*sip
)
1743 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1745 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
1746 "<display name=\"sip:%s\"/>\r\n"
1747 "<atom id=\"1234\">\r\n"
1748 "<address uri=\"sip:%s\">\r\n"
1749 "<status status=\"%s\"/>\r\n"
1762 static gchar
* gen_pidf(struct sipe_account_data
*sip
)
1764 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1765 "<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"
1766 "<tuple id=\"0\">\r\n"
1768 "<basic>open</basic>\r\n"
1769 "<ep:activities>\r\n"
1770 " <ep:activity>%s</ep:activity>\r\n"
1774 "<ci:display-name>%s</ci:display-name>\r\n"
1782 static void send_notify(struct sipe_account_data
*sip
, struct sipe_watcher
*watcher
)
1784 gchar
*doc
= watcher
->needsxpidf
? gen_xpidf(sip
) : gen_pidf(sip
);
1785 gchar
*hdr
= watcher
->needsxpidf
? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
1786 send_sip_request(sip
->gc
, "NOTIFY", watcher
->name
, watcher
->name
, hdr
, doc
, &watcher
->dialog
, NULL
);
1790 static gboolean
process_service_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1792 if (msg
->response
!= 200 && msg
->response
!= 408) {
1793 /* never send again */
1794 sip
->republish
= -1;
1799 static void send_publish(struct sipe_account_data
*sip
)
1801 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
1802 gchar
*doc
= g_strdup_printf(
1803 "<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>",
1804 uri
, generateUUIDfromEPID(getuuid()), uri
,
1805 "00:00:00-05:00", // TODO timezone
1806 "PC" // TODO machine name
1809 gchar
*tmp
= get_contact(sip
);
1810 gchar
*hdr
= g_strdup_printf("Contact: %s; +sip.instance=\"<urn:uuid:%s>\"\r\nAccept: application/ms-location-profile-definition+xml\r\nContent-Type: application/msrtc-category-publish+xml\r\n", tmp
,generateUUIDfromEPID(getuuid()));
1813 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_service_response
);
1814 //sip->republish = time(NULL) + 500;
1820 static void send_service(struct sipe_account_data
*sip
)
1822 //gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->sipdomain);
1823 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
1824 //gchar *doc = gen_pidf(sip);
1826 gchar
*doc
= gen_pidf(sip
);
1827 gchar
*hdr
= g_strdup("Event: presence\r\nContent-Type: application/pidf+xml\r\n");
1829 //gchar *hdr = g_strdup("Content-Type: application/SOAP+xml\r\n");
1830 gchar
*tmp
= get_contact(sip
);
1831 hdr
= g_strdup_printf("Contact: %s\r\n%s; +sip.instance=\"<urn:uuid:%s>\"", tmp
, hdr
,generateUUIDfromEPID(getuuid()));
1833 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
,
1835 doc
, NULL
, process_service_response
);
1836 sip
->republish
= time(NULL
) + 500;
1842 static void process_incoming_subscribe(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1844 const char *from_hdr
= sipmsg_find_header(msg
, "From");
1845 gchar
*from
= parse_from(from_hdr
);
1846 gchar
*theirtag
= find_tag(from_hdr
);
1847 gchar
*ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
1848 gboolean tagadded
= FALSE
;
1849 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1850 gchar
*expire
= sipmsg_find_header(msg
, "Expire");
1851 // gchar *ms-received-port =find_received_port(sipmsg_find_header(msg, "Contact"));
1853 struct sipe_watcher
*watcher
= watcher_find(sip
, from
);
1858 if (!watcher
) { /* new subscription */
1859 gchar
*acceptheader
= sipmsg_find_header(msg
, "Accept");
1860 gboolean needsxpidf
= FALSE
;
1861 if (!purple_privacy_check(sip
->account
, from
)) {
1862 send_sip_response(sip
->gc
, msg
, 202, "Ok", NULL
);
1866 gchar
*tmp
= acceptheader
;
1867 gboolean foundpidf
= FALSE
;
1868 gboolean foundxpidf
= FALSE
;
1869 while (tmp
&& tmp
< acceptheader
+ strlen(acceptheader
)) {
1870 gchar
*tmp2
= strchr(tmp
, ',');
1871 if (tmp2
) *tmp2
= '\0';
1872 if (!strcmp("application/pidf+xml", tmp
))
1874 if (!strcmp("application/xpidf+xml", tmp
))
1879 while (*tmp
== ' ') tmp
++;
1883 if (!foundpidf
&& foundxpidf
) needsxpidf
= TRUE
;
1884 g_free(acceptheader
);
1886 watcher
= watcher_create(sip
, from
, callid
, ourtag
, theirtag
, needsxpidf
);
1889 gchar
*to
= g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg
, "To"), ourtag
);
1890 sipmsg_remove_header(msg
, "To");
1891 sipmsg_add_header(msg
, "To", to
);
1895 watcher
->expire
= time(NULL
) + strtol(expire
, NULL
, 10);
1897 watcher
->expire
= time(NULL
) + 600;
1899 sipmsg_remove_header(msg
, "Contact");
1900 tmp
= get_contact(sip
);
1901 sipmsg_add_header(msg
, "Contact", tmp
);
1903 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
);
1904 send_sip_response(sip
->gc
, msg
, 200, "Ok", NULL
);
1905 send_notify(sip
, watcher
);
1914 static void process_input_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1916 gboolean found
= FALSE
;
1917 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
1918 if (msg
->response
== 0) { /* request */
1919 if (!strcmp(msg
->method
, "MESSAGE")) {
1920 process_incoming_message(sip
, msg
);
1922 } else if (!strcmp(msg
->method
, "NOTIFY")) {
1923 purple_debug_info("sipe","send->process_incoming_notify\n");
1924 process_incoming_notify(sip
, msg
);
1926 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
1927 purple_debug_info("sipe","send->process_incoming_subscribe\n");
1928 process_incoming_subscribe(sip
, msg
);
1930 } else if (!strcmp(msg
->method
, "INVITE")) {
1931 process_incoming_invite(sip
, msg
);
1933 } else if (!strcmp(msg
->method
, "INFO")) {
1935 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
1937 serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
1939 printf("INFO body:\n%s\n", msg
->body
);
1940 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1942 } else if (!strcmp(msg
->method
, "ACK")) {
1943 // ACK's don't need any response
1944 //send_sip_response(sip->gc, msg, 200, "OK", NULL);
1946 } else if (!strcmp(msg
->method
, "BYE")) {
1947 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1949 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
1950 struct sip_im_session
* session
= find_im_session (sip
, from
);
1954 // TODO Let the user know the other user left the conversation?
1955 im_session_destroy(sip
, session
);
1960 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
1962 } else { /* response */
1963 struct transaction
*trans
= transactions_find(sip
, msg
);
1965 if (msg
->response
== 407) {
1966 gchar
*resend
, *auth
, *ptmp
;
1968 if (sip
->proxy
.retries
> 30) return;
1969 sip
->proxy
.retries
++;
1970 /* do proxy authentication */
1972 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
1974 fill_auth(sip
, ptmp
, &sip
->proxy
);
1975 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
, TRUE
);
1976 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
1977 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
1979 resend
= sipmsg_to_string(trans
->msg
);
1980 /* resend request */
1981 sendout_pkt(sip
->gc
, resend
);
1984 if (msg
->response
== 100) {
1985 /* ignore provisional response */
1986 purple_debug_info("sipe", "got trying response\n");
1988 sip
->proxy
.retries
= 0;
1989 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
1990 if (msg
->response
== 401) sip
->registrar
.retries
++;
1991 else sip
->registrar
.retries
= 0;
1992 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
1994 if (msg
->response
== 401) {
1995 gchar
*resend
, *auth
, *ptmp
;
1997 if (sip
->registrar
.retries
> 4) return;
1998 sip
->registrar
.retries
++;
2000 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
2001 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
2003 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
2006 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
2008 fill_auth(sip
, ptmp
, &sip
->registrar
);
2009 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
, TRUE
);
2010 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
2011 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
2013 //sipmsg_remove_header(trans->msg, "Authorization");
2014 //sipmsg_add_header(trans->msg, "Authorization", auth);
2016 resend
= sipmsg_to_string(trans
->msg
);
2017 /* resend request */
2018 sendout_pkt(sip
->gc
, resend
);
2022 if (trans
->callback
) {
2023 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
2024 /* call the callback to process response*/
2025 (trans
->callback
)(sip
, msg
, trans
);
2027 /* Not sure if this is needed or what needs to be done
2028 but transactions seem to be removed prematurely so
2029 this only removes them if the response is 200 OK */
2030 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
2031 /*Has a bug and it's unneccesary*/
2032 /*transactions_remove(sip, trans);*/
2037 /* This is done because in an OCS2007 server trace the MS
2038 * Communicator client seems to reset the CSeq after an OK */
2042 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction");
2046 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
2050 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
2058 /* according to the RFC remove CRLF at the beginning */
2059 while (*cur
== '\r' || *cur
== '\n') {
2062 if (cur
!= conn
->inbuf
) {
2063 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
2064 conn
->inbufused
= strlen(conn
->inbuf
);
2067 /* Received a full Header? */
2068 if ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
) {
2069 time_t currtime
= time(NULL
);
2072 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
2073 msg
= sipmsg_parse_header(conn
->inbuf
);
2076 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
2077 if (restlen
>= msg
->bodylen
) {
2078 dummy
= g_malloc(msg
->bodylen
+ 1);
2079 memcpy(dummy
, cur
, msg
->bodylen
);
2080 dummy
[msg
->bodylen
] = '\0';
2082 cur
+= msg
->bodylen
;
2083 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
2084 conn
->inbufused
= strlen(conn
->inbuf
);
2090 // Verify the signature before processing it
2091 if (sip
->registrar
.ntlm_key
) {
2092 struct sipmsg_breakdown msgbd
;
2094 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
2095 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
2097 if (signature_input_str
!= NULL
) {
2098 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
2101 gchar
* rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
2102 if (signature
!= NULL
&& rspauth
!= NULL
) {
2103 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
2104 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
2105 process_input_message(sip
, msg
);
2107 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
2111 sipmsg_breakdown_free(&msgbd
);
2113 process_input_message(sip
, msg
);
2117 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a incomplete sip msg: %s\n", conn
->inbuf
);
2121 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
2123 PurpleConnection
*gc
= data
;
2124 struct sipe_account_data
*sip
= gc
->proto_data
;
2129 static char buffer
[65536];
2130 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
2132 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
2133 msg
= sipmsg_parse_msg(buffer
);
2134 if (msg
) process_input_message(sip
, msg
);
2138 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
2140 PurpleConnection
*gc
= data
;
2141 struct sipe_account_data
*sip
= gc
->proto_data
;
2142 struct sip_connection
*conn
= NULL
;
2144 static char buf
[4096];
2146 /* TODO: It should be possible to make this check unnecessary */
2147 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
2148 if (gsc
) purple_ssl_close(gsc
);
2153 conn
= connection_find(sip
, sip
->gsc
->fd
);
2155 purple_debug_error("sipe", "Connection not found!\n");
2156 if (sip
->gsc
) purple_ssl_close(sip
->gsc
);
2161 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
2162 conn
->inbuflen
+= SIMPLE_BUF_INC
;
2163 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
2166 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
2168 if (len
< 0 && errno
== EAGAIN
) {
2169 /* Try again later */
2171 } else if (len
< 0) {
2172 purple_debug_info("sipe", "sipe_input_cb_ssl: read error\n");
2174 connection_remove(sip
, sip
->gsc
->fd
);
2175 if (sip
->fd
== gsc
->fd
) sip
->fd
= -1;
2178 } else if (len
== 0) {
2179 purple_connection_error(gc
, _("Server has disconnected"));
2181 connection_remove(sip
, sip
->gsc
->fd
);
2182 if (sip
->fd
== gsc
->fd
) sip
->fd
= -1;
2187 conn
->inbufused
+= len
;
2188 conn
->inbuf
[conn
->inbufused
] = '\0';
2190 process_input(sip
, conn
);
2194 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
2196 PurpleConnection
*gc
= data
;
2197 struct sipe_account_data
*sip
= gc
->proto_data
;
2199 struct sip_connection
*conn
= connection_find(sip
, source
);
2201 purple_debug_error("sipe", "Connection not found!\n");
2205 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
2206 conn
->inbuflen
+= SIMPLE_BUF_INC
;
2207 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
2210 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
2212 if (len
< 0 && errno
== EAGAIN
)
2214 else if (len
<= 0) {
2215 purple_debug_info("sipe", "sipe_input_cb: read error\n");
2216 connection_remove(sip
, source
);
2217 if (sip
->fd
== source
) sip
->fd
= -1;
2221 conn
->inbufused
+= len
;
2222 conn
->inbuf
[conn
->inbufused
] = '\0';
2224 process_input(sip
, conn
);
2227 /* Callback for new connections on incoming TCP port */
2228 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
2230 PurpleConnection
*gc
= data
;
2231 struct sipe_account_data
*sip
= gc
->proto_data
;
2232 struct sip_connection
*conn
;
2234 int newfd
= accept(source
, NULL
, NULL
);
2236 conn
= connection_create(sip
, newfd
);
2238 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2241 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
2243 PurpleConnection
*gc
= data
;
2244 struct sipe_account_data
*sip
;
2245 struct sip_connection
*conn
;
2247 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2255 purple_connection_error(gc
, _("Could not connect"));
2259 sip
= gc
->proto_data
;
2262 conn
= connection_create(sip
, source
);
2264 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2268 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2271 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
2273 PurpleConnection
*gc
= data
;
2274 struct sipe_account_data
*sip
;
2275 struct sip_connection
*conn
;
2277 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2279 if (gsc
) purple_ssl_close(gsc
);
2283 sip
= gc
->proto_data
;
2285 conn
= connection_create(sip
, sip
->fd
);
2286 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
2287 sip
->listenfd
= sip
->fd
;
2288 sip
->registertimeout
= purple_timeout_add((rand()%100)+3*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2292 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
2295 static guint
sipe_ht_hash_nick(const char *nick
)
2297 char *lc
= g_utf8_strdown(nick
, -1);
2298 guint bucket
= g_str_hash(lc
);
2304 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
2306 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
2309 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
2311 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2313 sip
->listen_data
= NULL
;
2315 if (listenfd
== -1) {
2316 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2322 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
2323 sip
->listenfd
= sip
->fd
;
2325 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
2327 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
2328 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2332 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
2334 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2337 sip
->query_data
= NULL
;
2339 if (!hosts
|| !hosts
->data
) {
2340 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
2344 addr_size
= GPOINTER_TO_INT(hosts
->data
);
2345 hosts
= g_slist_remove(hosts
, hosts
->data
);
2346 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
2347 g_free(hosts
->data
);
2348 hosts
= g_slist_remove(hosts
, hosts
->data
);
2350 hosts
= g_slist_remove(hosts
, hosts
->data
);
2351 g_free(hosts
->data
);
2352 hosts
= g_slist_remove(hosts
, hosts
->data
);
2355 /* create socket for incoming connections */
2356 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
2357 sipe_udp_host_resolved_listen_cb
, sip
);
2358 if (sip
->listen_data
== NULL
) {
2359 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2364 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
2367 PurpleConnection
*gc
= data
;
2368 struct sipe_account_data
*sip
;
2370 /* If the connection is already disconnected, we don't need to do anything else */
2371 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2374 sip
= gc
->proto_data
;
2378 case PURPLE_SSL_CONNECT_FAILED
:
2379 purple_connection_error(gc
, _("Connection Failed"));
2381 case PURPLE_SSL_HANDSHAKE_FAILED
:
2382 purple_connection_error(gc
, _("SSL Handshake Failed"));
2388 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
2390 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2391 PurpleProxyConnectData
*connect_data
;
2393 sip
->listen_data
= NULL
;
2395 sip
->listenfd
= listenfd
;
2396 if (sip
->listenfd
== -1) {
2397 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2401 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
2402 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2403 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
2404 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
2405 sipe_newconn_cb
, sip
->gc
);
2406 purple_debug_info("sipe", "connecting to %s port %d\n",
2407 sip
->realhostname
, sip
->realport
);
2408 /* open tcp connection to the server */
2409 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
2410 sip
->realport
, login_cb
, sip
->gc
);
2412 if (connect_data
== NULL
) {
2413 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
2419 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
2421 struct sipe_account_data
*sip
;
2426 sip
->srv_query_data
= NULL
;
2428 port
= purple_account_get_int(sip
->account
, "port", 0);
2430 /* find the host to connect to */
2432 hostname
= g_strdup(resp
->hostname
);
2433 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s\r\n", hostname
);
2438 if (!purple_account_get_bool(sip
->account
, "useproxy", FALSE
)) {
2439 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - using sipdomain\r\n");
2440 hostname
= g_strdup(sip
->sipdomain
);
2442 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - using specified SIP proxy\r\n");
2443 hostname
= g_strdup(purple_account_get_string(sip
->account
, "proxy", sip
->sipdomain
));
2447 sip
->realhostname
= hostname
;
2448 sip
->realport
= port
;
2449 if (!sip
->realport
) sip
->realport
= 5060;
2453 // /* create socket for incoming connections */
2454 // sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_STREAM,
2455 // sipe_tcp_connect_listen_cb, sip);
2456 // if (sip->listen_data == NULL) {
2457 // purple_connection_error(sip->gc, _("Could not create listen socket"));
2460 //} else { /* UDP */
2461 // purple_debug_info("sipe", "using udp with server %s and port %d\n", hostname, port);
2463 // sip->query_data = purple_dnsquery_a(hostname, port, sipe_udp_host_resolved, sip);
2464 // if (sip->query_data == NULL) {
2465 // purple_connection_error(sip->gc, _("Could not resolve hostname"));
2470 static void sipe_login(PurpleAccount
*account
)
2472 PurpleConnection
*gc
;
2473 struct sipe_account_data
*sip
;
2475 gchar
*hosttoconnect
;
2477 const char *username
= purple_account_get_username(account
);
2478 gc
= purple_account_get_connection(account
);
2480 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
2481 gc
->wants_to_die
= TRUE
;
2482 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
2486 if (!purple_account_get_bool(account
, "ssl", FALSE
)){
2487 if (!purple_ssl_is_supported())
2489 gc
->wants_to_die
= TRUE
;
2490 purple_connection_error(gc
,
2491 _("SSL support is needed for SSL/TLS support. Please install a supported "
2498 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
2500 sip
->account
= account
;
2501 sip
->registerexpire
= 900;
2502 sip
->udp
= purple_account_get_bool(account
, "udp", FALSE
);
2503 sip
->use_ssl
= purple_account_get_bool(account
, "ssl", FALSE
);
2505 purple_debug_info("sipe", "sip->use_ssl->%d\n", sip
->use_ssl
);
2507 /* TODO: is there a good default grow size? */
2509 sip
->txbuf
= purple_circ_buffer_new(0);
2511 userserver
= g_strsplit(username
, "@", 2);
2512 purple_connection_set_display_name(gc
, userserver
[0]);
2513 sip
->username
= g_strdup(g_strjoin("@", userserver
[0], userserver
[1], NULL
));
2514 sip
->sipdomain
= g_strdup(userserver
[1]);
2515 sip
->password
= g_strdup(purple_connection_get_password(gc
));
2516 g_strfreev(userserver
);
2519 // Communicator queries _sipinternaltls._tcp.domain.com and uses that
2520 // information to connect to the OCS server.
2522 // XXX FIXME: eventually we should also query for sipexternaltls as well
2523 // if Pidgin is not on the local LAN
2524 // This doesn't quite work as advertised yet so make sure your have
2525 // your OCS FQDN in the proxy setting in the SIPE account settings
2527 sip
->srv_query_data
= purple_srv_resolve("sipinternaltls", "tcp", sip
->sipdomain
, srvresolved
, sip
);
2530 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - realhostname: %s\r\n", sip
->realhostname
);
2532 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
2534 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
2536 /* TODO: Set the status correctly. */
2537 sip
->status
= g_strdup("available");
2539 if (!purple_account_get_bool(account
, "useproxy", FALSE
)) {
2540 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - checking realhostname again: %s\r\n", sip
->realhostname
);
2541 hosttoconnect
= g_strdup(sip
->sipdomain
);
2543 hosttoconnect
= g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
));
2547 purple_debug_info("sipe", "HosttoConnect->%s\n", hosttoconnect
);
2550 sip
->gsc
= purple_ssl_connect(account
,hosttoconnect
, purple_account_get_int(account
, "port", 5061), login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
2553 sip
->srv_query_data
= purple_srv_resolve("sip",
2554 sip
->udp
? "udp" : "tcp", hosttoconnect
, srvresolved
, sip
);
2557 g_free(hosttoconnect
);
2560 static void sipe_close(PurpleConnection
*gc
)
2562 struct sipe_account_data
*sip
= gc
->proto_data
;
2565 /* leave all conversations */
2566 im_session_close_all(sip
);
2569 do_register_exp(sip
, 0);
2570 connection_free_all(sip
);
2572 if (sip
->query_data
!= NULL
)
2573 purple_dnsquery_destroy(sip
->query_data
);
2575 if (sip
->srv_query_data
!= NULL
)
2576 purple_srv_cancel(sip
->srv_query_data
);
2578 if (sip
->listen_data
!= NULL
)
2579 purple_network_listen_cancel(sip
->listen_data
);
2581 g_free(sip
->sipdomain
);
2582 g_free(sip
->username
);
2583 g_free(sip
->password
);
2584 g_free(sip
->registrar
.nonce
);
2585 g_free(sip
->registrar
.opaque
);
2586 g_free(sip
->registrar
.target
);
2587 g_free(sip
->registrar
.realm
);
2588 g_free(sip
->registrar
.digest_session_key
);
2589 g_free(sip
->proxy
.nonce
);
2590 g_free(sip
->proxy
.opaque
);
2591 g_free(sip
->proxy
.target
);
2592 g_free(sip
->proxy
.realm
);
2593 g_free(sip
->proxy
.digest_session_key
);
2595 purple_circ_buffer_destroy(sip
->txbuf
);
2596 g_free(sip
->realhostname
);
2597 if (sip
->listenpa
) purple_input_remove(sip
->listenpa
);
2598 if (sip
->tx_handler
) purple_input_remove(sip
->tx_handler
);
2599 if (sip
->resendtimeout
) purple_timeout_remove(sip
->resendtimeout
);
2600 if (sip
->registertimeout
) purple_timeout_remove(sip
->registertimeout
);
2602 g_free(gc
->proto_data
);
2603 gc
->proto_data
= NULL
;
2606 /* not needed since privacy is checked for every subscribe */
2607 static void dummy_add_deny(PurpleConnection
*gc
, const char *name
) {
2610 static void dummy_permit_deny(PurpleConnection
*gc
)
2614 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
2620 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
2626 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
2630 static PurplePlugin
*my_protocol
= NULL
;
2632 static PurplePluginProtocolInfo prpl_info
=
2635 NULL
, /* user_splits */
2636 NULL
, /* protocol_options */
2637 NO_BUDDY_ICONS
, /* icon_spec */
2638 sipe_list_icon
, /* list_icon */
2639 NULL
, /* list_emblems */
2640 NULL
, /* status_text */
2641 NULL
, /* tooltip_text */
2642 sipe_status_types
, /* away_states */
2643 NULL
, /* blist_node_menu */
2644 NULL
, /* chat_info */
2645 NULL
, /* chat_info_defaults */
2646 sipe_login
, /* login */
2647 sipe_close
, /* close */
2648 sipe_im_send
, /* send_im */
2649 NULL
, /* set_info */
2650 // sipe_typing, /* send_typing */
2651 NULL
, /* send_typing */
2652 NULL
, /* get_info */
2653 sipe_set_status
, /* set_status */
2654 NULL
, /* set_idle */
2655 NULL
, /* change_passwd */
2656 sipe_add_buddy
, /* add_buddy */
2657 NULL
, /* add_buddies */
2658 sipe_remove_buddy
, /* remove_buddy */
2659 NULL
, /* remove_buddies */
2660 dummy_add_deny
, /* add_permit */
2661 dummy_add_deny
, /* add_deny */
2662 dummy_add_deny
, /* rem_permit */
2663 dummy_add_deny
, /* rem_deny */
2664 dummy_permit_deny
, /* set_permit_deny */
2665 NULL
, /* join_chat */
2666 NULL
, /* reject_chat */
2667 NULL
, /* get_chat_name */
2668 NULL
, /* chat_invite */
2669 NULL
, /* chat_leave */
2670 NULL
, /* chat_whisper */
2671 NULL
, /* chat_send */
2672 sipe_keep_alive
, /* keepalive */
2673 NULL
, /* register_user */
2674 NULL
, /* get_cb_info */
2675 NULL
, /* get_cb_away */
2676 NULL
, /* alias_buddy */
2677 NULL
, /* group_buddy */
2678 NULL
, /* rename_group */
2679 NULL
, /* buddy_free */
2680 sipe_convo_closed
, /* convo_closed */
2681 purple_normalize_nocase
, /* normalize */
2682 NULL
, /* set_buddy_icon */
2683 NULL
, /* remove_group */
2684 NULL
, /* get_cb_real_name */
2685 NULL
, /* set_chat_topic */
2686 NULL
, /* find_blist_chat */
2687 NULL
, /* roomlist_get_list */
2688 NULL
, /* roomlist_cancel */
2689 NULL
, /* roomlist_expand_category */
2690 NULL
, /* can_receive_file */
2691 NULL
, /* send_file */
2692 NULL
, /* new_xfer */
2693 NULL
, /* offline_message */
2694 NULL
, /* whiteboard_prpl_ops */
2695 sipe_send_raw
, /* send_raw */
2699 static PurplePluginInfo info
= {
2700 PURPLE_PLUGIN_MAGIC
,
2701 PURPLE_MAJOR_VERSION
,
2702 PURPLE_MINOR_VERSION
,
2703 PURPLE_PLUGIN_PROTOCOL
, /**< type */
2704 NULL
, /**< ui_requirement */
2706 NULL
, /**< dependencies */
2707 PURPLE_PRIORITY_DEFAULT
, /**< priority */
2708 "prpl-sipe", /**< id */
2709 "Microsoft LCS/OCS", /**< name */
2710 VERSION
, /**< version */
2711 N_("SIP/SIMPLE Exchange Protocol Plugin"), /** summary */
2712 N_("The SIP/SIMPLE Exchange Protocol Plugin"), /** description */
2713 "Anibal Avelar <avelar@gmail.com>", /**< author */
2714 PURPLE_WEBSITE
, /**< homepage */
2715 sipe_plugin_load
, /**< load */
2716 sipe_plugin_unload
, /**< unload */
2717 sipe_plugin_destroy
, /**< destroy */
2718 NULL
, /**< ui_info */
2719 &prpl_info
, /**< extra_info */
2728 static void init_plugin(PurplePlugin
*plugin
)
2730 PurpleAccountUserSplit
*split
;
2731 PurpleAccountOption
*option
;
2733 purple_plugin_register(plugin
);
2735 //split = purple_account_user_split_new(_("Server"), "", '@');
2736 //prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
2737 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
2738 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2739 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
2740 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2742 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
2743 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2745 option
= purple_account_option_bool_new(_("Use SSL/TLS"), "ssl", FALSE
);
2746 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
,option
);
2748 option
= purple_account_option_string_new(_("UserAgent"), "useragent", "Purple/" VERSION
);
2749 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
,option
);
2751 option
= purple_account_option_bool_new(_("Use UDP"), "udp", FALSE
);
2752 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2754 option
= purple_account_option_int_new(_("Connect port"), "port", 5060);
2755 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2757 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
2758 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2760 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
2761 option
= purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
2762 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2764 /*option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2765 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2766 option = purple_account_option_string_new(_("Proxy"), "proxy", "");
2767 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2768 option
= purple_account_option_string_new(_("Auth User"), "authuser", "");
2769 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2770 option
= purple_account_option_string_new(_("Auth Domain"), "authdomain", "");
2771 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2772 my_protocol
= plugin
;
2776 /* I had to redefined the function for it load, but works */
2777 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
2778 plugin
->info
= &(info
);
2779 init_plugin((plugin
));
2780 sipe_plugin_load((plugin
));
2781 return purple_plugin_register(plugin
);