6 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
7 * Copyright (C) 2007 Anibal Avelar <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 <sys/socket.h>
34 #include <sys/ioctl.h>
35 #include <sys/types.h>
36 #include <netinet/in.h>
42 #define _LIBC_INTERNAL_
54 # define _(String) ((const char *) gettext (String))
56 # define _(String) ((const char *) (String))
57 #endif /* ENABLE_NLS */
59 #include "accountopt.h"
61 #include "conversation.h"
77 #endif /*USE_KERBEROS*/
80 #include "sipe-sign.h"
84 /* Keep in sync with sipe_transport_type! */
85 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
86 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
90 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
93 static gchar
*get_epid()
95 return sipe_uuid_get_macaddr();
98 static char *genbranch()
100 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
101 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
102 rand() & 0xFFFF, rand() & 0xFFFF);
105 static char *gencallid()
107 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
108 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
109 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
110 rand() & 0xFFFF, rand() & 0xFFFF);
113 static gchar
*find_tag(const gchar
*hdr
)
115 gchar
* tag
= sipmsg_find_part_of_header (hdr
, "tag=", ";", NULL
);
117 // In case it's at the end and there's no trailing ;
118 tag
= sipmsg_find_part_of_header (hdr
, "tag=", NULL
, NULL
);
124 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
129 static void sipe_keep_alive(PurpleConnection
*gc
)
131 struct sipe_account_data
*sip
= gc
->proto_data
;
132 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
133 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
134 gchar buf
[2] = {0, 0};
135 purple_debug_info("sipe", "sending keep alive\n");
136 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
140 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
142 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
143 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
146 static void send_notify(struct sipe_account_data
*sip
, struct sipe_watcher
*);
147 static void sipe_close(PurpleConnection
*gc
);
149 static void send_service(struct sipe_account_data
*sip
);
150 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
);
151 static void send_presence_info(struct sipe_account_data
*sip
);
153 static void do_notifies(struct sipe_account_data
*sip
)
155 GSList
*tmp
= sip
->watcher
;
156 purple_debug_info("sipe", "do_notifies()\n");
159 purple_debug_info("sipe", "notifying %s\n", ((struct sipe_watcher
*)tmp
->data
)->name
);
160 send_notify(sip
, tmp
->data
);
165 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
167 struct sip_connection
*ret
= NULL
;
168 GSList
*entry
= sip
->openconns
;
171 if (ret
->fd
== fd
) return ret
;
177 static struct sipe_watcher
*watcher_find(struct sipe_account_data
*sip
,
180 struct sipe_watcher
*watcher
;
181 GSList
*entry
= sip
->watcher
;
183 watcher
= entry
->data
;
184 if (!strcmp(name
, watcher
->name
)) return watcher
;
190 static struct sipe_watcher
*watcher_create(struct sipe_account_data
*sip
,
191 const gchar
*name
, const gchar
*callid
, const gchar
*ourtag
,
192 const gchar
*theirtag
, gboolean needsxpidf
)
194 struct sipe_watcher
*watcher
= g_new0(struct sipe_watcher
, 1);
195 watcher
->name
= g_strdup(name
);
196 watcher
->dialog
.callid
= g_strdup(callid
);
197 watcher
->dialog
.ourtag
= g_strdup(ourtag
);
198 watcher
->dialog
.theirtag
= g_strdup(theirtag
);
199 watcher
->needsxpidf
= needsxpidf
;
200 sip
->watcher
= g_slist_append(sip
->watcher
, watcher
);
204 static void watcher_remove(struct sipe_account_data
*sip
, const gchar
*name
)
206 struct sipe_watcher
*watcher
= watcher_find(sip
, name
);
207 sip
->watcher
= g_slist_remove(sip
->watcher
, watcher
);
208 g_free(watcher
->name
);
209 g_free(watcher
->dialog
.callid
);
210 g_free(watcher
->dialog
.ourtag
);
211 g_free(watcher
->dialog
.theirtag
);
215 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
217 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
219 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
223 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
225 struct sip_connection
*conn
= connection_find(sip
, fd
);
226 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
227 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
232 static void connection_free_all(struct sipe_account_data
*sip
)
234 struct sip_connection
*ret
= NULL
;
235 GSList
*entry
= sip
->openconns
;
238 connection_remove(sip
, ret
->fd
);
239 entry
= sip
->openconns
;
243 static gchar
*auth_header_without_newline(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
, gboolean force_reauth
)
245 const gchar
*method
= msg
->method
;
246 const gchar
*target
= msg
->target
;
251 const char *authdomain
;
252 const char *authuser
;
253 const char *krb5_realm
;
255 gchar
*krb5_token
= NULL
;
257 authdomain
= purple_account_get_string(sip
->account
, "authdomain", "");
258 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
260 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
261 // and do error checking
263 // KRB realm should always be uppercase
264 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
266 if (sip
->realhostname
) {
267 host
= sip
->realhostname
;
268 } else if (purple_account_get_bool(sip
->account
, "useproxy", TRUE
)) {
269 host
= purple_account_get_string(sip
->account
, "proxy", "");
271 host
= sip
->sipdomain
;
274 /*gboolean new_auth = krb5_auth.gss_context == NULL;
276 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
279 if (new_auth || force_reauth) {
280 krb5_token = krb5_auth.base64_token;
283 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
284 krb5_token = krb5_auth.base64_token;*/
286 if (!authuser
|| strlen(authuser
) < 1) {
287 authuser
= sip
->username
;
290 if (auth
->type
== 1) { /* Digest */
291 sprintf(noncecount
, "%08d", auth
->nc
++);
292 response
= purple_cipher_http_digest_calculate_response(
293 "md5", method
, target
, NULL
, NULL
,
294 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
295 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
297 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
);
300 } else if (auth
->type
== 2) { /* NTLM */
301 // If we have a signature for the message, include that
302 if (msg
->signature
) {
303 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
);
307 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
308 /* TODO: Don't hardcode "purple" as the hostname */
309 const gchar
* ntlm_key
;
310 gchar
* gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, "purple", authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
311 auth
->ntlm_key
= (gchar
*)ntlm_key
;
312 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
317 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
319 } else if (auth
->type
== 3) {
322 /*if (new_auth || force_reauth) {
323 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
325 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);
327 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
330 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
331 gchar * mic = "MICTODO";
332 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
333 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
334 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
335 //auth->opaque ? auth->opaque : "", auth->target);
336 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
341 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
344 sprintf(noncecount
, "%08d", auth
->nc
++);
345 response
= purple_cipher_http_digest_calculate_response(
346 "md5", method
, target
, NULL
, NULL
,
347 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
348 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
350 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
);
355 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
, gboolean force_reauth
)
357 gchar
*with
, *without
;
359 without
= auth_header_without_newline(sip
, auth
, msg
, force_reauth
);
360 with
= g_strdup_printf("%s\r\n", without
);
366 static char *parse_attribute(const char *attrname
, const char *source
)
368 const char *tmp
, *tmp2
;
370 int len
= strlen(attrname
);
372 if (!strncmp(source
, attrname
, len
)) {
374 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
376 retval
= g_strndup(tmp
, tmp2
- tmp
);
378 retval
= g_strdup(tmp
);
384 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
387 const char *authuser
;
390 const char *krb5_realm
;
393 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
394 // and do error checking
396 // KRB realm should always be uppercase
397 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
399 if (sip->realhostname) {
400 host = sip->realhostname;
401 } else if (purple_account_get_bool(sip->account, "useproxy", TRUE)) {
402 host = purple_account_get_string(sip->account, "proxy", "");
404 host = sip->sipdomain;
407 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
409 if (!authuser
|| strlen(authuser
) < 1) {
410 authuser
= sip
->username
;
414 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
418 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
420 parts
= g_strsplit(hdr
+5, "\", ", 0);
423 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
424 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
425 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
428 if ((tmp
= parse_attribute("targetname=\"",
432 else if ((tmp
= parse_attribute("realm=\"",
436 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
443 if (!strstr(hdr
, "gssapi-data")) {
451 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
452 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
454 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
455 parts
= g_strsplit(hdr
+9, "\", ", 0);
458 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
459 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
460 /*if (krb5_auth.gss_context == NULL) {
461 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
463 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
466 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
468 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
470 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
477 //if (!strstr(hdr, "gssapi-data")) {
486 parts
= g_strsplit(hdr
, " ", 0);
488 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
491 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
498 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
500 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
501 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
507 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
509 PurpleConnection
*gc
= data
;
510 struct sipe_account_data
*sip
= gc
->proto_data
;
514 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
516 if (max_write
== 0) {
517 if (sip
->tx_handler
!= 0){
518 purple_input_remove(sip
->tx_handler
);
524 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
526 if (written
< 0 && errno
== EAGAIN
)
528 else if (written
<= 0) {
529 /*TODO: do we really want to disconnect on a failure to write?*/
530 purple_connection_error(gc
, _("Could not write"));
534 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
537 static void sipe_canwrite_cb_ssl(gpointer data
, gint src
, PurpleInputCondition cond
)
539 PurpleConnection
*gc
= data
;
540 struct sipe_account_data
*sip
= gc
->proto_data
;
544 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
546 if (max_write
== 0) {
547 if (sip
->tx_handler
!= 0) {
548 purple_input_remove(sip
->tx_handler
);
554 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
556 if (written
< 0 && errno
== EAGAIN
)
558 else if (written
<= 0) {
559 /*TODO: do we really want to disconnect on a failure to write?*/
560 purple_connection_error(gc
, _("Could not write"));
564 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
567 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
569 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
571 PurpleConnection
*gc
= data
;
572 struct sipe_account_data
*sip
;
573 struct sip_connection
*conn
;
575 if (!PURPLE_CONNECTION_IS_VALID(gc
))
583 purple_connection_error(gc
, _("Could not connect"));
587 sip
= gc
->proto_data
;
589 sip
->connecting
= FALSE
;
591 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
593 /* If there is more to write now, we need to register a handler */
594 if (sip
->txbuf
->bufused
> 0)
595 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
597 conn
= connection_create(sip
, source
);
598 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
601 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
603 PurpleConnection
*gc
= data
;
604 struct sipe_account_data
*sip
;
605 struct sip_connection
*conn
;
607 if (!PURPLE_CONNECTION_IS_VALID(gc
))
609 if(gsc
) purple_ssl_close(gsc
);
613 sip
= gc
->proto_data
;
616 sip
->connecting
= FALSE
;
618 sipe_canwrite_cb_ssl(gc
, sip
->gsc
->fd
, PURPLE_INPUT_WRITE
);
620 /* If there is more to write now */
621 if (sip
->txbuf
->bufused
> 0)
623 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
626 conn
= connection_create(sip
, sip
->gsc
->fd
);
627 purple_ssl_input_add(sip
->gsc
, sipe_input_cb_ssl
, gc
);
631 static void sendlater(PurpleConnection
*gc
, const char *buf
)
633 struct sipe_account_data
*sip
= gc
->proto_data
;
635 if (!sip
->connecting
) {
636 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
637 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
638 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
640 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
641 purple_connection_error(gc
, _("Couldn't create socket"));
644 sip
->connecting
= TRUE
;
647 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
648 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
650 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
653 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
655 struct sipe_account_data
*sip
= gc
->proto_data
;
656 time_t currtime
= time(NULL
);
657 int writelen
= strlen(buf
);
659 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
660 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
661 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
662 purple_debug_info("sipe", "could not send packet\n");
671 if (sip
->tx_handler
) {
676 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
678 ret
= write(sip
->fd
, buf
, writelen
);
682 if (ret
< 0 && errno
== EAGAIN
)
684 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
689 if (ret
< writelen
) {
690 if (!sip
->tx_handler
){
692 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
695 sip
->tx_handler
= purple_input_add(sip
->fd
,
696 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
701 /* XXX: is it OK to do this? You might get part of a request sent
702 with part of another. */
703 if (sip
->txbuf
->bufused
> 0)
704 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
706 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
712 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
714 sendout_pkt(gc
, buf
);
718 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
720 GSList
*tmp
= msg
->headers
;
723 GString
*outstr
= g_string_new("");
724 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
726 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
727 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
728 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
729 tmp
= g_slist_next(tmp
);
731 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
732 sendout_pkt(sip
->gc
, outstr
->str
);
733 g_string_free(outstr
, TRUE
);
736 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
739 if (sip
->registrar
.ntlm_key
) {
740 struct sipmsg_breakdown msgbd
;
742 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
743 // TODO generate this
744 msgbd
.rand
= g_strdup("0878F41B");
745 sip
->registrar
.ntlm_num
++;
746 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
747 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
748 if (signature_input_str
!= NULL
) {
749 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
750 msg
->rand
= g_strdup(msgbd
.rand
);
751 msg
->num
= g_strdup(msgbd
.num
);
753 sipmsg_breakdown_free(&msgbd
);
756 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
757 buf
= auth_header_without_newline(sip
, &sip
->registrar
, msg
, FALSE
);
758 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
759 sipmsg_add_header(msg
, "Authorization", buf
);
761 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
762 //sipmsg_add_header_pos(msg, "Authorization", buf, 5);
765 } 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")) {
767 sip
->registrar
.type
=2;
769 buf
= auth_header_without_newline(sip
, &sip
->registrar
, msg
, FALSE
);
770 //buf = auth_header(sip, &sip->proxy, msg, FALSE);
771 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
772 //sipmsg_add_header(msg, "Authorization", buf);
775 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
779 static char *get_contact(struct sipe_account_data
*sip
)
781 return g_strdup(sip
->contact
);
785 static char *get_contact_service(struct sipe_account_data
*sip
)
787 return g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip
->listenport
, TRANSPORT_DESCRIPTOR
, generateUUIDfromEPID(get_epid()));
788 //return g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace", sip->username, sip->listenport, purple_network_get_my_ip(-1), TRANSPORT_DESCRIPTOR);
791 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
792 const char *text
, const char *body
)
796 GString
*outstr
= g_string_new("");
797 struct sipe_account_data
*sip
= gc
->proto_data
;
800 contact
= get_contact(sip
);
801 sipmsg_remove_header(msg
, "Contact");
802 sipmsg_add_header(msg
, "Contact", contact
);
805 /* When sending the acknowlegements and errors, the content length from the original
806 message is still here, but there is no body; we need to make sure we're sending the
807 correct content length */
808 sipmsg_remove_header(msg
, "Content-Length");
811 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
812 sipmsg_add_header(msg
, "Content-Length", len
);
814 sipmsg_add_header(msg
, "Content-Length", "0");
817 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
818 //gchar * mic = "MICTODO";
819 msg
->response
= code
;
821 sipmsg_remove_header(msg
, "Authentication-Info");
822 sign_outgoing_message(msg
, sip
, msg
->method
);
824 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
825 GSList
*tmp
= msg
->headers
;
827 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
828 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
830 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
831 tmp
= g_slist_next(tmp
);
833 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
834 sendout_pkt(gc
, outstr
->str
);
835 g_string_free(outstr
, TRUE
);
838 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
840 if (trans
->msg
) sipmsg_free(trans
->msg
);
841 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
845 static struct transaction
*
846 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
848 struct transaction
*trans
= g_new0(struct transaction
, 1);
849 trans
->time
= time(NULL
);
850 trans
->msg
= (struct sipmsg
*)msg
;
851 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
852 trans
->callback
= callback
;
853 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
857 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
859 struct transaction
*trans
;
860 GSList
*transactions
= sip
->transactions
;
861 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
863 while (transactions
) {
864 trans
= transactions
->data
;
865 if (!strcmp(trans
->cseq
, cseq
)) {
868 transactions
= transactions
->next
;
874 static struct transaction
*
875 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
876 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
877 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
879 struct sipe_account_data
*sip
= gc
->proto_data
;
880 const char *addh
= "";
884 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
885 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
886 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
887 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
888 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
889 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
890 gchar
*route
= strdup("");
892 if (dialog
&& dialog
->routes
)
894 GSList
*iter
= dialog
->routes
;
899 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, iter
->data
);
901 iter
= g_slist_next(iter
);
905 if (!ourtag
&& !dialog
) {
909 if (!strcmp(method
, "REGISTER")) {
910 if (sip
->regcallid
) {
912 callid
= g_strdup(sip
->regcallid
);
914 sip
->regcallid
= g_strdup(callid
);
918 if (addheaders
) addh
= addheaders
;
920 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
921 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
922 "From: <sip:%s>%s%s;epid=%s\r\n"
923 "To: <%s>%s%s%s%s\r\n"
924 "Max-Forwards: 70\r\n"
929 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
931 dialog
&& dialog
->request
? dialog
->request
: url
,
932 TRANSPORT_DESCRIPTOR
,
933 purple_network_get_my_ip(-1),
935 branch
? ";branch=" : "",
936 branch
? branch
: "",
938 ourtag
? ";tag=" : "",
939 ourtag
? ourtag
: "",
940 get_epid(), // TODO generate one per account/login
942 theirtag
? ";tag=" : "",
943 theirtag
? theirtag
: "",
944 theirepid
? ";epid=" : "",
945 theirepid
? theirepid
: "",
946 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
952 body
? strlen(body
) : 0,
956 //printf ("parsing msg buf:\n%s\n\n", buf);
957 msg
= sipmsg_parse_msg(buf
);
967 sign_outgoing_message (msg
, sip
, method
);
969 buf
= sipmsg_to_string (msg
);
971 /* add to ongoing transactions */
972 struct transaction
* trans
= transactions_add_buf(sip
, msg
, tc
);
973 sendout_pkt(gc
, buf
);
978 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
980 gchar
*from
= g_strdup_printf("sip:%s", sip
->username
);
981 gchar
*hdr
= g_strdup("Content-Type: application/SOAP+xml\r\n");
983 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
984 tr
->payload
= payload
;
990 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
992 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
995 static char *get_contact_register(struct sipe_account_data
*sip
)
997 return g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, BYE, CANCEL, NOTIFY, ACK, BENOTIFY\";proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip
->listenport
, TRANSPORT_DESCRIPTOR
, generateUUIDfromEPID(get_epid()));
1000 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1002 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
1003 char *to
= g_strdup_printf("sip:%s", sip
->username
);
1004 char *contact
= get_contact_register(sip
);
1005 //char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire);
1006 // 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);
1007 //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);
1008 //char *hdr = g_strdup_printf("Contact: %s\r\nSupported: com.microsoft.msrtc.presence, gruu-10, adhoclist\r\nEvent: registration\r\nAllow-Events: presence\r\nms-keep-alive: UAC;hop-hop=yes\r\nExpires: %d\r\n", contact,expire);
1009 char *hdr
= g_strdup_printf("Contact: %s\r\n"
1010 "Supported: gruu-10, adhoclist\r\n"
1011 "Event: registration\r\n"
1012 "Allow-Events: presence\r\n"
1013 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1014 "Expires: %d\r\n", contact
,expire
);
1017 sip
->registerstatus
= 1;
1020 sip
->reregister
= time(NULL
) + expire
- 50;
1022 sip
->reregister
= time(NULL
) + 600;
1025 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1026 process_register_response
);
1033 static void do_register(struct sipe_account_data
*sip
)
1035 do_register_exp(sip
, sip
->registerexpire
);
1038 static gchar
*parse_from(const gchar
*hdr
)
1041 const gchar
*tmp
, *tmp2
= hdr
;
1043 if (!hdr
) return NULL
;
1044 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1045 tmp
= strchr(hdr
, '<');
1047 /* i hate the different SIP UA behaviours... */
1048 if (tmp
) { /* sip address in <...> */
1050 tmp
= strchr(tmp2
, '>');
1052 from
= g_strndup(tmp2
, tmp
- tmp2
);
1054 purple_debug_info("sipe", "found < without > in From\n");
1058 tmp
= strchr(tmp2
, ';');
1060 from
= g_strndup(tmp2
, tmp
- tmp2
);
1062 from
= g_strdup(tmp2
);
1065 purple_debug_info("sipe", "got %s\n", from
);
1069 static xmlnode
* xmlnode_get_descendant(xmlnode
* parent
, ...)
1075 va_start(args
, parent
);
1076 while ((name
= va_arg(args
, const char *)) != NULL
) {
1077 node
= xmlnode_get_child(parent
, name
);
1078 if (node
== NULL
) return NULL
;
1087 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1089 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1090 if (!purple_group
) {
1091 purple_group
= purple_group_new(group
->name
);
1092 purple_blist_add_group(purple_group
, NULL
);
1096 group
->purple_group
= purple_group
;
1097 sip
->groups
= g_slist_append(sip
->groups
, group
);
1098 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1100 purple_debug_info("sipe", "did not add group %s\n", group
->name
);
1104 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1110 struct sipe_group
*group
;
1111 GSList
*entry
= sip
->groups
;
1113 group
= entry
->data
;
1114 if (group
->id
== id
) {
1117 entry
= entry
->next
;
1122 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, gchar
* name
)
1128 struct sipe_group
*group
;
1129 GSList
*entry
= sip
->groups
;
1131 group
= entry
->data
;
1132 if (!strcmp(group
->name
, name
)) {
1135 entry
= entry
->next
;
1141 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1143 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1144 gchar
* body
= g_strdup_printf(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->delta_num
++);
1145 send_soap_request(sip
, body
);
1147 g_free(group
->name
);
1148 group
->name
= g_strdup(name
);
1152 sipe_group_set_user (struct sipe_account_data
*sip
, struct sipe_group
* group
, const gchar
* who
)
1154 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1155 PurpleBuddy
* purple_buddy
= purple_find_buddy (sip
->account
, who
);
1158 group
= sipe_group_find_by_id (sip
, buddy
->group_id
);
1160 buddy
->group_id
= group
? group
->id
: 1;
1162 if (buddy
&& purple_buddy
) {
1163 gchar
* alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1164 purple_debug_info("sipe", "Saving buddy %s with alias %s and group_id %d\n", who
, alias
, buddy
->group_id
);
1165 gchar
* body
= g_strdup_printf(SIPE_SOAP_SET_CONTACT
,
1166 alias
, buddy
->group_id
, "true", buddy
->name
, sip
->delta_num
++
1168 send_soap_request(sip
, body
);
1173 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1175 if (msg
->response
== 200) {
1176 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1178 struct group_user_context
* ctx
= (struct group_user_context
*)tc
->payload
;
1179 group
->name
= ctx
->group_name
;
1181 xmlnode
* xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1182 if (!xml
) return FALSE
;
1184 xmlnode
* node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1185 if (!node
) return FALSE
;
1187 char * group_id
= xmlnode_get_data(node
);
1188 if (!group_id
) return FALSE
;
1190 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1192 sipe_group_add(sip
, group
);
1193 sipe_group_set_user(sip
, group
, ctx
->user_name
);
1202 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1204 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1205 ctx
->group_name
= g_strdup(name
);
1206 ctx
->user_name
= g_strdup(who
);
1208 gchar
* body
= g_strdup_printf(SIPE_SOAP_ADD_GROUP
, name
, sip
->delta_num
++);
1209 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1213 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1217 if (msg
->response
== 200 || msg
->response
== 202) {
1221 to
= parse_from(sipmsg_find_header(tc
->msg
, "To")); /* cant be NULL since it is our own msg */
1223 /* we can not subscribe -> user is offline (TODO unknown status?) */
1225 purple_prpl_got_user_status(sip
->account
, to
, "offline", NULL
);
1230 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
)
1232 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1233 gchar
*tmp
= get_contact(sip
);
1234 gchar
*contact
= g_strdup_printf(
1235 "Accept: application/pidf+xml, application/xpidf+xml\r\n"
1236 "Event: presence\r\n"
1237 "Contact: %s\r\n", tmp
);
1240 /* subscribe to buddy presence */
1241 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, contact
, "", NULL
, process_subscribe_response
);
1247 static void sipe_subscribe(struct sipe_account_data
*sip
, struct sipe_buddy
*buddy
)
1249 sipe_subscribe_to_name(sip
, buddy
->name
);
1251 /* resubscribe before subscription expires */
1252 /* add some jitter */
1253 buddy
->resubscribe
= time(NULL
)+1140+(rand()%50);
1256 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1258 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(purple_status_get_type(status
));
1259 struct sipe_account_data
*sip
= NULL
;
1261 if (!purple_status_is_active(status
))
1265 sip
= account
->gc
->proto_data
;
1268 g_free(sip
->status
);
1270 if (primitive
== PURPLE_STATUS_AWAY
) {
1271 sip
->status
= g_strdup("away");
1272 } else if (primitive
== PURPLE_STATUS_AVAILABLE
) {
1273 sip
->status
= g_strdup("available");
1274 } else if (primitive
== PURPLE_STATUS_UNAVAILABLE
) {
1275 sip
->status
= g_strdup("busy");
1278 send_presence_info(sip
);
1283 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
)
1285 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1286 sipe_group_set_user(sip
, NULL
, name
);
1290 sipe_group_buddy(PurpleConnection
*gc
,
1292 const char *old_group_name
,
1293 const char *new_group_name
)
1295 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1296 struct sipe_group
* group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1298 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1300 sipe_group_set_user(sip
, group
, who
);
1304 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1306 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1307 struct sipe_buddy
*b
;
1309 // Prepend sip: if needed
1310 if (strncmp("sip:", buddy
->name
, 4)) {
1311 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
1312 purple_blist_rename_buddy(buddy
, buf
);
1316 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1317 b
= g_new0(struct sipe_buddy
, 1);
1318 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1319 b
->name
= g_strdup(buddy
->name
);
1320 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1322 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1324 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1328 // Not Used; Remove?
1329 /*static void sipe_get_buddies(PurpleConnection *gc)
1331 PurpleBlistNode *gnode, *cnode, *bnode;
1333 purple_debug_info("sipe", "sipe_get_buddies\n");
1335 for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
1336 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue;
1337 for (cnode = gnode->child; cnode; cnode = cnode->next) {
1338 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue;
1339 for (bnode = cnode->child; bnode; bnode = bnode->next) {
1340 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue;
1341 if (((PurpleBuddy*)bnode)->account == gc->account)
1342 sipe_add_buddy(gc, (PurpleBuddy*)bnode, (PurpleGroup *)gnode);
1348 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1350 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1351 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1354 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1357 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->delta_num
++);
1358 send_soap_request(sip
, body
);
1367 sipe_rename_group(PurpleConnection
*gc
,
1368 const char *old_name
,
1370 GList
*moved_buddies
)
1372 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1373 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1375 sipe_group_rename(sip
, s_group
, group
->name
);
1377 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1382 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1384 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1385 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1387 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1388 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->delta_num
++);
1389 send_soap_request(sip
, body
);
1392 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1393 g_free(s_group
->name
);
1395 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1399 static GList
*sipe_status_types(PurpleAccount
*acc
)
1401 PurpleStatusType
*type
;
1402 GList
*types
= NULL
;
1405 type
= purple_status_type_new_with_attrs(
1406 PURPLE_STATUS_AVAILABLE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1407 // Translators: noun
1408 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1410 types
= g_list_append(types
, type
);
1413 type
= purple_status_type_new_with_attrs(
1414 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1415 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1417 types
= g_list_append(types
, type
);
1420 type
= purple_status_type_new_with_attrs(
1421 PURPLE_STATUS_UNAVAILABLE
, "busy", _("Busy"), TRUE
, TRUE
, FALSE
,
1422 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1424 types
= g_list_append(types
, type
);
1427 type
= purple_status_type_new_full(
1428 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1429 types
= g_list_append(types
, type
);
1434 static gboolean
sipe_add_lcs_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1436 int len
= msg
->bodylen
;
1438 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1439 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1443 /* Convert the contact from XML to Purple Buddies */
1444 xmlnode
* isc
= xmlnode_from_str(msg
->body
, len
);
1449 gchar
* delta_num
= g_strdup(xmlnode_get_attrib(isc
, "deltaNum"));
1451 sip
->delta_num
= (int)g_ascii_strtod(delta_num
, NULL
);
1455 xmlnode
*group_node
;
1456 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1457 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1459 group
->name
= g_strdup(xmlnode_get_attrib(group_node
, "name"));
1460 if (!strncmp(group
->name
, "~", 1)){
1462 group
->name
= "General";
1464 group
->name
= g_strdup(group
->name
);
1465 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1467 sipe_group_add(sip
, group
);
1470 // Make sure we have at least one group
1471 if (g_slist_length(sip
->groups
) == 0) {
1472 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1474 group
->name
= g_strdup("General");
1476 PurpleGroup
* purple_group
= purple_group_new(group
->name
);
1477 purple_blist_add_group(purple_group
, NULL
);
1478 sip
->groups
= g_slist_append(sip
->groups
, group
);
1481 /* Parse contacts */
1483 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1484 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
1485 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
1486 gchar
**item_groups
= g_strsplit(xmlnode_get_attrib(item
, "groups"), " ", 0);
1488 struct sipe_group
* group
= NULL
;
1490 // Find the first group this contact belongs to; that's where we'll place it in the buddy list
1491 if (item_groups
[0]) {
1492 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[0], NULL
));
1495 // If couldn't find the right group for this contact, just put them in the first group we have
1496 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1497 group
= sip
->groups
->data
;
1500 if (group
!= NULL
) {
1501 char * buddy_name
= g_strdup_printf("sip:%s", uri
);
1503 //b = purple_find_buddy(sip->account, buddy_name);
1504 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1506 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1510 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1512 if (name
!= NULL
&& strlen(name
) != 0) {
1513 purple_blist_alias_buddy(b
, name
);
1515 purple_blist_alias_buddy(b
, uri
);
1518 struct sipe_buddy
* buddy
= g_new0(struct sipe_buddy
, 1);
1519 buddy
->name
= g_strdup(b
->name
);
1520 buddy
->group_id
= group
->id
;
1521 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
1523 purple_debug_info("sipe", "Added buddy %s to group %s\n", buddy
->name
, group
->name
);
1525 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
1535 static void sipe_subscribe_buddylist(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1537 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1538 gchar
*tmp
= get_contact(sip
);
1539 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-roaming-contacts\r\n"
1540 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
1541 "Supported: com.microsoft.autoextend\r\n"
1542 "Supported: ms-benotify\r\n"
1543 "Proxy-Require: ms-benotify\r\n"
1544 "Supported: ms-piggyback-first-notify\r\n"
1545 "Contact: %s\r\n", tmp
);
1548 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, sipe_add_lcs_contacts
);
1553 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1555 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1556 gchar
*tmp
= get_contact(sip
);
1557 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-roaming-self\r\n"
1558 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
1559 "Supported: com.microsoft.autoextend\r\n"
1560 "Supported: ms-benotify\r\n"
1561 "Proxy-Require: ms-benotify\r\n"
1562 "Supported: ms-piggyback-first-notify\r\n"
1564 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n"
1569 gchar
*body
=g_strdup("<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\"><roaming type=\"categories\"/><roaming type=\"containers\"/><roaming type=\"subscribers\"/></roamingList>");
1571 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, NULL
);
1577 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1579 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1580 gchar
*tmp
= get_contact(sip
);
1581 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-provisioning-v2\r\n"
1582 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
1583 "Supported: com.microsoft.autoextend\r\n"
1584 "Supported: ms-benotify\r\n"
1585 "Proxy-Require: ms-benotify\r\n"
1586 "Supported: ms-piggyback-first-notify\r\n"
1589 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
1594 gchar
*body
=g_strdup("<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\"><provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/><provisioningGroup name=\"ucPolicy\"/></provisioningGroupList>");
1595 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, NULL
);
1601 /* IM Session (INVITE and MESSAGE methods) */
1603 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
1605 if (sip
== NULL
|| who
== NULL
) {
1609 struct sip_im_session
*session
;
1610 GSList
*entry
= sip
->im_sessions
;
1612 session
= entry
->data
;
1613 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
1616 entry
= entry
->next
;
1621 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
1623 struct sip_im_session
*session
= find_im_session(sip
, who
);
1625 session
= g_new0(struct sip_im_session
, 1);
1626 session
->with
= g_strdup(who
);
1627 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
1632 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1634 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
1635 // TODO free session resources
1638 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
1644 if (strncmp("sip:", session
->with
, 4)) {
1645 fullto
= g_strdup_printf("sip:%s", session
->with
);
1647 fullto
= g_strdup(session
->with
);
1650 hdr
= g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1651 //hdr = g_strdup("Content-Type: text/rtf\r\n");
1652 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
1654 tmp
= get_contact(sip
);
1655 hdr
= g_strdup_printf("Contact: %s\r\n%s", tmp
, hdr
);
1658 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msg
, session
->dialog
, NULL
);
1666 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
1668 GSList
*entry
= session
->outgoing_message_queue
;
1670 char *queued_msg
= entry
->data
;
1671 sipe_send_message(sip
, session
, queued_msg
);
1673 // Remove from the queue and free the string
1674 entry
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
1680 sipe_get_route_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
1682 GSList
*hdr
= msg
->headers
;
1683 struct siphdrelement
*elem
;
1689 if(!strcmp(elem
->name
, "Record-Route"))
1691 gchar
*route
= sipmsg_find_part_of_header(elem
->value
, "<", ">", NULL
);
1692 dialog
->routes
= g_slist_append(dialog
->routes
, route
);
1694 hdr
= g_slist_next(hdr
);
1699 dialog
->routes
= g_slist_reverse(dialog
->routes
);
1704 dialog
->request
= dialog
->routes
->data
;
1705 dialog
->routes
= g_slist_remove(dialog
->routes
, dialog
->routes
->data
);
1708 contact
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Contact"), "<", ">", NULL
);
1709 dialog
->routes
= g_slist_append(dialog
->routes
, contact
);
1714 sipe_parse_dialog(struct sipmsg
* msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
1716 gchar
*us
= outgoing
? "From" : "To";
1717 gchar
*them
= outgoing
? "To" : "From";
1719 dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
1720 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, us
));
1721 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, them
));
1722 if (!dialog
->theirepid
) {
1723 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", ";", NULL
);
1725 if (!dialog
->theirepid
) {
1726 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", NULL
, NULL
);
1729 sipe_get_route_header(msg
, dialog
, outgoing
);
1734 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1736 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
1737 struct sip_im_session
* session
= find_im_session(sip
, with
);
1741 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
1745 if (msg
->response
!= 200) {
1746 purple_debug_info("sipe", "process_invite_response: INVITE response not 200, ignoring\n");
1747 im_session_destroy(sip
, session
);
1751 struct sip_dialog
* dialog
= session
->dialog
;
1753 purple_debug_info("sipe", "process_invite_response: session outgoign dialog is NULL\n");
1757 sipe_parse_dialog(msg
, dialog
, TRUE
);
1760 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
1761 session
->outgoing_invite
= NULL
;
1762 sipe_im_process_queue(sip
, session
);
1768 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
* session
, gchar
* msg_body
)
1775 if (session
->dialog
) {
1776 purple_debug_info("sipe", "session with %s already has a dialog open\n", session
->with
);
1780 session
->dialog
= g_new0(struct sip_dialog
, 1);
1782 if (strstr(session
->with
, "sip:")) {
1783 to
= g_strdup(session
->with
);
1785 to
= g_strdup_printf("sip:%s", session
->with
);
1788 char * base64_msg
= purple_base64_encode((guchar
*) msg_body
, strlen(msg_body
));
1789 char * ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, base64_msg
);
1792 contact
= get_contact(sip
);
1793 hdr
= g_strdup_printf(
1795 "Content-Type: application/sdp\r\n",
1796 contact
, ms_text_format
, sip
->username
, sip
->username
, to
);
1797 g_free(ms_text_format
);
1799 body
= g_strdup_printf(
1801 "o=- 0 0 IN IP4 %s\r\n"
1805 "m=message %d sip null\r\n"
1806 "a=accept-types:text/plain text/html image/gif "
1807 "multipart/alternative application/im-iscomposing+xml\r\n",
1808 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
1810 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
1811 to
, to
, hdr
, body
, session
->dialog
, process_invite_response
);
1820 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1823 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->dialog
, NULL
);
1824 im_session_destroy(sip
, session
);
1829 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
1831 struct sipe_account_data
*sip
= gc
->proto_data
;
1833 purple_debug_info("sipe", "conversation with %s closed\n", who
);
1834 im_session_close(sip
, find_im_session(sip
, who
));
1838 im_session_close_all (struct sipe_account_data
*sip
)
1840 GSList
*entry
= sip
->im_sessions
;
1842 im_session_close (sip
, entry
->data
);
1843 entry
= sip
->im_sessions
;
1847 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
1849 struct sipe_account_data
*sip
= gc
->proto_data
;
1850 char *to
= g_strdup(who
);
1851 char *text
= purple_unescape_html(what
);
1853 struct sip_im_session
* session
= find_or_create_im_session(sip
, who
);
1855 // Queue the message
1856 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, text
);
1858 if (session
->dialog
&& session
->dialog
->callid
) {
1859 sipe_im_process_queue(sip
, session
);
1860 } else if (!session
->outgoing_invite
) {
1861 // Need to send the INVITE to get the outgoing dialog setup
1862 sipe_invite(sip
, session
, text
);
1870 /* End IM Session (INVITE and MESSAGE methods) */
1873 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
1875 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1877 if (state
== PURPLE_NOT_TYPING
)
1880 struct sip_im_session
* session
= find_im_session(sip
, who
);
1882 if (session
&& session
->dialog
) {
1883 send_sip_request(gc
, "INFO", who
, who
,
1884 "Content-Type: application/xml\r\n",
1885 SIPE_SEND_TYPING
, session
->dialog
, NULL
);
1888 return SIPE_TYPING_SEND_TIMEOUT
;
1892 static void sipe_buddy_resub(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1894 time_t curtime
= time(NULL
);
1895 if (buddy
->resubscribe
< curtime
) {
1896 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_buddy_resub %s\n", name
);
1897 sipe_subscribe(sip
, buddy
);
1901 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
1903 GSList
*tmp
= sip
->transactions
;
1904 time_t currtime
= time(NULL
);
1906 struct transaction
*trans
= tmp
->data
;
1908 purple_debug_info("sipe", "have open transaction age: %ld\n", currtime
-trans
->time
);
1909 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
1912 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
1914 sendout_sipmsg(sip
, trans
->msg
);
1921 static gboolean
subscribe_timeout(struct sipe_account_data
*sip
)
1924 time_t curtime
= time(NULL
);
1925 /* register again if first registration expires */
1926 if (sip
->reregister
< curtime
) {
1929 /* check for every subscription if we need to resubscribe */
1930 //Fixxxer we need resub?
1931 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_resub
, (gpointer
)sip
);
1933 /* remove a timed out suscriber */
1937 struct sipe_watcher
*watcher
= tmp
->data
;
1938 if (watcher
->expire
< curtime
) {
1939 watcher_remove(sip
, watcher
->name
);
1942 if (tmp
) tmp
= tmp
->next
;
1948 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1952 gboolean found
= FALSE
;
1954 from
= parse_from(sipmsg_find_header(msg
, "From"));
1958 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
1960 contenttype
= sipmsg_find_header(msg
, "Content-Type");
1961 if (!contenttype
|| !strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
1962 serv_got_im(sip
->gc
, from
, msg
->body
, 0, time(NULL
));
1963 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1966 if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
1967 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1972 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1976 state
= xmlnode_get_child(isc
, "state");
1979 purple_debug_info("sipe", "process_incoming_message: no state found\n");
1984 statedata
= xmlnode_get_data(state
);
1986 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
1987 else serv_got_typing_stopped(sip
->gc
, from
);
1992 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1996 purple_debug_info("sipe", "got unknown mime-type");
1997 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
2002 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2004 // Only accept text invitations
2005 if (msg
->body
&& !strstr(msg
->body
, "m=message")) {
2006 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
2010 gchar
* from
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "<", ">", NULL
);
2011 struct sip_im_session
* session
= find_or_create_im_session (sip
, from
);
2013 if (session
->dialog
) {
2014 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
2016 session
->dialog
= g_new0(struct sip_dialog
, 1);
2018 sipe_parse_dialog(msg
, session
->dialog
, FALSE
);
2020 session
->dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
2021 session
->dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
2022 session
->dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
2023 session
->dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
2026 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
2030 send_sip_response(sip
->gc
, msg
, 200, "OK", g_strdup_printf(
2032 "o=- 0 0 IN IP4 %s\r\n"
2036 "m=message %d sip sip:%s\r\n"
2037 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
2038 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
2039 sip
->realport
, sip
->username
));
2042 static void sipe_connection_cleanup(struct sipe_account_data
*);
2043 static void create_connection(struct sipe_account_data
*, gchar
*, int);
2045 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2047 gchar
*tmp
, krb5_token
;
2048 const gchar
*expires_header
;
2051 expires_header
= sipmsg_find_header(msg
, "Expires");
2052 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
2053 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
2055 switch (msg
->response
) {
2058 sip
->registerstatus
= 0;
2060 sip
->registerstatus
= 3;
2061 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
2063 gchar
*gruu
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Contact"), "gruu=\"", "\"", NULL
);
2065 sip
->contact
= g_strdup_printf("<%s>", gruu
);
2067 sip
->contact
= g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace", sip
->username
, sip
->listenport
, purple_network_get_my_ip(-1), TRANSPORT_DESCRIPTOR
);
2070 /* get buddies from blist; Has a bug */
2071 subscribe_timeout(sip
);
2073 tmp
= sipmsg_find_header(msg
, "Allow-Events");
2074 if (tmp
&& strstr(tmp
, "vnd-microsoft-provisioning")){
2075 sipe_subscribe_buddylist(sip
, msg
);
2078 sipe_subscribe_roaming_self(sip
, msg
);
2079 sipe_subscribe_roaming_provisioning(sip
, msg
);
2080 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
2082 // Should we remove the transaction here?
2083 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
2084 transactions_remove(sip
, tc
);
2089 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
2091 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
2092 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
2096 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
2099 tmp
= g_strsplit(parts
[0], ":", 0);
2100 hostname
= g_strdup(tmp
[0]);
2101 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
2105 tmp
= g_strsplit(parts
[i
], "=", 0);
2107 if (g_strcasecmp("transport", tmp
[0]) == 0) {
2108 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
2109 transport
= SIPE_TRANSPORT_TCP
;
2110 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
2111 transport
= SIPE_TRANSPORT_UDP
;
2120 /* Close old connection */
2121 sipe_connection_cleanup(sip
);
2123 /* Create new connection */
2124 sip
->transport
= transport
;
2125 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
2126 hostname
, port
, TRANSPORT_DESCRIPTOR
);
2127 create_connection(sip
, hostname
, port
);
2132 if (sip
->registerstatus
!= 2) {
2133 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
2134 if (sip
->registrar
.retries
> 3) {
2135 sip
->gc
->wants_to_die
= TRUE
;
2136 purple_connection_error(sip
->gc
, _("Wrong Password"));
2139 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
2140 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
2142 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
2144 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
2145 fill_auth(sip
, tmp
, &sip
->registrar
);
2146 sip
->registerstatus
= 2;
2147 if (sip
->account
->disconnecting
) {
2148 do_register_exp(sip
, 0);
2156 const gchar
*warning
= sipmsg_find_header(msg
, "Warning");
2157 if (warning
!= NULL
) {
2159 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
2161 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
2162 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
2165 warning
= _("You have been rejected by the server");
2168 sip
->gc
->wants_to_die
= TRUE
;
2169 purple_connection_error(sip
->gc
, warning
);
2175 const gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2176 if (warning
!= NULL
) {
2177 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2178 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
2181 warning
= _("Service unavailable: no reason given");
2184 sip
->gc
->wants_to_die
= TRUE
;
2185 purple_connection_error(sip
->gc
, warning
);
2193 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2198 gchar
*activity
= g_strdup("available");
2200 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
2201 gboolean isonline
= FALSE
;
2203 fromhdr
= sipmsg_find_header(msg
, "From");
2204 from
= parse_from(fromhdr
);
2210 pidf
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2212 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
2216 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
2217 if ((status
= xmlnode_get_child(tuple
, "status"))) {
2218 basicstatus
= xmlnode_get_child(status
, "basic");
2223 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
2228 tmp2
= xmlnode_get_data(basicstatus
);
2230 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
2235 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", tmp2
);
2236 if (strstr(tmp2
, "open")) {
2240 xmlnode
*display_name_node
= xmlnode_get_child(pidf
, "display-name");
2241 if (display_name_node
) {
2242 PurpleBuddy
* buddy
= purple_find_buddy (sip
->account
, from
);
2243 char * display_name
= xmlnode_get_data(display_name_node
);
2244 if (buddy
&& display_name
) {
2245 purple_blist_server_alias_buddy (buddy
, g_strdup(display_name
));
2249 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
2250 if ((status
= xmlnode_get_child(tuple
, "status"))) {
2251 if (basicstatus
= xmlnode_get_child(status
, "activities")) {
2252 if (basicstatus
= xmlnode_get_child(basicstatus
, "activity")) {
2253 activity
= xmlnode_get_data(basicstatus
);
2258 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
2261 gchar
* status_id
= NULL
;
2263 if (strstr(activity
, "busy")) {
2265 } else if (strstr(activity
, "away")) {
2271 status_id
= "available";
2274 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
2275 purple_prpl_got_user_status(sip
->account
, from
, status_id
, NULL
);
2277 purple_prpl_got_user_status(sip
->account
, from
, "offline", NULL
);
2285 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2288 static void process_incoming_benotify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2290 xmlnode
*xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2293 gchar
* delta_num
= g_strdup(xmlnode_get_attrib(xml
, "deltaNum"));
2295 sip
->delta_num
= (int)g_ascii_strtod(delta_num
, NULL
);
2302 static gchar
* gen_xpidf(struct sipe_account_data
*sip
)
2304 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
2306 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
2307 "<display name=\"sip:%s\"/>\r\n"
2308 "<atom id=\"1234\">\r\n"
2309 "<address uri=\"sip:%s\">\r\n"
2310 "<status status=\"%s\"/>\r\n"
2323 static gchar
* gen_pidf(struct sipe_account_data
*sip
)
2325 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
2326 "<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"
2327 "<tuple id=\"0\">\r\n"
2329 "<basic>open</basic>\r\n"
2330 "<ep:activities>\r\n"
2331 " <ep:activity>%s</ep:activity>\r\n"
2335 "<ci:display-name>%s</ci:display-name>\r\n"
2343 static void send_notify(struct sipe_account_data
*sip
, struct sipe_watcher
*watcher
)
2345 gchar
*doc
= watcher
->needsxpidf
? gen_xpidf(sip
) : gen_pidf(sip
);
2346 gchar
*hdr
= watcher
->needsxpidf
? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
2347 send_sip_request(sip
->gc
, "NOTIFY", watcher
->name
, watcher
->name
, hdr
, doc
, &watcher
->dialog
, NULL
);
2351 static gboolean
process_service_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2354 if (msg
->response
!= 200 && msg
->response
!= 408) {
2355 /* never send again */
2356 sip
->republish
= -1;
2361 static void send_clear_notes(struct sipe_account_data
*sip
)
2366 process_send_presence_info_v0_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2368 if (msg
->response
== 488) {
2369 sip
->presence_method_version
= 1;
2370 send_presence_info(sip
);
2375 static void send_presence_info_v0(struct sipe_account_data
*sip
, char * note
)
2378 if (!strcmp(sip
->status
, "away")) {
2380 } else if (!strcmp(sip
->status
, "busy")) {
2387 gchar
*name
= g_strdup_printf("sip: sip:%s", sip
->username
);
2388 gchar
* body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
, name
, 200, code
, note
);
2389 send_soap_request_with_cb(sip
, body
, process_send_presence_info_v0_response
, NULL
);
2395 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2397 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
2398 if (msg
->response
== 200) {
2399 sip
->status_version
= 0;
2400 send_presence_info(sip
);
2406 process_send_presence_info_v1_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2408 if (msg
->response
== 409) {
2409 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
2410 // TODO need to parse the version #'s?
2411 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
2412 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
2414 gchar
*tmp
= get_contact(sip
);
2415 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
2416 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
2418 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
2428 static void send_presence_info_v1(struct sipe_account_data
*sip
, char * note
)
2431 if (!strcmp(sip
->status
, "away")) {
2433 } else if (!strcmp(sip
->status
, "busy")) {
2440 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
2441 gchar
*doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
2442 sip
->status_version
, code
,
2443 sip
->status_version
, code
,
2444 sip
->status_version
, note
? note
: "",
2445 sip
->status_version
, note
? note
: "",
2446 sip
->status_version
, note
? note
: ""
2448 sip
->status_version
++;
2450 gchar
*tmp
= get_contact(sip
);
2451 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
2452 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
2454 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_info_v1_response
);
2462 static void send_presence_info(struct sipe_account_data
*sip
)
2464 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
2465 if (!status
) return;
2467 gchar
*note
= g_strdup(purple_status_get_attr_string(status
, "message"));
2469 purple_debug_info("sipe", "sending presence info, version = %d\n", sip
->presence_method_version
);
2470 if (sip
->presence_method_version
!= 1) {
2471 send_presence_info_v0(sip
, note
);
2473 send_presence_info_v1(sip
, note
);
2477 static void send_service(struct sipe_account_data
*sip
)
2479 //gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->sipdomain);
2480 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
2481 //gchar *doc = gen_pidf(sip);
2483 gchar
*doc
= gen_pidf(sip
);
2484 gchar
*hdr
= g_strdup("Event: presence\r\nContent-Type: application/pidf+xml\r\n");
2486 //gchar *hdr = g_strdup("Content-Type: application/SOAP+xml\r\n");
2487 gchar
*tmp
= get_contact(sip
);
2488 hdr
= g_strdup_printf("Contact: %s\r\n%s; +sip.instance=\"<urn:uuid:%s>\"", tmp
, hdr
,generateUUIDfromEPID(get_epid()));
2490 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
,
2492 doc
, NULL
, process_service_response
);
2493 sip
->republish
= time(NULL
) + 500;
2499 static void process_incoming_subscribe(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2501 const char *from_hdr
= sipmsg_find_header(msg
, "From");
2502 gchar
*from
= parse_from(from_hdr
);
2503 gchar
*theirtag
= find_tag(from_hdr
);
2504 gchar
*ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
2505 gboolean tagadded
= FALSE
;
2506 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2507 gchar
*expire
= sipmsg_find_header(msg
, "Expire");
2509 struct sipe_watcher
*watcher
= watcher_find(sip
, from
);
2514 if (!watcher
) { /* new subscription */
2515 gchar
*acceptheader
= sipmsg_find_header(msg
, "Accept");
2516 gboolean needsxpidf
= FALSE
;
2517 if (!purple_privacy_check(sip
->account
, from
)) {
2518 send_sip_response(sip
->gc
, msg
, 202, "Ok", NULL
);
2522 gchar
*tmp
= acceptheader
;
2523 gboolean foundpidf
= FALSE
;
2524 gboolean foundxpidf
= FALSE
;
2525 while (tmp
&& tmp
< acceptheader
+ strlen(acceptheader
)) {
2526 gchar
*tmp2
= strchr(tmp
, ',');
2527 if (tmp2
) *tmp2
= '\0';
2528 if (!strcmp("application/pidf+xml", tmp
))
2530 if (!strcmp("application/xpidf+xml", tmp
))
2535 while (*tmp
== ' ') tmp
++;
2539 if (!foundpidf
&& foundxpidf
) needsxpidf
= TRUE
;
2540 g_free(acceptheader
);
2542 watcher
= watcher_create(sip
, from
, callid
, ourtag
, theirtag
, needsxpidf
);
2545 gchar
*to
= g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg
, "To"), ourtag
);
2546 sipmsg_remove_header(msg
, "To");
2547 sipmsg_add_header(msg
, "To", to
);
2551 watcher
->expire
= time(NULL
) + strtol(expire
, NULL
, 10);
2553 watcher
->expire
= time(NULL
) + 600;
2555 sipmsg_remove_header(msg
, "Contact");
2556 tmp
= get_contact(sip
);
2557 sipmsg_add_header(msg
, "Contact", tmp
);
2559 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
);
2560 send_sip_response(sip
->gc
, msg
, 200, "Ok", NULL
);
2561 send_notify(sip
, watcher
);
2570 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2572 gboolean found
= FALSE
;
2573 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
2574 if (msg
->response
== 0) { /* request */
2575 if (!strcmp(msg
->method
, "MESSAGE")) {
2576 process_incoming_message(sip
, msg
);
2578 } else if (!strcmp(msg
->method
, "NOTIFY")) {
2579 purple_debug_info("sipe","send->process_incoming_notify\n");
2580 process_incoming_notify(sip
, msg
);
2582 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
2583 purple_debug_info("sipe","send->process_incoming_benotify\n");
2584 process_incoming_benotify(sip
, msg
);
2586 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
2587 purple_debug_info("sipe","send->process_incoming_subscribe\n");
2588 process_incoming_subscribe(sip
, msg
);
2590 } else if (!strcmp(msg
->method
, "INVITE")) {
2591 process_incoming_invite(sip
, msg
);
2593 } else if (!strcmp(msg
->method
, "INFO")) {
2595 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
2597 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
2599 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2601 } else if (!strcmp(msg
->method
, "ACK")) {
2602 // ACK's don't need any response
2604 } else if (!strcmp(msg
->method
, "BYE")) {
2605 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2607 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
2608 struct sip_im_session
* session
= find_im_session (sip
, from
);
2612 // TODO Let the user know the other user left the conversation?
2613 im_session_destroy(sip
, session
);
2618 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
2620 } else { /* response */
2621 struct transaction
*trans
= transactions_find(sip
, msg
);
2623 if (msg
->response
== 407) {
2624 gchar
*resend
, *auth
, *ptmp
;
2626 if (sip
->proxy
.retries
> 30) return;
2627 sip
->proxy
.retries
++;
2628 /* do proxy authentication */
2630 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
2632 fill_auth(sip
, ptmp
, &sip
->proxy
);
2633 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
, TRUE
);
2634 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
2635 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
2637 resend
= sipmsg_to_string(trans
->msg
);
2638 /* resend request */
2639 sendout_pkt(sip
->gc
, resend
);
2642 if (msg
->response
== 100 || msg
->response
== 180) {
2643 /* ignore provisional response */
2644 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
2646 sip
->proxy
.retries
= 0;
2647 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
2648 if (msg
->response
== 401) sip
->registrar
.retries
++;
2649 else sip
->registrar
.retries
= 0;
2650 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
2652 if (msg
->response
== 401) {
2653 gchar
*resend
, *auth
, *ptmp
;
2655 if (sip
->registrar
.retries
> 4) return;
2656 sip
->registrar
.retries
++;
2658 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
2659 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
2661 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
2664 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
2666 fill_auth(sip
, ptmp
, &sip
->registrar
);
2667 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
, TRUE
);
2668 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
2669 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
2671 //sipmsg_remove_header(trans->msg, "Authorization");
2672 //sipmsg_add_header(trans->msg, "Authorization", auth);
2674 resend
= sipmsg_to_string(trans
->msg
);
2675 /* resend request */
2676 sendout_pkt(sip
->gc
, resend
);
2681 if (trans
->callback
) {
2682 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
2683 /* call the callback to process response*/
2684 (trans
->callback
)(sip
, msg
, trans
);
2686 /* Not sure if this is needed or what needs to be done
2687 but transactions seem to be removed prematurely so
2688 this only removes them if the response is 200 OK */
2689 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
2690 /*Has a bug and it's unneccesary*/
2691 /*transactions_remove(sip, trans);*/
2697 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction");
2701 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
2705 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
2713 /* according to the RFC remove CRLF at the beginning */
2714 while (*cur
== '\r' || *cur
== '\n') {
2717 if (cur
!= conn
->inbuf
) {
2718 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
2719 conn
->inbufused
= strlen(conn
->inbuf
);
2722 /* Received a full Header? */
2723 while ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
) {
2724 time_t currtime
= time(NULL
);
2727 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
2728 msg
= sipmsg_parse_header(conn
->inbuf
);
2731 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
2732 if (restlen
>= msg
->bodylen
) {
2733 dummy
= g_malloc(msg
->bodylen
+ 1);
2734 memcpy(dummy
, cur
, msg
->bodylen
);
2735 dummy
[msg
->bodylen
] = '\0';
2737 cur
+= msg
->bodylen
;
2738 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
2739 conn
->inbufused
= strlen(conn
->inbuf
);
2741 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
2742 restlen
, msg
->bodylen
, strlen(conn
->inbuf
));
2748 purple_debug_info("sipe", "body:\n%s", msg
->body
);
2751 // Verify the signature before processing it
2752 if (sip
->registrar
.ntlm_key
) {
2753 struct sipmsg_breakdown msgbd
;
2755 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
2756 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
2758 if (signature_input_str
!= NULL
) {
2759 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
2762 gchar
* rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
2764 if (signature
!= NULL
) {
2765 if (rspauth
!= NULL
) {
2766 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
2767 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
2768 process_input_message(sip
, msg
);
2770 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
2771 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
2772 sip
->gc
->wants_to_die
= TRUE
;
2774 } else if (msg
->response
== 401) {
2775 purple_connection_error(sip
->gc
, _("Wrong Password"));
2776 sip
->gc
->wants_to_die
= TRUE
;
2780 sipmsg_breakdown_free(&msgbd
);
2782 process_input_message(sip
, msg
);
2787 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
2789 PurpleConnection
*gc
= data
;
2790 struct sipe_account_data
*sip
= gc
->proto_data
;
2795 static char buffer
[65536];
2796 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
2798 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
2799 msg
= sipmsg_parse_msg(buffer
);
2800 if (msg
) process_input_message(sip
, msg
);
2804 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
2806 PurpleConnection
*gc
= data
;
2807 struct sipe_account_data
*sip
= gc
->proto_data
;
2808 struct sip_connection
*conn
= NULL
;
2810 gboolean firstread
= TRUE
;
2812 /* TODO: It should be possible to make this check unnecessary */
2813 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
2814 purple_ssl_close(gsc
);
2819 conn
= connection_find(sip
, gsc
->fd
);
2821 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
2822 if (sip
->fd
!= -1) purple_ssl_close(gsc
);
2823 purple_connection_error(sip
->gc
, _("Connection not found; Please try to connect again.\n"));
2824 sip
->gc
->wants_to_die
= TRUE
;
2828 /* Read all available data from the SSL connection */
2830 /* Increase input buffer size as needed */
2831 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
2832 conn
->inbuflen
+= SIMPLE_BUF_INC
;
2833 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
2834 purple_debug_error("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
2837 /* Try to read as much as there is space left in the buffer */
2838 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
2839 len
= purple_ssl_read(sip
->gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
2841 if (len
< 0 && errno
== EAGAIN
) {
2842 /* Try again later */
2844 } else if (len
< 0) {
2845 purple_debug_info("sipe", "sipe_input_cb_ssl: read error\n");
2847 connection_remove(sip
, sip
->gsc
->fd
);
2848 if (sip
->fd
== gsc
->fd
) sip
->fd
= -1;
2849 purple_debug_info("sipe", "sipe_input_cb_ssl: connection_remove\n");
2852 } else if (firstread
&& (len
== 0)) {
2853 purple_connection_error(gc
, _("Server has disconnected"));
2855 connection_remove(sip
, sip
->gsc
->fd
);
2856 if (sip
->fd
== gsc
->fd
) sip
->fd
= -1;
2861 conn
->inbufused
+= len
;
2864 /* Equivalence indicates that there is possibly more data to read */
2865 } while (len
== readlen
);
2867 conn
->inbuf
[conn
->inbufused
] = '\0';
2868 process_input(sip
, conn
);
2872 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
2874 PurpleConnection
*gc
= data
;
2875 struct sipe_account_data
*sip
= gc
->proto_data
;
2877 struct sip_connection
*conn
= connection_find(sip
, source
);
2879 purple_debug_error("sipe", "Connection not found!\n");
2883 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
2884 conn
->inbuflen
+= SIMPLE_BUF_INC
;
2885 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
2888 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
2890 if (len
< 0 && errno
== EAGAIN
)
2892 else if (len
<= 0) {
2893 purple_debug_info("sipe", "sipe_input_cb: read error\n");
2894 connection_remove(sip
, source
);
2895 if (sip
->fd
== source
) sip
->fd
= -1;
2899 conn
->inbufused
+= len
;
2900 conn
->inbuf
[conn
->inbufused
] = '\0';
2902 process_input(sip
, conn
);
2905 /* Callback for new connections on incoming TCP port */
2906 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
2908 PurpleConnection
*gc
= data
;
2909 struct sipe_account_data
*sip
= gc
->proto_data
;
2910 struct sip_connection
*conn
;
2912 int newfd
= accept(source
, NULL
, NULL
);
2914 conn
= connection_create(sip
, newfd
);
2916 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2919 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
2921 PurpleConnection
*gc
= data
;
2922 struct sipe_account_data
*sip
;
2923 struct sip_connection
*conn
;
2925 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2933 purple_connection_error(gc
, _("Could not connect"));
2937 sip
= gc
->proto_data
;
2940 conn
= connection_create(sip
, source
);
2942 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2946 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2949 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
2951 PurpleConnection
*gc
= data
;
2952 struct sipe_account_data
*sip
;
2953 struct sip_connection
*conn
;
2955 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2957 if (gsc
) purple_ssl_close(gsc
);
2961 sip
= gc
->proto_data
;
2964 conn
= connection_create(sip
, sip
->fd
);
2965 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
2966 sip
->listenfd
= sip
->fd
;
2967 sip
->registertimeout
= purple_timeout_add((rand()%100) + 1000, (GSourceFunc
)subscribe_timeout
, sip
);
2971 purple_ssl_input_add(sip
->gsc
, sipe_input_cb_ssl
, gc
);
2974 static guint
sipe_ht_hash_nick(const char *nick
)
2976 char *lc
= g_utf8_strdown(nick
, -1);
2977 guint bucket
= g_str_hash(lc
);
2983 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
2985 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
2988 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
2990 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2992 sip
->listen_data
= NULL
;
2994 if (listenfd
== -1) {
2995 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
3001 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
3002 sip
->listenfd
= sip
->fd
;
3004 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
3006 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
3007 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
3011 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
3013 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
3016 sip
->query_data
= NULL
;
3018 if (!hosts
|| !hosts
->data
) {
3019 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
3023 addr_size
= GPOINTER_TO_INT(hosts
->data
);
3024 hosts
= g_slist_remove(hosts
, hosts
->data
);
3025 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
3026 g_free(hosts
->data
);
3027 hosts
= g_slist_remove(hosts
, hosts
->data
);
3029 hosts
= g_slist_remove(hosts
, hosts
->data
);
3030 g_free(hosts
->data
);
3031 hosts
= g_slist_remove(hosts
, hosts
->data
);
3034 /* create socket for incoming connections */
3035 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
3036 sipe_udp_host_resolved_listen_cb
, sip
);
3037 if (sip
->listen_data
== NULL
) {
3038 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
3043 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
3046 PurpleConnection
*gc
= data
;
3047 struct sipe_account_data
*sip
;
3049 /* If the connection is already disconnected, we don't need to do anything else */
3050 if (!PURPLE_CONNECTION_IS_VALID(gc
))
3053 sip
= gc
->proto_data
;
3057 case PURPLE_SSL_CONNECT_FAILED
:
3058 purple_connection_error(gc
, _("Connection Failed"));
3060 case PURPLE_SSL_HANDSHAKE_FAILED
:
3061 purple_connection_error(gc
, _("SSL Handshake Failed"));
3067 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
3069 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
3070 PurpleProxyConnectData
*connect_data
;
3072 sip
->listen_data
= NULL
;
3074 sip
->listenfd
= listenfd
;
3075 if (sip
->listenfd
== -1) {
3076 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
3080 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
3081 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
3082 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
3083 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
3084 sipe_newconn_cb
, sip
->gc
);
3085 purple_debug_info("sipe", "connecting to %s port %d\n",
3086 sip
->realhostname
, sip
->realport
);
3087 /* open tcp connection to the server */
3088 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
3089 sip
->realport
, login_cb
, sip
->gc
);
3091 if (connect_data
== NULL
) {
3092 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
3097 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
3099 PurpleAccount
*account
= sip
->account
;
3100 PurpleConnection
*gc
= sip
->gc
;
3102 if (purple_account_get_bool(account
, "useport", FALSE
)) {
3103 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
3104 port
= purple_account_get_int(account
, "port", 0);
3106 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
3109 sip
->realhostname
= hostname
;
3110 sip
->realport
= port
;
3112 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
3115 /* TODO: is there a good default grow size? */
3116 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
3117 sip
->txbuf
= purple_circ_buffer_new(0);
3119 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
3121 if (!purple_ssl_is_supported()) {
3122 gc
->wants_to_die
= TRUE
;
3123 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
3127 purple_debug_info("sipe", "using SSL\n");
3129 /*Â Redirect: delete old connction first */
3131 purple_debug_info("sipe", "redirect-close-old gsc %08x gsc/fd %d sip %08x sip/fd %d\n", sip
->gsc
, sip
->gsc
->fd
, sip
, sip
->fd
);
3132 purple_ssl_close(sip
->gsc
);
3135 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
3136 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
3137 if (sip
->gsc
== NULL
) {
3138 purple_connection_error(gc
, _("Could not create SSL context"));
3141 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
3143 purple_debug_info("sipe", "using UDP\n");
3145 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
3146 if (sip
->query_data
== NULL
) {
3147 purple_connection_error(gc
, _("Could not resolve hostname"));
3151 purple_debug_info("sipe", "using TCP\n");
3152 /* create socket for incoming connections */
3153 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
3154 sipe_tcp_connect_listen_cb
, sip
);
3155 if (sip
->listen_data
== NULL
) {
3156 purple_connection_error(gc
, _("Could not create listen socket"));
3162 /* Service list for autodection */
3163 static const struct sipe_service_data service_autodetect
[] = {
3164 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
3165 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
3166 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
3167 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
3171 /* Service list for SSL/TLS */
3172 static const struct sipe_service_data service_tls
[] = {
3173 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
3174 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
3178 /* Service list for TCP */
3179 static const struct sipe_service_data service_tcp
[] = {
3180 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
3181 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
3185 /* Service list for UDP */
3186 static const struct sipe_service_data service_udp
[] = {
3187 { "sip", "udp", SIPE_TRANSPORT_UDP
},
3191 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
3192 static void resolve_next_service(struct sipe_account_data
*sip
,
3193 const struct sipe_service_data
*start
)
3196 sip
->service_data
= start
;
3198 sip
->service_data
++;
3199 if (sip
->service_data
->service
== NULL
) {
3200 /* Try connecting to the SIP hostname directly */
3201 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
3202 // If SSL is supported, default to using it; OCS servers aren't configured
3203 // by default to accept TCP
3204 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
3205 gchar
* hostname
= g_strdup(sip
->sipdomain
);
3206 create_connection(sip
, hostname
, 0);
3211 /* Try to resolve next service */
3212 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
3213 sip
->service_data
->transport
,
3218 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
3220 struct sipe_account_data
*sip
= data
;
3222 sip
->srv_query_data
= NULL
;
3224 /* find the host to connect to */
3226 gchar
*hostname
= g_strdup(resp
->hostname
);
3227 int port
= resp
->port
;
3228 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
3232 sip
->transport
= sip
->service_data
->type
;
3234 create_connection(sip
, hostname
, port
);
3236 resolve_next_service(sip
, NULL
);
3240 static void sipe_login(PurpleAccount
*account
)
3242 PurpleConnection
*gc
;
3243 struct sipe_account_data
*sip
;
3245 const char *transport
;
3247 const char *username
= purple_account_get_username(account
);
3248 gc
= purple_account_get_connection(account
);
3250 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
3251 gc
->wants_to_die
= TRUE
;
3252 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
3256 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
3258 sip
->account
= account
;
3259 sip
->registerexpire
= 900;
3261 userserver
= g_strsplit(username
, "@", 2);
3262 purple_connection_set_display_name(gc
, userserver
[0]);
3263 sip
->username
= g_strdup(g_strjoin("@", userserver
[0], userserver
[1], NULL
));
3264 sip
->sipdomain
= g_strdup(userserver
[1]);
3265 sip
->password
= g_strdup(purple_connection_get_password(gc
));
3266 g_strfreev(userserver
);
3268 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
3270 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
3272 /* TODO: Set the status correctly. */
3273 sip
->status
= g_strdup("available");
3275 transport
= purple_account_get_string(account
, "transport", "auto");
3277 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
3278 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
3279 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
3280 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
3282 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
3283 } else if (strcmp(transport
, "auto") == 0) {
3284 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
3285 } else if (strcmp(transport
, "tls") == 0) {
3286 resolve_next_service(sip
, service_tls
);
3287 } else if (strcmp(transport
, "tcp") == 0) {
3288 resolve_next_service(sip
, service_tcp
);
3290 resolve_next_service(sip
, service_udp
);
3294 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
3296 connection_free_all(sip
);
3298 if (sip
->query_data
!= NULL
)
3299 purple_dnsquery_destroy(sip
->query_data
);
3300 sip
->query_data
== NULL
;
3302 if (sip
->srv_query_data
!= NULL
)
3303 purple_srv_cancel(sip
->srv_query_data
);
3304 sip
->srv_query_data
= NULL
;
3306 if (sip
->listen_data
!= NULL
)
3307 purple_network_listen_cancel(sip
->listen_data
);
3308 sip
->listen_data
= NULL
;
3310 g_free(sip
->registrar
.nonce
);
3311 sip
->registrar
.nonce
= NULL
;
3312 g_free(sip
->registrar
.opaque
);
3313 sip
->registrar
.opaque
= NULL
;
3314 g_free(sip
->registrar
.realm
);
3315 sip
->registrar
.realm
= NULL
;
3316 g_free(sip
->registrar
.target
);
3317 sip
->registrar
.target
= NULL
;
3318 g_free(sip
->registrar
.digest_session_key
);
3319 sip
->registrar
.digest_session_key
= NULL
;
3320 g_free(sip
->registrar
.ntlm_key
);
3321 sip
->registrar
.ntlm_key
= NULL
;
3322 sip
->registrar
.type
= 0;
3323 sip
->registrar
.retries
= 0;
3325 g_free(sip
->proxy
.nonce
);
3326 sip
->proxy
.nonce
= NULL
;
3327 g_free(sip
->proxy
.opaque
);
3328 sip
->proxy
.opaque
= NULL
;
3329 g_free(sip
->proxy
.realm
);
3330 sip
->proxy
.realm
= NULL
;
3331 g_free(sip
->proxy
.target
);
3332 sip
->proxy
.target
= NULL
;
3333 g_free(sip
->proxy
.digest_session_key
);
3334 sip
->proxy
.digest_session_key
= NULL
;
3335 g_free(sip
->proxy
.ntlm_key
);
3336 sip
->proxy
.ntlm_key
= NULL
;
3337 sip
->proxy
.type
= 0;
3338 sip
->proxy
.retries
= 0;
3341 purple_circ_buffer_destroy(sip
->txbuf
);
3344 g_free(sip
->realhostname
);
3345 sip
->realhostname
= NULL
;
3348 purple_input_remove(sip
->listenpa
);
3350 if (sip
->tx_handler
)
3351 purple_input_remove(sip
->tx_handler
);
3352 sip
->tx_handler
= 0;
3353 if (sip
->resendtimeout
)
3354 purple_timeout_remove(sip
->resendtimeout
);
3355 sip
->resendtimeout
= 0;
3356 if (sip
->registertimeout
)
3357 purple_timeout_remove(sip
->registertimeout
);
3358 sip
->registertimeout
= 0;
3361 static void sipe_close(PurpleConnection
*gc
)
3363 struct sipe_account_data
*sip
= gc
->proto_data
;
3366 /* leave all conversations */
3367 im_session_close_all(sip
);
3370 do_register_exp(sip
, 0);
3372 sipe_connection_cleanup(sip
);
3373 g_free(sip
->sipdomain
);
3374 g_free(sip
->username
);
3375 g_free(sip
->password
);
3377 g_free(gc
->proto_data
);
3378 gc
->proto_data
= NULL
;
3381 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
3383 PurpleAccount
*acct
= purple_connection_get_account(gc
);
3384 char *id
= g_strdup_printf("sip:%s", g_list_nth_data(row
, 0));
3385 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
3387 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
3388 purple_conversation_present(conv
);
3392 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
3395 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
3396 g_list_nth_data(row
, 0), NULL
, g_list_nth_data(row
, 1));
3399 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,struct transaction
*tc
)
3401 PurpleNotifySearchResults
*results
;
3402 PurpleNotifySearchColumn
*column
;
3403 xmlnode
*searchResults
;
3406 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3407 if (!searchResults
) {
3408 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
3412 results
= purple_notify_searchresults_new();
3414 if (results
== NULL
) {
3415 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
3416 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
3418 xmlnode_free(searchResults
);
3422 column
= purple_notify_searchresults_column_new(_("User Name"));
3423 purple_notify_searchresults_column_add(results
, column
);
3425 column
= purple_notify_searchresults_column_new(_("Name"));
3426 purple_notify_searchresults_column_add(results
, column
);
3428 column
= purple_notify_searchresults_column_new(_("Company"));
3429 purple_notify_searchresults_column_add(results
, column
);
3431 column
= purple_notify_searchresults_column_new(_("Country"));
3432 purple_notify_searchresults_column_add(results
, column
);
3434 column
= purple_notify_searchresults_column_new(_("Email"));
3435 purple_notify_searchresults_column_add(results
, column
);
3437 int match_count
= 0;
3438 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
3441 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
3442 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
3443 g_strfreev(uri_parts
);
3445 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
3446 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
3447 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
3448 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
3450 purple_notify_searchresults_row_add(results
, row
);
3454 gboolean more
= FALSE
;
3455 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
3456 char *data
= xmlnode_get_data_unescaped(mrow
);
3457 more
= (g_strcasecmp(data
, "true") == 0);
3461 gchar
*secondary
= g_strdup_printf(
3462 dngettext(GETTEXT_PACKAGE
,
3463 "Found %d contact%s:",
3464 "Found %d contacts%s:", match_count
),
3465 match_count
, more
? _(" (more matched your query)") : "");
3467 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
3468 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
3469 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
3472 xmlnode_free(searchResults
);
3476 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
3478 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
3479 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
3483 PurpleRequestField
*field
= entries
->data
;
3484 const char *id
= purple_request_field_get_id(field
);
3485 const char *value
= purple_request_field_string_get_value(field
);
3487 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
);
3489 if (value
!= NULL
) attrs
[i
++] = g_strdup_printf(SIPE_SOAP_SEARCH_ROW
, id
, value
);
3490 } while ((entries
= g_list_next(entries
)) != NULL
);
3494 gchar
*query
= g_strjoinv(NULL
, attrs
);
3495 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
3496 send_soap_request_with_cb(gc
->proto_data
, body
,
3497 (TransCallback
) process_search_contact_response
, NULL
);
3505 static void sipe_show_find_contact(PurplePluginAction
*action
)
3507 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
3508 PurpleRequestFields
*fields
;
3509 PurpleRequestFieldGroup
*group
;
3510 PurpleRequestField
*field
;
3512 fields
= purple_request_fields_new();
3513 group
= purple_request_field_group_new(NULL
);
3514 purple_request_fields_add_group(fields
, group
);
3516 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
3517 purple_request_field_group_add_field(group
, field
);
3518 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
3519 purple_request_field_group_add_field(group
, field
);
3520 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
3521 purple_request_field_group_add_field(group
, field
);
3522 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
3523 purple_request_field_group_add_field(group
, field
);
3525 purple_request_fields(gc
,
3527 _("Search for a Contact"),
3528 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
3530 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
3532 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
3535 GList
*sipe_actions(PurplePlugin
*plugin
, gpointer context
)
3537 PurpleConnection
*gc
= (PurpleConnection
*) context
;
3538 struct sipe_account_data
*sip
= gc
->proto_data
;
3540 PurplePluginAction
*act
;
3542 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
3543 menu
= g_list_prepend(menu
, act
);
3545 menu
= g_list_reverse(menu
);
3550 /* not needed since privacy is checked for every subscribe */
3551 static void dummy_add_deny(PurpleConnection
*gc
, const char *name
) {
3554 static void dummy_permit_deny(PurpleConnection
*gc
)
3558 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
3564 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
3570 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
3574 static PurplePlugin
*my_protocol
= NULL
;
3576 static PurplePluginProtocolInfo prpl_info
=
3579 NULL
, /* user_splits */
3580 NULL
, /* protocol_options */
3581 NO_BUDDY_ICONS
, /* icon_spec */
3582 sipe_list_icon
, /* list_icon */
3583 NULL
, /* list_emblems */
3584 NULL
, /* status_text */ // TODO
3585 NULL
, /* tooltip_text */ // add custom info to contact tooltip
3586 sipe_status_types
, /* away_states */
3587 NULL
, /* blist_node_menu */
3588 NULL
, /* chat_info */
3589 NULL
, /* chat_info_defaults */
3590 sipe_login
, /* login */
3591 sipe_close
, /* close */
3592 sipe_im_send
, /* send_im */
3593 NULL
, /* set_info */ // TODO maybe
3594 sipe_send_typing
, /* send_typing */
3595 NULL
, /* get_info */ // TODO maybe
3596 sipe_set_status
, /* set_status */
3597 NULL
, /* set_idle */
3598 NULL
, /* change_passwd */
3599 sipe_add_buddy
, /* add_buddy */
3600 NULL
, /* add_buddies */
3601 sipe_remove_buddy
, /* remove_buddy */
3602 NULL
, /* remove_buddies */
3603 dummy_add_deny
, /* add_permit */
3604 dummy_add_deny
, /* add_deny */
3605 dummy_add_deny
, /* rem_permit */
3606 dummy_add_deny
, /* rem_deny */
3607 dummy_permit_deny
, /* set_permit_deny */
3608 NULL
, /* join_chat */
3609 NULL
, /* reject_chat */
3610 NULL
, /* get_chat_name */
3611 NULL
, /* chat_invite */
3612 NULL
, /* chat_leave */
3613 NULL
, /* chat_whisper */
3614 NULL
, /* chat_send */
3615 sipe_keep_alive
, /* keepalive */
3616 NULL
, /* register_user */
3617 NULL
, /* get_cb_info */ // deprecated
3618 NULL
, /* get_cb_away */ // deprecated
3619 sipe_alias_buddy
, /* alias_buddy */
3620 sipe_group_buddy
, /* group_buddy */
3621 sipe_rename_group
, /* rename_group */
3622 NULL
, /* buddy_free */
3623 sipe_convo_closed
, /* convo_closed */
3624 purple_normalize_nocase
, /* normalize */
3625 NULL
, /* set_buddy_icon */
3626 sipe_remove_group
, /* remove_group */
3627 NULL
, /* get_cb_real_name */ // TODO?
3628 NULL
, /* set_chat_topic */
3629 NULL
, /* find_blist_chat */
3630 NULL
, /* roomlist_get_list */
3631 NULL
, /* roomlist_cancel */
3632 NULL
, /* roomlist_expand_category */
3633 NULL
, /* can_receive_file */
3634 NULL
, /* send_file */
3635 NULL
, /* new_xfer */
3636 NULL
, /* offline_message */
3637 NULL
, /* whiteboard_prpl_ops */
3638 sipe_send_raw
, /* send_raw */
3642 static PurplePluginInfo info
= {
3643 PURPLE_PLUGIN_MAGIC
,
3644 PURPLE_MAJOR_VERSION
,
3645 PURPLE_MINOR_VERSION
,
3646 PURPLE_PLUGIN_PROTOCOL
, /**< type */
3647 NULL
, /**< ui_requirement */
3649 NULL
, /**< dependencies */
3650 PURPLE_PRIORITY_DEFAULT
, /**< priority */
3651 "prpl-sipe", /**< id */
3652 "Microsoft LCS/OCS", /**< name */
3653 VERSION
, /**< version */
3654 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
3655 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
3656 "Anibal Avelar <avelar@gmail.com>, " /**< author */
3657 "Gabriel Burt <gburt@novell.com>", /**< author */
3658 PURPLE_WEBSITE
, /**< homepage */
3659 sipe_plugin_load
, /**< load */
3660 sipe_plugin_unload
, /**< unload */
3661 sipe_plugin_destroy
, /**< destroy */
3662 NULL
, /**< ui_info */
3663 &prpl_info
, /**< extra_info */
3672 static void init_plugin(PurplePlugin
*plugin
)
3674 PurpleAccountUserSplit
*split
;
3675 PurpleAccountOption
*option
;
3676 PurpleKeyValuePair
*kvp
;
3679 purple_debug_info(PACKAGE
, "bindtextdomain = %s", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
3680 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s",
3681 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
3684 purple_plugin_register(plugin
);
3686 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
3687 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3688 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
3689 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3691 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
3692 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3693 // Translators: noun (networking port)
3694 option
= purple_account_option_int_new(_("Port"), "port", 5061);
3695 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3697 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
3698 purple_account_option_add_list_item(option
, _("Auto"), "auto");
3699 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
3700 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
3701 purple_account_option_add_list_item(option
, _("UDP"), "udp");
3702 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3704 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
3705 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
3707 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
3708 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3710 // TODO commented out so won't show in the preferences until we fix krb message signing
3711 /*option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
3712 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3714 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
3715 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
3716 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3719 option
= purple_account_option_string_new(_("Auth User"), "authuser", "");
3720 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3721 option
= purple_account_option_string_new(_("Auth Domain"), "authdomain", "");
3722 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3723 my_protocol
= plugin
;
3726 /* I had to redefined the function for it load, but works */
3727 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
3728 plugin
->info
= &(info
);
3729 init_plugin((plugin
));
3730 sipe_plugin_load((plugin
));
3731 return purple_plugin_register(plugin
);