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>
40 # define _(String) ((const char *) gettext (String))
42 # define _(String) ((const char *) (String))
43 #endif /* ENABLE_NLS */
48 #define _LIBC_INTERNAL_
61 #include "accountopt.h"
63 #include "conversation.h"
80 #endif /*USE_KERBEROS*/
83 #include "sipe-sign.h"
87 /* Keep in sync with sipe_transport_type! */
88 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
89 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
93 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
96 static gchar
*get_epid()
98 return sipe_uuid_get_macaddr();
101 static char *genbranch()
103 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
104 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
105 rand() & 0xFFFF, rand() & 0xFFFF);
108 static char *gencallid()
110 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
111 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
112 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
113 rand() & 0xFFFF, rand() & 0xFFFF);
116 static gchar
*find_tag(const gchar
*hdr
)
118 gchar
* tag
= sipmsg_find_part_of_header (hdr
, "tag=", ";", NULL
);
120 // In case it's at the end and there's no trailing ;
121 tag
= sipmsg_find_part_of_header (hdr
, "tag=", NULL
, NULL
);
127 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
132 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
134 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
135 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
138 static void sipe_close(PurpleConnection
*gc
);
140 static void sipe_subscribe_to_name_single(struct sipe_account_data
*sip
, const char * buddy_name
);
141 static void sipe_subscribe_to_buddies_batched(struct sipe_account_data
*sip
);
142 static void send_presence_info(struct sipe_account_data
*sip
);
144 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
146 static void sipe_keep_alive_timeout(struct sipe_account_data
*sip
, const gchar
*hdr
)
148 gchar
*timeout
= sipmsg_find_part_of_header(hdr
, "timeout=", ";", NULL
);
149 if (timeout
!= NULL
) {
150 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
151 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
152 sip
->keepalive_timeout
);
156 static void sipe_keep_alive(PurpleConnection
*gc
)
158 struct sipe_account_data
*sip
= gc
->proto_data
;
159 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
160 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
161 gchar buf
[2] = {0, 0};
162 purple_debug_info("sipe", "sending keep alive\n");
163 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
165 time_t now
= time(NULL
);
166 if ((sip
->keepalive_timeout
> 0) &&
167 ((now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
168 #if PURPLE_VERSION_CHECK(2,4,0)
169 && ((now
- gc
->last_received
) >= sip
->keepalive_timeout
)
172 purple_debug_info("sipe", "sending keep alive\n");
173 sendout_pkt(gc
, "\r\n\r\n");
174 sip
->last_keepalive
= now
;
179 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
181 struct sip_connection
*ret
= NULL
;
182 GSList
*entry
= sip
->openconns
;
185 if (ret
->fd
== fd
) return ret
;
191 static void sipe_auth_free(struct sip_auth
*auth
)
195 g_free(auth
->opaque
);
199 g_free(auth
->target
);
201 g_free(auth
->digest_session_key
);
202 auth
->digest_session_key
= NULL
;
203 g_free(auth
->ntlm_key
);
204 auth
->ntlm_key
= NULL
;
205 auth
->type
= AUTH_TYPE_UNSET
;
210 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
212 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
214 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
218 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
220 struct sip_connection
*conn
= connection_find(sip
, fd
);
222 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
223 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
229 static void connection_free_all(struct sipe_account_data
*sip
)
231 struct sip_connection
*ret
= NULL
;
232 GSList
*entry
= sip
->openconns
;
235 connection_remove(sip
, ret
->fd
);
236 entry
= sip
->openconns
;
240 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
242 const gchar
*method
= msg
->method
;
243 const gchar
*target
= msg
->target
;
248 const char *authdomain
= sip
->authdomain
;
249 const char *authuser
= sip
->authuser
;
250 //const char *krb5_realm;
252 //gchar *krb5_token = NULL;
254 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
255 // and do error checking
257 // KRB realm should always be uppercase
258 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
260 if (sip
->realhostname
) {
261 host
= sip
->realhostname
;
262 } else if (purple_account_get_bool(sip
->account
, "useproxy", TRUE
)) {
263 host
= purple_account_get_string(sip
->account
, "proxy", "");
265 host
= sip
->sipdomain
;
268 /*gboolean new_auth = krb5_auth.gss_context == NULL;
270 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
273 if (new_auth || force_reauth) {
274 krb5_token = krb5_auth.base64_token;
277 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
278 krb5_token = krb5_auth.base64_token;*/
284 if (!authuser
|| strlen(authuser
) < 1) {
285 authuser
= sip
->username
;
288 if (auth
->type
== AUTH_TYPE_DIGEST
) { /* Digest */
289 sprintf(noncecount
, "%08d", auth
->nc
++);
290 response
= purple_cipher_http_digest_calculate_response(
291 "md5", method
, target
, NULL
, NULL
,
292 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
293 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
295 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
);
298 } else if (auth
->type
== AUTH_TYPE_NTLM
) { /* NTLM */
299 // If we have a signature for the message, include that
300 if (msg
->signature
) {
301 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
);
305 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
306 const gchar
*ntlm_key
;
308 #if GLIB_CHECK_VERSION(2,8,0)
309 const gchar
* hostname
= g_get_host_name();
311 static char hostname
[256];
312 int ret
= gethostname(hostname
, sizeof(hostname
));
313 hostname
[sizeof(hostname
) - 1] = '\0';
314 if (ret
== -1 || hostname
[0] == '\0') {
315 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Error when getting host name. Using \"localhost.\"\n");
317 strcpy(hostname
, "localhost");
320 /*const gchar * hostname = purple_get_host_name();*/
322 gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, hostname
, authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
323 auth
->ntlm_key
= (gchar
*)ntlm_key
;
324 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
329 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
331 } else if (auth
->type
== AUTH_TYPE_KERBEROS
) {
334 /*if (new_auth || force_reauth) {
335 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
337 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);
339 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
342 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
343 gchar * mic = "MICTODO";
344 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
345 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
346 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
347 //auth->opaque ? auth->opaque : "", auth->target);
348 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
353 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
356 sprintf(noncecount
, "%08d", auth
->nc
++);
357 response
= purple_cipher_http_digest_calculate_response(
358 "md5", method
, target
, NULL
, NULL
,
359 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
360 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
362 ret
= g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"", authuser
, auth
->realm
, auth
->nonce
, target
, noncecount
, response
);
367 static char *parse_attribute(const char *attrname
, const char *source
)
369 const char *tmp
, *tmp2
;
371 int len
= strlen(attrname
);
373 if (!strncmp(source
, attrname
, len
)) {
375 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
377 retval
= g_strndup(tmp
, tmp2
- tmp
);
379 retval
= g_strdup(tmp
);
385 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
388 const char *authuser
;
391 //const char *krb5_realm;
394 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
395 // and do error checking
397 // KRB realm should always be uppercase
398 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
400 if (sip->realhostname) {
401 host = sip->realhostname;
402 } else if (purple_account_get_bool(sip->account, "useproxy", TRUE)) {
403 host = purple_account_get_string(sip->account, "proxy", "");
405 host = sip->sipdomain;
408 authuser
= sip
->authuser
;
410 if (!authuser
|| strlen(authuser
) < 1) {
411 authuser
= sip
->username
;
415 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
419 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
420 auth
->type
= AUTH_TYPE_NTLM
;
421 parts
= g_strsplit(hdr
+5, "\", ", 0);
424 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
425 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
426 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
429 if ((tmp
= parse_attribute("targetname=\"",
433 else if ((tmp
= parse_attribute("realm=\"",
437 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
444 if (!strstr(hdr
, "gssapi-data")) {
452 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
453 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
454 auth
->type
= AUTH_TYPE_KERBEROS
;
455 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
456 parts
= g_strsplit(hdr
+9, "\", ", 0);
459 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
460 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
461 /*if (krb5_auth.gss_context == NULL) {
462 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
464 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
467 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
469 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
471 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
481 auth
->type
= AUTH_TYPE_DIGEST
;
482 parts
= g_strsplit(hdr
, " ", 0);
484 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
487 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
494 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
496 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
497 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
503 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
505 PurpleConnection
*gc
= data
;
506 struct sipe_account_data
*sip
= gc
->proto_data
;
510 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
512 if (max_write
== 0) {
513 if (sip
->tx_handler
!= 0){
514 purple_input_remove(sip
->tx_handler
);
520 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
522 if (written
< 0 && errno
== EAGAIN
)
524 else if (written
<= 0) {
525 /*TODO: do we really want to disconnect on a failure to write?*/
526 purple_connection_error(gc
, _("Could not write"));
530 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
533 static void sipe_canwrite_cb_ssl(gpointer data
, gint src
, PurpleInputCondition cond
)
535 PurpleConnection
*gc
= data
;
536 struct sipe_account_data
*sip
= gc
->proto_data
;
540 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
542 if (max_write
== 0) {
543 if (sip
->tx_handler
!= 0) {
544 purple_input_remove(sip
->tx_handler
);
550 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
552 if (written
< 0 && errno
== EAGAIN
)
554 else if (written
<= 0) {
555 /*TODO: do we really want to disconnect on a failure to write?*/
556 purple_connection_error(gc
, _("Could not write"));
560 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
563 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
565 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
567 PurpleConnection
*gc
= data
;
568 struct sipe_account_data
*sip
;
569 struct sip_connection
*conn
;
571 if (!PURPLE_CONNECTION_IS_VALID(gc
))
579 purple_connection_error(gc
, _("Could not connect"));
583 sip
= gc
->proto_data
;
585 sip
->connecting
= FALSE
;
586 sip
->last_keepalive
= time(NULL
);
588 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
590 /* If there is more to write now, we need to register a handler */
591 if (sip
->txbuf
->bufused
> 0)
592 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
594 conn
= connection_create(sip
, source
);
595 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
598 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
600 struct sipe_account_data
*sip
;
601 struct sip_connection
*conn
;
603 if (!PURPLE_CONNECTION_IS_VALID(gc
))
605 if (gsc
) purple_ssl_close(gsc
);
609 sip
= gc
->proto_data
;
612 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
613 sip
->connecting
= FALSE
;
614 sip
->last_keepalive
= time(NULL
);
616 conn
= connection_create(sip
, gsc
->fd
);
618 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
623 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
625 PurpleConnection
*gc
= data
;
626 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
627 if (sip
== NULL
) return;
629 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
631 /* If there is more to write now */
632 if (sip
->txbuf
->bufused
> 0) {
633 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
638 static void sendlater(PurpleConnection
*gc
, const char *buf
)
640 struct sipe_account_data
*sip
= gc
->proto_data
;
642 if (!sip
->connecting
) {
643 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
644 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
645 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
647 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
648 purple_connection_error(gc
, _("Couldn't create socket"));
651 sip
->connecting
= TRUE
;
654 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
655 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
657 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
660 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
662 struct sipe_account_data
*sip
= gc
->proto_data
;
663 time_t currtime
= time(NULL
);
664 int writelen
= strlen(buf
);
666 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
667 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
668 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
669 purple_debug_info("sipe", "could not send packet\n");
678 if (sip
->tx_handler
) {
683 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
685 ret
= write(sip
->fd
, buf
, writelen
);
689 if (ret
< 0 && errno
== EAGAIN
)
691 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
696 if (ret
< writelen
) {
697 if (!sip
->tx_handler
){
699 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
702 sip
->tx_handler
= purple_input_add(sip
->fd
,
703 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
708 /* XXX: is it OK to do this? You might get part of a request sent
709 with part of another. */
710 if (sip
->txbuf
->bufused
> 0)
711 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
713 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
719 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
721 sendout_pkt(gc
, buf
);
725 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
727 GSList
*tmp
= msg
->headers
;
730 GString
*outstr
= g_string_new("");
731 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
733 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
734 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
735 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
736 tmp
= g_slist_next(tmp
);
738 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
739 sendout_pkt(sip
->gc
, outstr
->str
);
740 g_string_free(outstr
, TRUE
);
743 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
746 if (sip
->registrar
.ntlm_key
) {
747 struct sipmsg_breakdown msgbd
;
748 gchar
*signature_input_str
;
750 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
751 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
752 sip
->registrar
.ntlm_num
++;
753 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
754 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
755 if (signature_input_str
!= NULL
) {
756 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
757 msg
->rand
= g_strdup(msgbd
.rand
);
758 msg
->num
= g_strdup(msgbd
.num
);
759 g_free(signature_input_str
);
761 sipmsg_breakdown_free(&msgbd
);
764 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
765 buf
= auth_header(sip
, &sip
->registrar
, msg
);
766 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
767 sipmsg_add_header(msg
, "Authorization", buf
);
769 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
772 } 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")) {
773 sip
->registrar
.nc
= 3;
774 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
776 buf
= auth_header(sip
, &sip
->registrar
, msg
);
777 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
780 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
784 static char *get_contact(struct sipe_account_data
*sip
)
786 return g_strdup(sip
->contact
);
791 static char *get_contact_service(struct sipe_account_data *sip)
793 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()));
794 //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);
798 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
799 const char *text
, const char *body
)
803 GString
*outstr
= g_string_new("");
804 struct sipe_account_data
*sip
= gc
->proto_data
;
808 sipmsg_remove_header(msg
, "ms-user-data");
810 contact
= get_contact(sip
);
811 sipmsg_remove_header(msg
, "Contact");
812 sipmsg_add_header(msg
, "Contact", contact
);
815 /* When sending the acknowlegements and errors, the content length from the original
816 message is still here, but there is no body; we need to make sure we're sending the
817 correct content length */
818 sipmsg_remove_header(msg
, "Content-Length");
821 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
822 sipmsg_add_header(msg
, "Content-Length", len
);
824 sipmsg_remove_header(msg
, "Content-Type");
825 sipmsg_add_header(msg
, "Content-Length", "0");
828 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
829 //gchar * mic = "MICTODO";
830 msg
->response
= code
;
832 sipmsg_remove_header(msg
, "Authentication-Info");
833 sign_outgoing_message(msg
, sip
, msg
->method
);
835 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
838 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
839 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
841 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
842 tmp
= g_slist_next(tmp
);
844 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
845 sendout_pkt(gc
, outstr
->str
);
846 g_string_free(outstr
, TRUE
);
849 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
851 if (trans
->msg
) sipmsg_free(trans
->msg
);
852 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
856 static struct transaction
*
857 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
859 struct transaction
*trans
= g_new0(struct transaction
, 1);
860 trans
->time
= time(NULL
);
861 trans
->msg
= (struct sipmsg
*)msg
;
862 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
863 trans
->callback
= callback
;
864 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
868 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
870 struct transaction
*trans
;
871 GSList
*transactions
= sip
->transactions
;
872 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
874 while (transactions
) {
875 trans
= transactions
->data
;
876 if (!strcmp(trans
->cseq
, cseq
)) {
879 transactions
= transactions
->next
;
885 static struct transaction
*
886 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
887 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
888 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
890 struct sipe_account_data
*sip
= gc
->proto_data
;
891 const char *addh
= "";
894 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
895 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
896 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
897 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
898 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
899 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
900 gchar
*route
= strdup("");
901 gchar
*epid
= get_epid(); // TODO generate one per account/login
902 struct transaction
*trans
;
904 if (dialog
&& dialog
->routes
)
906 GSList
*iter
= dialog
->routes
;
911 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
913 iter
= g_slist_next(iter
);
917 if (!ourtag
&& !dialog
) {
921 if (!strcmp(method
, "REGISTER")) {
922 if (sip
->regcallid
) {
924 callid
= g_strdup(sip
->regcallid
);
926 sip
->regcallid
= g_strdup(callid
);
930 if (addheaders
) addh
= addheaders
;
932 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
933 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
934 "From: <sip:%s>%s%s;epid=%s\r\n"
935 "To: <%s>%s%s%s%s\r\n"
936 "Max-Forwards: 70\r\n"
941 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
943 dialog
&& dialog
->request
? dialog
->request
: url
,
944 TRANSPORT_DESCRIPTOR
,
945 purple_network_get_my_ip(-1),
947 branch
? ";branch=" : "",
948 branch
? branch
: "",
950 ourtag
? ";tag=" : "",
951 ourtag
? ourtag
: "",
954 theirtag
? ";tag=" : "",
955 theirtag
? theirtag
: "",
956 theirepid
? ";epid=" : "",
957 theirepid
? theirepid
: "",
958 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
964 body
? strlen(body
) : 0,
968 //printf ("parsing msg buf:\n%s\n\n", buf);
969 msg
= sipmsg_parse_msg(buf
);
980 sign_outgoing_message (msg
, sip
, method
);
982 buf
= sipmsg_to_string (msg
);
984 /* add to ongoing transactions */
985 trans
= transactions_add_buf(sip
, msg
, tc
);
986 sendout_pkt(gc
, buf
);
992 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
994 gchar
*from
= g_strdup_printf("sip:%s", sip
->username
);
995 gchar
*contact
= get_contact(sip
);
996 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
997 "Content-Type: application/SOAP+xml\r\n",contact
);
999 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1000 tr
->payload
= payload
;
1006 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1008 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
1011 static char *get_contact_register(struct sipe_account_data
*sip
)
1013 char *epid
= get_epid();
1014 char *uuid
= generateUUIDfromEPID(epid
);
1015 char *buf
= 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
, uuid
);
1021 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1023 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
1024 char *to
= g_strdup_printf("sip:%s", sip
->username
);
1025 char *contact
= get_contact_register(sip
);
1026 char *hdr
= g_strdup_printf("Contact: %s\r\n"
1027 "Supported: gruu-10, adhoclist, msrtc-event-categories\r\n"
1028 "Event: registration\r\n"
1029 "Allow-Events: presence\r\n"
1030 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1031 "Expires: %d\r\n", contact
,expire
);
1034 sip
->registerstatus
= 1;
1036 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1037 process_register_response
);
1044 static void do_register_cb(struct sipe_account_data
*sip
)
1046 do_register_exp(sip
, sip
->registerexpire
);
1047 sip
->reregister_set
= FALSE
;
1050 static void do_register(struct sipe_account_data
*sip
)
1052 do_register_exp(sip
, sip
->registerexpire
);
1056 * Returns URI from provided To or From header.
1058 * Needs to g_free() after use.
1060 * @return URI with sip: prefix
1062 static gchar
*parse_from(const gchar
*hdr
)
1065 const gchar
*tmp
, *tmp2
= hdr
;
1067 if (!hdr
) return NULL
;
1068 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1069 tmp
= strchr(hdr
, '<');
1071 /* i hate the different SIP UA behaviours... */
1072 if (tmp
) { /* sip address in <...> */
1074 tmp
= strchr(tmp2
, '>');
1076 from
= g_strndup(tmp2
, tmp
- tmp2
);
1078 purple_debug_info("sipe", "found < without > in From\n");
1082 tmp
= strchr(tmp2
, ';');
1084 from
= g_strndup(tmp2
, tmp
- tmp2
);
1086 from
= g_strdup(tmp2
);
1089 purple_debug_info("sipe", "got %s\n", from
);
1093 static xmlnode
* xmlnode_get_descendant(xmlnode
* parent
, ...)
1096 xmlnode
* node
= NULL
;
1099 va_start(args
, parent
);
1100 while ((name
= va_arg(args
, const char *)) != NULL
) {
1101 node
= xmlnode_get_child(parent
, name
);
1102 if (node
== NULL
) return NULL
;
1112 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1114 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1115 send_soap_request(sip
, body
);
1120 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1123 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1125 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1128 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1132 void sipe_auth_user_cb(void * data
)
1134 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1137 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1142 void sipe_deny_user_cb(void * data
)
1144 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1147 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1152 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1154 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1155 sipe_contact_allow_deny(sip
, name
, TRUE
);
1159 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1161 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1162 sipe_contact_allow_deny(sip
, name
, FALSE
);
1166 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1168 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1169 sipe_contact_set_acl(sip, name, "");
1173 sipe_process_incoming_pending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1177 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1178 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1180 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1182 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1183 if (!watchers
) return;
1185 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1186 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1187 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1188 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1190 // TODO pull out optional displayName to pass as alias
1192 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1193 job
->who
= remote_user
;
1195 purple_account_request_authorization(
1209 xmlnode_free(watchers
);
1214 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1216 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1217 if (!purple_group
) {
1218 purple_group
= purple_group_new(group
->name
);
1219 purple_blist_add_group(purple_group
, NULL
);
1223 group
->purple_group
= purple_group
;
1224 sip
->groups
= g_slist_append(sip
->groups
, group
);
1225 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1227 purple_debug_info("sipe", "did not add group %s\n", group
->name
);
1231 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1233 struct sipe_group
*group
;
1239 entry
= sip
->groups
;
1241 group
= entry
->data
;
1242 if (group
->id
== id
) {
1245 entry
= entry
->next
;
1250 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, gchar
* name
)
1252 struct sipe_group
*group
;
1258 entry
= sip
->groups
;
1260 group
= entry
->data
;
1261 if (!strcmp(group
->name
, name
)) {
1264 entry
= entry
->next
;
1270 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1273 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1274 body
= g_strdup_printf(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1275 send_soap_request(sip
, body
);
1277 g_free(group
->name
);
1278 group
->name
= g_strdup(name
);
1282 * Only appends if no such value already stored.
1285 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1286 GSList
* res
= list
;
1287 if (!g_slist_find_custom(list
, data
, func
)) {
1288 res
= g_slist_insert_sorted(list
, data
, func
);
1294 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1295 return group1
->id
- group2
->id
;
1299 * Returns string like "2 4 7 8" - group ids buddy belong to.
1302 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1305 //creating array from GList, converting int to gchar*
1306 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1307 GSList
*entry
= buddy
->groups
;
1309 struct sipe_group
* group
= entry
->data
;
1310 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1311 entry
= entry
->next
;
1315 res
= g_strjoinv(" ", ids_arr
);
1316 g_strfreev(ids_arr
);
1321 * Sends buddy update to server
1324 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1326 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1327 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1329 if (buddy
&& purple_buddy
) {
1330 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1332 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1333 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1335 body
= g_strdup_printf(SIPE_SOAP_SET_CONTACT
,
1336 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1338 send_soap_request(sip
, body
);
1344 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1346 if (msg
->response
== 200) {
1347 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1349 struct group_user_context
* ctx
= (struct group_user_context
*)tc
->payload
;
1353 struct sipe_buddy
*buddy
;
1354 group
->name
= ctx
->group_name
;
1356 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1357 if (!xml
) return FALSE
;
1359 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1360 if (!node
) return FALSE
;
1362 group_id
= xmlnode_get_data(node
);
1363 if (!group_id
) return FALSE
;
1365 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1367 sipe_group_add(sip
, group
);
1369 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1371 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1374 sipe_group_set_user(sip
, ctx
->user_name
);
1383 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1385 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1387 ctx
->group_name
= g_strdup(name
);
1388 ctx
->user_name
= g_strdup(who
);
1390 body
= g_strdup_printf(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1391 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1397 * Should return FALSE if repetitive action is not needed
1399 gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1402 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1403 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1404 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1405 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1406 ret
= sched_action
->repetitive
;
1407 g_free(sched_action
->payload
);
1408 g_free(sched_action
->name
);
1409 g_free(sched_action
);
1414 * Do schedule action for execution in the future.
1415 * Non repetitive execution.
1417 * @param name of action (will be copied)
1418 * @param timeout in seconds
1419 * @action callback function
1420 * @payload callback data (can be NULL, otherwise caller must allocate memory)
1422 void sipe_schedule_action(gchar
*name
, int timeout
, Action action
, struct sipe_account_data
*sip
, void * payload
)
1424 struct scheduled_action
*sched_action
;
1426 purple_debug_info("sipe","scheduling action %s timeout:%d\n", name
, timeout
);
1427 sched_action
= g_new0(struct scheduled_action
, 1);
1428 sched_action
->repetitive
= FALSE
;
1429 sched_action
->name
= g_strdup(name
);
1430 sched_action
->action
= action
;
1431 sched_action
->sip
= sip
;
1432 sched_action
->payload
= payload
;
1433 sched_action
->timeout_handler
= purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1434 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1435 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1439 * Kills action timer effectively cancelling
1442 * @param name of action
1444 void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, gchar
*name
)
1448 if (!sip
->timeouts
|| !name
) return;
1450 entry
= sip
->timeouts
;
1452 struct scheduled_action
*sched_action
= entry
->data
;
1453 if(!strcmp(sched_action
->name
, name
)) {
1454 GSList
*to_delete
= entry
;
1455 entry
= entry
->next
;
1456 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1457 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1458 purple_timeout_remove(sched_action
->timeout_handler
);
1459 g_free(sched_action
->payload
);
1460 g_free(sched_action
->name
);
1461 g_free(sched_action
);
1463 entry
= entry
->next
;
1468 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1470 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1473 //purple_debug_info("sipe","process_subscribe_response: body:\n%s\n", msg->body);
1475 if (msg
->response
== 200 || msg
->response
== 202)
1477 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1479 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1484 /* we can not subscribe -> user is offline (TODO unknown status?) */
1485 to
= parse_from(sipmsg_find_header(tc
->msg
, "To")); /* can't be NULL since it is our own msg */
1486 purple_prpl_got_user_status(sip
->account
, to
, "offline", NULL
);
1493 * Batch Category SUBSCRIBE [SIP-PRES] - msrtc-event-categories+xml
1494 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1495 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1496 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1500 static void sipe_subscribe_to_buddies_batched(struct sipe_account_data
*sip
){
1501 GList
*entry
= g_hash_table_get_values (sip
->buddies
);
1502 struct sipe_buddy
* buddy
;
1503 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1504 gchar
*tmp
= get_contact(sip
);
1507 gchar
*resources_uri
= g_strdup("<adhocList>\n");
1509 purple_debug_info("sipe", " sipe_subscribe_to_buddies_batched buddies size (%d)\n", g_hash_table_size(sip
->buddies
));
1511 buddy
= entry
->data
;
1512 purple_debug_info("sipe", "sipe_subscribe_to_buddies_batched (%s)\n", buddy
->name
);
1513 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", resources_uri
, buddy
->name
);
1514 entry
= entry
->next
;
1517 resources_uri
= g_strdup_printf("%s</adhocList>\n",resources_uri
);
1519 request
= g_strdup_printf(
1520 "Require: adhoclist, categoryList\r\n"
1521 "Supported: eventlist\r\n"
1522 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1523 "Supported: ms-piggyback-first-notify\r\n"
1524 "Supported: com.microsoft.autoextend\r\n"
1525 "Supported: ms-benotify\r\n"
1526 "Proxy-Require: ms-benotify\r\n"
1527 "Event: presence\r\n"
1528 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1529 "Contact: %s\r\n", tmp
);
1531 content
= g_strdup_printf(
1532 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1533 "<action name=\"subscribe\" id=\"63792024\">\n"
1535 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1536 "<category name=\"note\"/>\n"
1537 "<category name=\"state\"/>\n"
1540 "</batchSub>", sip
->username
, resources_uri
1545 /* subscribe to buddy presence */
1546 //send_sip_request(sip->gc, "SUBSCRIBE", resource_uri, resource_uri, request, content, NULL, process_subscribe_response);
1547 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1551 g_free(resources_uri
);
1556 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1557 * The user sends a single SUBSCRIBE request to the subscribed contact.
1558 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1562 static void sipe_subscribe_to_name_single(struct sipe_account_data
*sip
, const char * buddy_name
)
1564 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1565 gchar
*tmp
= get_contact(sip
);
1568 request
= g_strdup_printf(
1569 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1570 "Supported: ms-piggyback-first-notify\r\n"
1571 "Supported: com.microsoft.autoextend\r\n"
1572 "Supported: ms-benotify\r\n"
1573 "Proxy-Require: ms-benotify\r\n"
1574 "Event: presence\r\n"
1575 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1576 "Contact: %s\r\n", tmp
);
1578 content
= g_strdup_printf(
1579 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1580 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1581 "<resource uri=\"%s\"/>\n"
1583 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1584 "<category name=\"note\"/>\n"
1585 "<category name=\"state\"/>\n"
1588 "</batchSub>", sip
->username
, to
1593 /* subscribe to buddy presence */
1594 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1601 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1603 const char *status_id
= purple_status_get_id(status
);
1604 struct sipe_account_data
*sip
= NULL
;
1606 if (!purple_status_is_active(status
))
1610 sip
= account
->gc
->proto_data
;
1613 g_free(sip
->status
);
1614 sip
->status
= g_strdup(status_id
);
1615 send_presence_info(sip
);
1620 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
)
1622 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1623 sipe_group_set_user(sip
, name
);
1627 sipe_group_buddy(PurpleConnection
*gc
,
1629 const char *old_group_name
,
1630 const char *new_group_name
)
1632 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1633 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1634 struct sipe_group
* old_group
= NULL
;
1635 struct sipe_group
* new_group
;
1637 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1638 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1640 if(!buddy
) { // buddy not in roaming list
1644 if (old_group_name
) {
1645 old_group
= sipe_group_find_by_name(sip
, g_strdup(old_group_name
));
1647 new_group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1650 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1651 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1655 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1657 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1658 sipe_group_set_user(sip
, who
);
1662 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1664 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1665 struct sipe_buddy
*b
;
1667 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1669 // Prepend sip: if needed
1670 if (strncmp("sip:", buddy
->name
, 4)) {
1671 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
1672 purple_blist_rename_buddy(buddy
, buf
);
1676 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1677 b
= g_new0(struct sipe_buddy
, 1);
1678 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1679 b
->name
= g_strdup(buddy
->name
);
1680 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1681 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1682 sipe_subscribe_to_name_single(sip
, b
->name
); //@TODO should go to callback
1684 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1689 * Unassociates buddy from group first.
1690 * Then see if no groups left, removes buddy completely.
1691 * Otherwise updates buddy groups on server.
1693 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1695 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1696 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1697 struct sipe_group
*g
= NULL
;
1699 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1704 g
= sipe_group_find_by_name(sip
, group
->name
);
1708 b
->groups
= g_slist_remove(b
->groups
, g
);
1709 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
1712 if (g_slist_length(b
->groups
) < 1) {
1713 gchar
*action_name
= g_strdup_printf("<%s><%s>", "presence", buddy
->name
);
1714 sipe_cancel_scheduled_action(sip
, action_name
);
1715 g_free(action_name
);
1717 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1720 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1721 send_soap_request(sip
, body
);
1726 g_free(b
->annotation
);
1727 g_free(b
->device_name
);
1728 g_slist_free(b
->groups
);
1731 //updates groups on server
1732 sipe_group_set_user(sip
, b
->name
);
1738 sipe_rename_group(PurpleConnection
*gc
,
1739 const char *old_name
,
1741 GList
*moved_buddies
)
1743 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1744 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1746 sipe_group_rename(sip
, s_group
, group
->name
);
1748 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1753 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1755 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1756 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1759 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1760 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1761 send_soap_request(sip
, body
);
1764 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1765 g_free(s_group
->name
);
1767 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1771 static GList
*sipe_status_types(PurpleAccount
*acc
)
1773 PurpleStatusType
*type
;
1774 GList
*types
= NULL
;
1777 type
= purple_status_type_new_with_attrs(
1778 PURPLE_STATUS_AVAILABLE
, NULL
, "Online", TRUE
, TRUE
, FALSE
,
1779 // Translators: noun
1780 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1782 types
= g_list_append(types
, type
);
1785 type
= purple_status_type_new_with_attrs(
1786 PURPLE_STATUS_UNAVAILABLE
, "busy", _("Busy"), TRUE
, TRUE
, FALSE
,
1787 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1789 types
= g_list_append(types
, type
);
1791 // Do Not Disturb (Not let user set it)
1792 type
= purple_status_type_new_with_attrs(
1793 PURPLE_STATUS_UNAVAILABLE
, "do-not-disturb", "Do Not Disturb", TRUE
, FALSE
, FALSE
,
1794 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1796 types
= g_list_append(types
, type
);
1799 type
= purple_status_type_new_with_attrs(
1800 PURPLE_STATUS_AWAY
, "be-right-back", _("Be Right Back"), TRUE
, TRUE
, FALSE
,
1801 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1803 types
= g_list_append(types
, type
);
1806 type
= purple_status_type_new_with_attrs(
1807 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1808 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1810 types
= g_list_append(types
, type
);
1813 type
= purple_status_type_new_with_attrs(
1814 PURPLE_STATUS_UNAVAILABLE
, "on-the-phone", _("On The Phone"), TRUE
, TRUE
, FALSE
,
1815 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1817 types
= g_list_append(types
, type
);
1820 type
= purple_status_type_new_with_attrs(
1821 PURPLE_STATUS_AWAY
, "out-to-lunch", "Out To Lunch", TRUE
, TRUE
, FALSE
,
1822 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1824 types
= g_list_append(types
, type
);
1827 type
= purple_status_type_new_full(
1828 PURPLE_STATUS_INVISIBLE
, NULL
, "Appear Offline", TRUE
, TRUE
, FALSE
);
1829 types
= g_list_append(types
, type
);
1832 type
= purple_status_type_new_full(
1833 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1834 types
= g_list_append(types
, type
);
1840 * A callback for g_hash_table_foreach
1842 static void sipe_buddy_subscribe_cb(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1844 sipe_subscribe_to_name_single(sip
, buddy
->name
);
1848 * Removes entries from purple buddy list
1849 * that does not correspond ones in the roaming contact list.
1851 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
1852 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1853 GSList
*entry
= buddies
;
1854 struct sipe_buddy
*buddy
;
1858 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
1859 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
1862 g
= purple_buddy_get_group(b
);
1863 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
1865 gboolean in_sipe_groups
= FALSE
;
1866 GSList
*entry2
= buddy
->groups
;
1868 struct sipe_group
*group
= entry2
->data
;
1869 if (!strcmp(group
->name
, g
->name
)) {
1870 in_sipe_groups
= TRUE
;
1873 entry2
= entry2
->next
;
1875 if(!in_sipe_groups
) {
1876 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
1877 purple_blist_remove_buddy(b
);
1880 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
1881 purple_blist_remove_buddy(b
);
1883 entry
= entry
->next
;
1887 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1889 int len
= msg
->bodylen
;
1891 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1894 gchar
*contacts_delta
;
1895 xmlnode
*group_node
;
1896 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1900 purple_debug_info("sipe", "msg->body:%s\n", msg
->body
);
1902 /* Convert the contact from XML to Purple Buddies */
1903 isc
= xmlnode_from_str(msg
->body
, len
);
1908 contacts_delta
= g_strdup(xmlnode_get_attrib(isc
, "deltaNum"));
1909 if (contacts_delta
) {
1910 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1914 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1915 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1917 group
->name
= g_strdup(xmlnode_get_attrib(group_node
, "name"));
1918 if (!strncmp(group
->name
, "~", 1)){
1920 group
->name
= "Other Contacts";
1922 group
->name
= g_strdup(group
->name
);
1923 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1925 sipe_group_add(sip
, group
);
1928 // Make sure we have at least one group
1929 if (g_slist_length(sip
->groups
) == 0) {
1930 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1931 PurpleGroup
*purple_group
;
1933 group
->name
= g_strdup("Other Contacts");
1935 purple_group
= purple_group_new(group
->name
);
1936 purple_blist_add_group(purple_group
, NULL
);
1937 sip
->groups
= g_slist_append(sip
->groups
, group
);
1940 /* Parse contacts */
1941 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1942 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
1943 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
1944 gchar
* groups
= g_strdup(xmlnode_get_attrib(item
, "groups"));
1945 gchar
* buddy_name
= g_strdup_printf("sip:%s", uri
);
1946 gchar
**item_groups
;
1947 struct sipe_group
*group
= NULL
;
1948 struct sipe_buddy
*buddy
= NULL
;
1951 // assign to group Other Contacts if nothing else received
1952 if(!groups
|| !strcmp("", groups
) ) {
1953 group
= sipe_group_find_by_name(sip
, "Other Contacts");
1954 groups
= group
? g_strdup_printf("%d", group
->id
) : "1";
1957 item_groups
= g_strsplit(groups
, " ", 0);
1959 while (item_groups
[i
]) {
1960 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
1962 // If couldn't find the right group for this contact, just put them in the first group we have
1963 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1964 group
= sip
->groups
->data
;
1967 if (group
!= NULL
) {
1968 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1970 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1971 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1974 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
1975 if (name
!= NULL
&& strlen(name
) != 0) {
1976 purple_blist_alias_buddy(b
, name
);
1981 buddy
= g_new0(struct sipe_buddy
, 1);
1982 buddy
->name
= g_strdup(b
->name
);
1983 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
1986 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1988 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
1990 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
1995 } // while, contact groups
1996 g_strfreev(item_groups
);
2006 sipe_cleanup_local_blist(sip
);
2008 //subscribe to buddies
2009 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2010 if(sip
->msrtc_event_categories
){
2011 sipe_subscribe_to_buddies_batched(sip
);
2013 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2015 sip
->subscribed_buddies
= TRUE
;
2024 * Subscribe roaming contacts
2026 static void sipe_subscribe_buddylist(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2028 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2029 gchar
*tmp
= get_contact(sip
);
2030 gchar
*hdr
= g_strdup_printf(
2031 "Event: vnd-microsoft-roaming-contacts\r\n"
2032 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2033 "Supported: com.microsoft.autoextend\r\n"
2034 "Supported: ms-benotify\r\n"
2035 "Proxy-Require: ms-benotify\r\n"
2036 "Supported: ms-piggyback-first-notify\r\n"
2037 "Contact: %s\r\n", tmp
);
2040 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, sipe_process_roaming_contacts
);
2046 sipe_process_pending_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2048 sipe_process_incoming_pending (sip
, msg
);
2052 static void sipe_subscribe_pending_buddies(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2054 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2055 gchar
*tmp
= get_contact(sip
);
2056 gchar
*hdr
= g_strdup_printf(
2057 "Event: presence.wpending\r\n"
2058 "Accept: text/xml+msrtc.wpending\r\n"
2059 "Supported: com.microsoft.autoextend\r\n"
2060 "Supported: ms-benotify\r\n"
2061 "Proxy-Require: ms-benotify\r\n"
2062 "Supported: ms-piggyback-first-notify\r\n"
2063 "Contact: %s\r\n", tmp
);
2066 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, sipe_process_pending_response
);
2071 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2073 const gchar
*contacts_delta
;
2076 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2082 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2085 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2092 sipe_process_acl_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2094 sipe_process_roaming_acl(sip
, msg
);
2099 * When we receive some self (BE) NOTIFY with a new subscriber
2100 * we sends a setSubscribers request to him [SIP-PRES]
2104 static void sipe_process_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2114 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2116 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2119 node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
);
2125 user
= xmlnode_get_attrib(node
, "user");
2131 to
= g_strdup_printf("sip:%s", sip
->username
);
2132 tmp
= get_contact(sip
);
2133 hdr
= g_strdup_printf(
2135 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", tmp
);
2137 body
= g_strdup_printf(
2138 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2139 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2140 "</setSubscribers>",user
);
2143 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
2150 static void sipe_subscribe_acl(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2152 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2153 gchar
*tmp
= get_contact(sip
);
2154 gchar
*hdr
= g_strdup_printf(
2155 "Event: vnd-microsoft-roaming-ACL\r\n"
2156 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
2157 "Supported: com.microsoft.autoextend\r\n"
2158 "Supported: ms-benotify\r\n"
2159 "Proxy-Require: ms-benotify\r\n"
2160 "Supported: ms-piggyback-first-notify\r\n"
2161 "Contact: %s\r\n", tmp
);
2164 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, sipe_process_acl_response
);
2170 * To request for presence information about the user, access level settings that have already been configured by the user
2171 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
2172 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
2175 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2177 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2178 gchar
*tmp
= get_contact(sip
);
2179 gchar
*hdr
= g_strdup_printf(
2180 "Event: vnd-microsoft-roaming-self\r\n"
2181 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
2182 "Supported: com.microsoft.autoextend\r\n"
2183 "Supported: ms-benotify\r\n"
2184 "Proxy-Require: ms-benotify\r\n"
2185 "Supported: ms-piggyback-first-notify\r\n"
2187 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
2189 gchar
*body
=g_strdup(
2190 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
2191 "<roaming type=\"categories\"/>"
2192 "<roaming type=\"containers\"/>"
2193 "<roaming type=\"subscribers\"/></roamingList>");
2196 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, NULL
);
2202 /** Subscription for provisioning information to help with initial
2203 * configuration. This subscription is a one-time query (denoted by the Expires header,
2204 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
2205 * configuration, meeting policies, and policy settings that Communicator must enforce.
2206 * TODO: for what we need this information.
2209 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2211 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2212 gchar
*tmp
= get_contact(sip
);
2213 gchar
*hdr
= g_strdup_printf(
2214 "Event: vnd-microsoft-provisioning-v2\r\n"
2215 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
2216 "Supported: com.microsoft.autoextend\r\n"
2217 "Supported: ms-benotify\r\n"
2218 "Proxy-Require: ms-benotify\r\n"
2219 "Supported: ms-piggyback-first-notify\r\n"
2222 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
2223 gchar
*body
= g_strdup(
2224 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
2225 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
2226 "<provisioningGroup name=\"ucPolicy\"/>"
2227 "</provisioningGroupList>");
2230 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, NULL
);
2236 /* IM Session (INVITE and MESSAGE methods) */
2238 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
2240 struct sip_im_session
*session
;
2242 if (sip
== NULL
|| who
== NULL
) {
2246 entry
= sip
->im_sessions
;
2248 session
= entry
->data
;
2249 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
2252 entry
= entry
->next
;
2257 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
2259 struct sip_im_session
*session
= find_im_session(sip
, who
);
2261 session
= g_new0(struct sip_im_session
, 1);
2262 session
->with
= g_strdup(who
);
2263 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
2268 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2270 struct sip_dialog
*dialog
= session
->dialog
;
2273 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
2276 entry
= dialog
->routes
;
2278 g_free(entry
->data
);
2279 entry
= g_slist_remove(entry
, entry
->data
);
2281 entry
= dialog
->supported
;
2283 g_free(entry
->data
);
2284 entry
= g_slist_remove(entry
, entry
->data
);
2286 g_free(dialog
->callid
);
2287 g_free(dialog
->ourtag
);
2288 g_free(dialog
->theirtag
);
2289 g_free(dialog
->theirepid
);
2290 g_free(dialog
->request
);
2292 g_free(session
->dialog
);
2294 entry
= session
->outgoing_message_queue
;
2296 g_free(entry
->data
);
2297 entry
= g_slist_remove(entry
, entry
->data
);
2300 g_free(session
->with
);
2304 static void sipe_present_message_undelivered_err(gchar
*with
, struct sipe_account_data
*sip
, gchar
*message
)
2306 char *msg
, *msg_tmp
;
2307 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
2308 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2310 msg_tmp
= g_strdup_printf( _("The following message could not be delivered to all recipients, "\
2311 "possibly because one or more persons are offline:\n%s") ,
2313 purple_conv_present_error(with
, sip
->account
, msg_tmp
);
2318 static void sipe_im_remove_first_from_queue (struct sip_im_session
* session
);
2319 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
);
2322 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2324 gboolean ret
= TRUE
;
2325 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
2326 struct sip_im_session
* session
= find_im_session(sip
, with
);
2327 struct sip_dialog
*dialog
;
2330 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2335 if (msg
->response
!= 200) {
2336 gchar
*queued_msg
= NULL
;
2337 purple_debug_info("sipe", "process_message_response: MESSAGE response not 200\n");
2339 if (session
->outgoing_message_queue
) {
2340 queued_msg
= session
->outgoing_message_queue
->data
;
2342 sipe_present_message_undelivered_err(with
, sip
, queued_msg
);
2343 im_session_destroy(sip
, session
);
2348 dialog
= session
->dialog
;
2350 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2354 sipe_im_remove_first_from_queue(session
);
2355 sipe_im_process_queue(sip
, session
);
2360 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
2370 if (strncmp("sip:", session
->with
, 4)) {
2371 fullto
= g_strdup_printf("sip:%s", session
->with
);
2373 fullto
= g_strdup(session
->with
);
2376 sipe_parse_html(msg
, &msgformat
, &msgtext
);
2377 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
2379 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2382 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2385 msgr
= g_strdup("");
2388 tmp
= get_contact(sip
);
2389 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
2390 //hdr = g_strdup("Content-Type: text/rtf\r\n");
2391 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
2392 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n",
2397 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msgtext
, session
->dialog
, process_message_response
);
2405 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
2407 GSList
*entry
= session
->outgoing_message_queue
;
2409 char *queued_msg
= entry
->data
;
2410 sipe_send_message(sip
, session
, queued_msg
);
2415 sipe_im_remove_first_from_queue (struct sip_im_session
* session
)
2417 if (session
&& session
->outgoing_message_queue
) {
2418 char *queued_msg
= session
->outgoing_message_queue
->data
;
2419 // Remove from the queue and free the string
2420 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2426 sipe_get_route_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2428 GSList
*hdr
= msg
->headers
;
2429 struct siphdrelement
*elem
;
2435 if(!strcmp(elem
->name
, "Record-Route"))
2437 gchar
*route
= sipmsg_find_part_of_header(elem
->value
, "<", ">", NULL
);
2438 dialog
->routes
= g_slist_append(dialog
->routes
, route
);
2440 hdr
= g_slist_next(hdr
);
2445 dialog
->routes
= g_slist_reverse(dialog
->routes
);
2450 dialog
->request
= dialog
->routes
->data
;
2451 dialog
->routes
= g_slist_remove(dialog
->routes
, dialog
->routes
->data
);
2454 contact
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Contact"), "<", ">", NULL
);
2455 dialog
->routes
= g_slist_append(dialog
->routes
, contact
);
2459 sipe_get_supported_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2461 GSList
*hdr
= msg
->headers
;
2462 struct siphdrelement
*elem
;
2466 if(!strcmp(elem
->name
, "Supported")
2467 && !g_slist_find_custom(dialog
->supported
, elem
->value
, (GCompareFunc
)strcmp
))
2469 dialog
->supported
= g_slist_append(dialog
->supported
, g_strdup(elem
->value
));
2472 hdr
= g_slist_next(hdr
);
2477 sipe_parse_dialog(struct sipmsg
* msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2479 gchar
*us
= outgoing
? "From" : "To";
2480 gchar
*them
= outgoing
? "To" : "From";
2482 dialog
->callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
2483 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, us
));
2484 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, them
));
2485 if (!dialog
->theirepid
) {
2486 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", ";", NULL
);
2488 if (!dialog
->theirepid
) {
2489 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", NULL
, NULL
);
2492 sipe_get_route_header(msg
, dialog
, outgoing
);
2493 sipe_get_supported_header(msg
, dialog
, outgoing
);
2498 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2500 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
2501 struct sip_im_session
* session
= find_im_session(sip
, with
);
2502 struct sip_dialog
*dialog
;
2505 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2510 if (msg
->response
!= 200) {
2511 gchar
*queued_msg
= NULL
;
2512 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2514 if (session
->outgoing_message_queue
) {
2515 queued_msg
= session
->outgoing_message_queue
->data
;
2517 sipe_present_message_undelivered_err(with
, sip
, queued_msg
);
2519 im_session_destroy(sip
, session
);
2524 dialog
= session
->dialog
;
2526 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2531 sipe_parse_dialog(msg
, dialog
, TRUE
);
2534 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
2535 session
->outgoing_invite
= NULL
;
2536 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)strcmp
)) {
2537 sipe_im_remove_first_from_queue(session
);
2539 sipe_im_process_queue(sip
, session
);
2547 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
* session
, gchar
* msg_body
)
2556 char *ms_text_format
;
2560 if (session
->dialog
) {
2561 purple_debug_info("sipe", "session with %s already has a dialog open\n", session
->with
);
2565 session
->dialog
= g_new0(struct sip_dialog
, 1);
2567 if (strstr(session
->with
, "sip:")) {
2568 to
= g_strdup(session
->with
);
2570 to
= g_strdup_printf("sip:%s", session
->with
);
2573 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
2574 purple_debug_info("sipe", "sipe_invite: msgformat=%s", msgformat
);
2576 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2580 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2584 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
2585 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
2590 contact
= get_contact(sip
);
2591 hdr
= g_strdup_printf(
2593 "Content-Type: application/sdp\r\n",
2594 contact
, ms_text_format
);
2595 g_free(ms_text_format
);
2597 body
= g_strdup_printf(
2599 "o=- 0 0 IN IP4 %s\r\n"
2603 "m=message %d sip null\r\n"
2604 "a=accept-types:text/plain text/html image/gif "
2605 "multipart/alternative application/im-iscomposing+xml\r\n",
2606 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
2608 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
2609 to
, to
, hdr
, body
, session
->dialog
, process_invite_response
);
2618 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2621 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->dialog
, NULL
);
2622 im_session_destroy(sip
, session
);
2627 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
2629 struct sipe_account_data
*sip
= gc
->proto_data
;
2631 purple_debug_info("sipe", "conversation with %s closed\n", who
);
2632 im_session_close(sip
, find_im_session(sip
, who
));
2636 im_session_close_all (struct sipe_account_data
*sip
)
2638 GSList
*entry
= sip
->im_sessions
;
2640 im_session_close (sip
, entry
->data
);
2641 entry
= sip
->im_sessions
;
2645 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
2647 struct sipe_account_data
*sip
;
2650 struct sip_im_session
*session
;
2652 purple_debug_info("sipe", "sipe_im_send what=%s\n", what
);
2654 sip
= gc
->proto_data
;
2656 text
= g_strdup(what
);
2658 session
= find_or_create_im_session(sip
, who
);
2660 // Queue the message
2661 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, text
);
2663 if (session
->dialog
&& session
->dialog
->callid
) {
2664 sipe_im_process_queue(sip
, session
);
2665 } else if (!session
->outgoing_invite
) {
2666 // Need to send the INVITE to get the outgoing dialog setup
2667 sipe_invite(sip
, session
, text
);
2675 /* End IM Session (INVITE and MESSAGE methods) */
2678 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
2680 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2681 struct sip_im_session
*session
;
2683 if (state
== PURPLE_NOT_TYPING
)
2686 session
= find_im_session(sip
, who
);
2688 if (session
&& session
->dialog
) {
2689 send_sip_request(gc
, "INFO", who
, who
,
2690 "Content-Type: application/xml\r\n",
2691 SIPE_SEND_TYPING
, session
->dialog
, NULL
);
2694 return SIPE_TYPING_SEND_TIMEOUT
;
2697 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
2699 GSList
*tmp
= sip
->transactions
;
2700 time_t currtime
= time(NULL
);
2702 struct transaction
*trans
= tmp
->data
;
2704 purple_debug_info("sipe", "have open transaction age: %ld\n", currtime
-trans
->time
);
2705 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
2708 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
2710 sendout_sipmsg(sip
, trans
->msg
);
2717 static void do_reauthenticate_cb(struct sipe_account_data
*sip
)
2719 /* register again when security token expires */
2720 /* we have to start a new authentication as the security token
2721 * is almost expired by sending a not signed REGISTER message */
2722 purple_debug_info("sipe", "do a full reauthentication\n");
2723 sipe_auth_free(&sip
->registrar
);
2724 sip
->registerstatus
= 0;
2726 sip
->reauthenticate_set
= FALSE
;
2729 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2733 gboolean found
= FALSE
;
2735 from
= parse_from(sipmsg_find_header(msg
, "From"));
2739 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
2741 contenttype
= sipmsg_find_header(msg
, "Content-Type");
2742 if (!contenttype
|| !strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
2743 gchar
*msgr
= sipmsg_find_part_of_header(contenttype
, "msgr=", NULL
, NULL
);
2744 gchar
*x_mms_im_format
= sipmsg_get_x_mms_im_format(msgr
);
2746 gchar
*body_esc
= g_markup_escape_text(msg
->body
, -1);
2747 gchar
*body_html
= sipmsg_apply_x_mms_im_format(x_mms_im_format
, body_esc
);
2750 g_free(x_mms_im_format
);
2752 serv_got_im(sip
->gc
, from
, body_html
, 0, time(NULL
));
2754 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2756 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
2757 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2762 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
2766 state
= xmlnode_get_child(isc
, "state");
2769 purple_debug_info("sipe", "process_incoming_message: no state found\n");
2774 statedata
= xmlnode_get_data(state
);
2776 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
2777 else serv_got_typing_stopped(sip
->gc
, from
);
2782 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2786 purple_debug_info("sipe", "got unknown mime-type");
2787 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
2792 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2794 gchar
*ms_text_format
;
2796 struct sip_im_session
*session
;
2797 // Only accept text invitations
2798 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
2799 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
2803 from
= parse_from(sipmsg_find_header(msg
, "From"));
2804 session
= find_or_create_im_session (sip
, from
);
2806 if (session
->dialog
) {
2807 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
2809 session
->dialog
= g_new0(struct sip_dialog
, 1);
2811 sipe_parse_dialog(msg
, session
->dialog
, FALSE
);
2813 session
->dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
2814 session
->dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
2815 session
->dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
2816 session
->dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
2819 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
2822 //ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk=
2823 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
2824 if (ms_text_format
&& !strncmp(ms_text_format
, "text/plain", 10)) {
2825 gchar
*msgr
= sipmsg_find_part_of_header(ms_text_format
, "msgr=", ";", NULL
);
2826 gchar
*x_mms_im_format
= sipmsg_get_x_mms_im_format(msgr
);
2828 gchar
*ms_body
= sipmsg_find_part_of_header(ms_text_format
, "ms-body=", NULL
, NULL
);
2831 gchar
*body
= purple_base64_decode(ms_body
, NULL
);
2832 gchar
*body_esc
= g_markup_escape_text(body
, -1);
2833 gchar
*body_html
= sipmsg_apply_x_mms_im_format(x_mms_im_format
, body_esc
);
2837 serv_got_im(sip
->gc
, from
, body_html
, 0, time(NULL
));
2839 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message reciept
2841 g_free(x_mms_im_format
);
2845 sipmsg_remove_header(msg
, "Ms-Conversation-ID");
2846 sipmsg_remove_header(msg
, "Ms-Text-Format");
2847 sipmsg_remove_header(msg
, "EndPoints");
2848 sipmsg_remove_header(msg
, "User-Agent");
2849 sipmsg_remove_header(msg
, "Roster-Manager");
2851 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
2852 //sipmsg_add_header(msg, "Supported", "ms-renders-gif");
2854 send_sip_response(sip
->gc
, msg
, 200, "OK", g_strdup_printf(
2856 "o=- 0 0 IN IP4 %s\r\n"
2860 "m=message %d sip sip:%s\r\n"
2861 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
2862 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
2863 sip
->realport
, sip
->username
));
2866 static void sipe_connection_cleanup(struct sipe_account_data
*);
2867 static void create_connection(struct sipe_account_data
*, gchar
*, int);
2869 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2873 const gchar
*expires_header
;
2875 GSList
*hdr
= msg
->headers
;
2876 struct siphdrelement
*elem
;
2878 expires_header
= sipmsg_find_header(msg
, "Expires");
2879 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
2880 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
2882 switch (msg
->response
) {
2885 sip
->registerstatus
= 0;
2888 gchar
*contact_hdr
= NULL
;
2893 sip
->registerexpire
= expires
;
2895 if (!sip
->reregister_set
) {
2896 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
2897 sipe_schedule_action(action_name
, expires
, (Action
) do_register_cb
, sip
, NULL
);
2898 g_free(action_name
);
2899 sip
->reregister_set
= TRUE
;
2902 sip
->registerstatus
= 3;
2904 if (!sip
->reauthenticate_set
) {
2905 /* we have to reauthenticate as our security token expires
2906 after eight hours (be five minutes early) */
2907 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
2908 guint reauth_timeout
= (8 * 3600) - 360;
2909 sipe_schedule_action(action_name
, reauth_timeout
, (Action
) do_reauthenticate_cb
, sip
, NULL
);
2910 g_free(action_name
);
2911 sip
->reauthenticate_set
= TRUE
;
2914 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
2917 uuid
= generateUUIDfromEPID(epid
);
2920 // There can be multiple Contact headers (one per location where the user is logged in) so
2921 // make sure to only get the one for this uuid
2922 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
2923 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
2924 if (valid_contact
) {
2925 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
2926 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
2927 g_free(valid_contact
);
2930 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
2936 sip
->contact
= g_strdup_printf("<%s>", gruu
);
2939 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
2940 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
);
2942 sip
->msrtc_event_categories
= FALSE
;
2947 if(!strcmp(elem
->name
, "Supported"))
2949 if (strstr(elem
->value
, "msrtc-event-categories")){
2950 sip
->msrtc_event_categories
= TRUE
;
2952 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s, %d\n", elem
->value
, sip
->msrtc_event_categories
);
2954 hdr
= g_slist_next(hdr
);
2957 if (!sip
->subscribed
) { //do it just once, not every re-register
2958 tmp
= sipmsg_find_header(msg
, "Allow-Events");
2959 if (tmp
&& strstr(tmp
, "vnd-microsoft-provisioning")){
2960 sipe_subscribe_buddylist(sip
, msg
);
2962 sipe_subscribe_acl(sip
, msg
);
2963 sipe_subscribe_roaming_self(sip
, msg
);
2964 sipe_subscribe_roaming_provisioning(sip
, msg
);
2965 sipe_subscribe_pending_buddies(sip
, msg
);
2966 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
2967 sip
->subscribed
= TRUE
;
2970 if (purple_account_get_bool(sip
->account
, "clientkeepalive", FALSE
)) {
2971 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Setting user defined keepalive\n");
2972 sip
->keepalive_timeout
= purple_account_get_int(sip
->account
, "keepalive", 0);
2974 tmp
= sipmsg_find_header(msg
, "ms-keep-alive");
2976 sipe_keep_alive_timeout(sip
, tmp
);
2980 // Should we remove the transaction here?
2981 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
2982 transactions_remove(sip
, tc
);
2987 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
2989 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
2990 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
2994 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
2997 tmp
= g_strsplit(parts
[0], ":", 0);
2998 hostname
= g_strdup(tmp
[0]);
2999 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
3003 tmp
= g_strsplit(parts
[i
], "=", 0);
3005 if (g_strcasecmp("transport", tmp
[0]) == 0) {
3006 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
3007 transport
= SIPE_TRANSPORT_TCP
;
3008 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
3009 transport
= SIPE_TRANSPORT_UDP
;
3018 /* Close old connection */
3019 sipe_connection_cleanup(sip
);
3021 /* Create new connection */
3022 sip
->transport
= transport
;
3023 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
3024 hostname
, port
, TRANSPORT_DESCRIPTOR
);
3025 create_connection(sip
, hostname
, port
);
3030 if (sip
->registerstatus
!= 2) {
3031 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
3032 if (sip
->registrar
.retries
> 3) {
3033 sip
->gc
->wants_to_die
= TRUE
;
3034 purple_connection_error(sip
->gc
, _("Wrong Password"));
3037 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3038 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3040 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3042 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3043 fill_auth(sip
, tmp
, &sip
->registrar
);
3044 sip
->registerstatus
= 2;
3045 if (sip
->account
->disconnecting
) {
3046 do_register_exp(sip
, 0);
3054 const gchar
*warning
= sipmsg_find_header(msg
, "Warning");
3055 if (warning
!= NULL
) {
3057 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
3059 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
3060 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
3063 warning
= _("You have been rejected by the server");
3066 sip
->gc
->wants_to_die
= TRUE
;
3067 purple_connection_error(sip
->gc
, warning
);
3073 const gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3074 if (warning
!= NULL
) {
3075 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3076 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
3079 warning
= _("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator");
3082 sip
->gc
->wants_to_die
= TRUE
;
3083 purple_connection_error(sip
->gc
, warning
);
3089 const gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3090 if (warning
!= NULL
) {
3091 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3092 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
3095 warning
= _("Service unavailable: no reason given");
3098 sip
->gc
->wants_to_die
= TRUE
;
3099 purple_connection_error(sip
->gc
, warning
);
3107 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3110 xmlnode
*xn_categories
;
3111 xmlnode
*xn_category
;
3114 const char *activity
= NULL
;
3116 xn_categories
= xmlnode_from_str(data
, len
);
3117 uri
= xmlnode_get_attrib(xn_categories
, "uri");
3119 purple_debug_info("sipe", "process_incoming_notify_rlmi\n");
3121 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
3123 xn_category
= xmlnode_get_next_twin(xn_category
) )
3125 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
3127 if (!strcmp(attrVar
, "note"))
3130 struct sipe_buddy
*sbuddy
;
3131 xn_node
= xmlnode_get_child(xn_category
, "note");
3132 if (!xn_node
) continue;
3133 xn_node
= xmlnode_get_child(xn_node
, "body");
3134 if (!xn_node
) continue;
3136 note
= xmlnode_get_data(xn_node
);
3138 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3141 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3142 sbuddy
->annotation
= g_strdup(note
);
3148 else if(!strcmp(attrVar
, "state"))
3152 xn_node
= xmlnode_get_child(xn_category
, "state");
3153 if (!xn_node
) continue;
3154 xn_node
= xmlnode_get_child(xn_node
, "availability");
3155 if (!xn_node
) continue;
3157 data
= xmlnode_get_data(xn_node
);
3162 activity
= "unknown";
3163 else if (avail
< 4500)
3164 activity
= "available";
3165 else if (avail
< 6000)
3167 else if (avail
< 7500)
3169 else if (avail
< 9000)
3171 else if (avail
< 12000)
3173 else if (avail
< 18000)
3176 activity
= "offline";
3184 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
3188 xmlnode_free(xn_categories
);
3191 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3193 const char *uri
,*state
;
3195 xmlnode
*xn_resource
;
3196 xmlnode
*xn_instance
;
3198 xn_list
= xmlnode_from_str(data
, len
);
3200 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
3202 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
3204 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
3205 if (!xn_instance
) return;
3207 state
= xmlnode_get_attrib(xn_instance
, "state");
3208 uri
= xmlnode_get_attrib(xn_instance
, "cid");
3209 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n",uri
,state
);
3210 if(strstr(state
,"resubscribe")){
3211 sipe_subscribe_to_name_single(sip
, uri
);
3216 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3219 gchar
*getbasic
= g_strdup("closed");
3220 gchar
*activity
= g_strdup("available");
3222 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
3223 gboolean isonline
= FALSE
;
3224 xmlnode
*display_name_node
;
3226 pidf
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3228 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",msg
->body
);
3232 uri
= xmlnode_get_attrib(pidf
, "entity");
3234 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
3236 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3237 basicstatus
= xmlnode_get_child(status
, "basic");
3242 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
3247 getbasic
= xmlnode_get_data(basicstatus
);
3249 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
3254 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
3255 if (strstr(getbasic
, "open")) {
3259 display_name_node
= xmlnode_get_child(pidf
, "display-name");
3260 // updating display name if alias was just URI
3261 if (display_name_node
) {
3262 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
3263 GSList
*entry
= buddies
;
3264 PurpleBuddy
*p_buddy
;
3265 char * display_name
= xmlnode_get_data(display_name_node
);
3268 const char *server_alias
;
3271 p_buddy
= entry
->data
;
3273 alias
= (char *)purple_buddy_get_alias(p_buddy
);
3274 alias
= alias
? g_strdup_printf("sip:%s", alias
) : NULL
;
3275 if (!g_ascii_strcasecmp(uri
, alias
)) {
3276 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
3277 purple_blist_alias_buddy(p_buddy
, display_name
);
3281 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3283 ( (server_alias
&& strcmp(display_name
, server_alias
))
3284 || !server_alias
|| strlen(server_alias
) == 0 )
3286 purple_blist_server_alias_buddy(p_buddy
, display_name
);
3289 entry
= entry
->next
;
3293 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
3294 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3295 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
3296 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
3297 activity
= xmlnode_get_data(basicstatus
);
3303 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
3306 gchar
* status_id
= NULL
;
3308 if (strstr(activity
, "busy")) {
3310 } else if (strstr(activity
, "away")) {
3316 status_id
= "available";
3319 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
3320 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
3322 purple_prpl_got_user_status(sip
->account
, uri
, "offline", NULL
);
3330 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3332 const char *availability
;
3333 const char *activity
;
3334 const char *display_name
= NULL
;
3335 const char *activity_name
;
3340 struct sipe_buddy
*sbuddy
;
3342 xmlnode
*xn_presentity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3344 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
3345 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
3346 xmlnode
*xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
3347 xmlnode
*xn_email
= xmlnode_get_child(xn_presentity
, "email");
3348 const char *email
= xn_email
? xmlnode_get_attrib(xn_email
, "email") : NULL
;
3349 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
3350 xmlnode
*xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
3351 const char *note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
3352 xmlnode
*xn_devices
= xmlnode_get_child(xn_presentity
, "devices");
3353 xmlnode
*xn_device_presence
= xn_devices
? xmlnode_get_child(xn_devices
, "devicePresence") : NULL
;
3354 xmlnode
*xn_device_name
= xn_device_presence
? xmlnode_get_child(xn_device_presence
, "deviceName") : NULL
;
3355 const char *device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
3357 name
= xmlnode_get_attrib(xn_presentity
, "uri");
3358 uri
= g_strdup_printf("sip:%s", name
);
3359 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
3360 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
3362 // updating display name if alias was just URI
3363 if (xn_display_name
) {
3364 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
3365 GSList
*entry
= buddies
;
3366 PurpleBuddy
*p_buddy
;
3367 display_name
= xmlnode_get_attrib(xn_display_name
, "displayName");
3370 const char *email_str
, *server_alias
;
3372 p_buddy
= entry
->data
;
3374 if (!g_ascii_strcasecmp(name
, purple_buddy_get_alias(p_buddy
))) {
3375 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
3376 purple_blist_alias_buddy(p_buddy
, display_name
);
3379 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3381 ( (server_alias
&& strcmp(display_name
, server_alias
))
3382 || !server_alias
|| strlen(server_alias
) == 0 )
3384 purple_blist_server_alias_buddy(p_buddy
, display_name
);
3388 email_str
= purple_blist_node_get_string((PurpleBlistNode
*)p_buddy
, "email");
3389 if (!email_str
|| g_ascii_strcasecmp(email_str
, email
)) {
3390 purple_blist_node_set_string((PurpleBlistNode
*)p_buddy
, "email", email
);
3394 entry
= entry
->next
;
3398 avl
= atoi(availability
);
3399 act
= atoi(activity
);
3402 activity_name
= "away";
3403 else if (act
<= 150)
3404 activity_name
= "out-to-lunch";
3405 else if (act
<= 300)
3406 activity_name
= "be-right-back";
3407 else if (act
<= 400)
3408 activity_name
= "available";
3409 else if (act
<= 500)
3410 activity_name
= "on-the-phone";
3411 else if (act
<= 600)
3412 activity_name
= "busy";
3414 activity_name
= "available";
3417 activity_name
= "offline";
3419 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3422 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3423 sbuddy
->annotation
= NULL
;
3424 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
3426 if (sbuddy
->device_name
) { g_free(sbuddy
->device_name
); }
3427 sbuddy
->device_name
= NULL
;
3428 if (device_name
) { sbuddy
->device_name
= g_strdup(device_name
); }
3431 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
3432 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
3433 xmlnode_free(xn_presentity
);
3437 static void process_incoming_notify_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3439 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
3441 purple_debug_info("sipe", "process_incoming_notify_presence: Content-Type: %s\n", ctype
? ctype
: "");
3443 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
3444 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
3446 const char *content
= msg
->body
;
3447 unsigned length
= msg
->bodylen
;
3448 PurpleMimeDocument
*mime
= NULL
;
3450 if (strstr(ctype
, "multipart"))
3452 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
3453 const char *content_type
;
3455 mime
= purple_mime_document_parse(doc
);
3456 parts
= purple_mime_document_get_parts(mime
);
3458 content
= purple_mime_part_get_data(parts
->data
);
3459 length
= purple_mime_part_get_length(parts
->data
);
3460 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
3461 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
3463 process_incoming_notify_rlmi_resub(sip
, content
, length
);
3467 process_incoming_notify_rlmi(sip
, content
, length
);
3469 parts
= parts
->next
;
3475 purple_mime_document_free(mime
);
3478 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
3480 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
3482 else if(strstr(ctype
, "application/rlmi+xml"))
3484 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
3487 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
3489 process_incoming_notify_msrtc(sip
, msg
);
3493 process_incoming_notify_pidf(sip
, msg
);
3498 * Dispatcher for all incoming subscription information
3499 * whether it comes from NOTIFY, BENOTIFY requests or
3500 * piggy-backed to subscription's OK responce.
3502 * @param request whether initiated from BE/NOTIFY request or OK-response message.
3503 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
3505 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
3507 gchar
*event
= sipmsg_find_header(msg
, "Event");
3508 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
3509 const char *uri
,*state
;
3511 xmlnode
*xn_resource
;
3512 xmlnode
*xn_instance
;
3516 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event
? event
: "", msg
->body
);
3517 purple_debug_info("sipe", "process_incoming_notify: subscription_state:%s\n\n", subscription_state
);
3521 const gchar
*expires_header
;
3522 expires_header
= sipmsg_find_header(msg
, "Expires");
3523 expires
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
3524 purple_debug_info("sipe", "process_incoming_notify: expires:%d\n\n", expires
);
3527 if (!subscription_state
|| strstr(subscription_state
, "active"))
3529 if (event
&& strstr(event
, "presence"))
3531 process_incoming_notify_presence(sip
, msg
);
3533 else if (event
&& strstr(event
, "vnd-microsoft-roaming-contacts"))
3535 sipe_process_roaming_contacts(sip
, msg
, NULL
);
3537 else if (event
&& strstr(event
, "vnd-microsoft-roaming-self"))
3539 sipe_process_roaming_self(sip
, msg
);
3541 else if (event
&& strstr(event
, "vnd-microsoft-roaming-ACL"))
3543 sipe_process_roaming_acl(sip
, msg
);
3545 else if (event
&& strstr(event
, "presence.wpending"))
3547 sipe_process_incoming_pending(sip
, msg
);
3551 purple_debug_info("sipe", "Unable to process (BE)NOTIFY. Event is not supported:%s\n", event
? event
: "");
3555 //The server sends a (BE)NOTIFY with the status 'terminated'
3556 if(request
&& subscription_state
&& strstr(subscription_state
, "terminated") )
3558 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3559 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from
);
3563 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
3564 if (request
&& !benotify
)
3566 sipmsg_remove_header(msg
, "Expires");
3567 sipmsg_remove_header(msg
, "subscription-state");
3568 sipmsg_remove_header(msg
, "Event");
3569 sipmsg_remove_header(msg
, "Require");
3570 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3577 static gchar* gen_xpidf(struct sipe_account_data *sip)
3579 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3581 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
3582 "<display name=\"sip:%s\"/>\r\n"
3583 "<atom id=\"1234\">\r\n"
3584 "<address uri=\"sip:%s\">\r\n"
3585 "<status status=\"%s\"/>\r\n"
3598 static gchar* gen_pidf(struct sipe_account_data *sip)
3600 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3601 "<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"
3602 "<tuple id=\"0\">\r\n"
3604 "<basic>open</basic>\r\n"
3605 "<ep:activities>\r\n"
3606 " <ep:activity>%s</ep:activity>\r\n"
3610 "<ci:display-name>%s</ci:display-name>\r\n"
3620 process_send_presence_info_v0_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3622 if (msg
->response
== 488) {
3623 sip
->presence_method_version
= 1;
3624 send_presence_info(sip
);
3629 static void send_presence_info_v0(struct sipe_account_data
*sip
, char * note
)
3631 int availability
= 300; // online
3632 int activity
= 400; // Available
3635 if (!strcmp(sip
->status
, "away")) {
3637 } else if (!strcmp(sip
->status
, "out-to-lunch")) {
3639 } else if (!strcmp(sip
->status
, "be-right-back")) {
3641 } else if (!strcmp(sip
->status
, "on-the-phone")) {
3643 } else if (!strcmp(sip
->status
, "do-not-disturb")) {
3645 } else if (!strcmp(sip
->status
, "busy")) {
3647 } else if (!strcmp(sip
->status
, "invisible")) {
3648 availability
= 0; // offline
3652 name
= g_strdup_printf("sip: sip:%s", sip
->username
);
3653 //@TODO: send user data - state; add hostname in upper case
3654 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
3655 send_soap_request_with_cb(sip
, body
, process_send_presence_info_v0_response
, NULL
);
3661 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3663 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
3664 if (msg
->response
== 200) {
3665 sip
->status_version
= 0;
3666 send_presence_info(sip
);
3672 process_send_presence_info_v1_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3674 if (msg
->response
== 409) {
3675 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
3676 // TODO need to parse the version #'s?
3677 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
3678 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
3682 purple_debug_info("sipe", "process_send_presence_info_v1_response = %s\n", msg
->body
);
3684 tmp
= get_contact(sip
);
3685 hdr
= g_strdup_printf("Contact: %s\r\n"
3686 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
3688 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
3698 static void send_presence_info_v1(struct sipe_account_data
*sip
, char * note
)
3705 if (!strcmp(sip
->status
, "away")) {
3707 } else if (!strcmp(sip
->status
, "busy")) {
3714 uri
= g_strdup_printf("sip:%s", sip
->username
);
3715 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
3716 sip
->status_version
, code
,
3717 sip
->status_version
, code
,
3718 sip
->status_version
, note
? note
: "",
3719 sip
->status_version
, note
? note
: "",
3720 sip
->status_version
, note
? note
: ""
3722 sip
->status_version
++;
3724 tmp
= get_contact(sip
);
3725 hdr
= g_strdup_printf("Contact: %s\r\n"
3726 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
3728 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_info_v1_response
);
3736 static void send_presence_info(struct sipe_account_data
*sip
)
3738 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
3740 if (!status
) return;
3742 note
= g_strdup(purple_status_get_attr_string(status
, "message"));
3744 purple_debug_info("sipe", "sending presence info, version = %d\n", sip
->presence_method_version
);
3745 if (sip
->presence_method_version
!= 1) {
3746 send_presence_info_v0(sip
, note
);
3748 send_presence_info_v1(sip
, note
);
3752 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
3754 gboolean found
= FALSE
;
3755 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
3756 if (msg
->response
== 0) { /* request */
3757 if (!strcmp(msg
->method
, "MESSAGE")) {
3758 process_incoming_message(sip
, msg
);
3760 } else if (!strcmp(msg
->method
, "NOTIFY")) {
3761 purple_debug_info("sipe","send->process_incoming_notify\n");
3762 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
3764 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
3765 purple_debug_info("sipe","send->process_incoming_benotify\n");
3766 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
3768 } else if (!strcmp(msg
->method
, "INVITE")) {
3769 process_incoming_invite(sip
, msg
);
3771 } else if (!strcmp(msg
->method
, "INFO")) {
3773 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3775 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
3778 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3780 } else if (!strcmp(msg
->method
, "ACK")) {
3781 // ACK's don't need any response
3783 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
3784 // LCS 2005 sends us these - just respond 200 OK
3786 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3787 } else if (!strcmp(msg
->method
, "BYE")) {
3788 struct sip_im_session
*session
;
3790 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3792 from
= parse_from(sipmsg_find_header(msg
, "From"));
3793 session
= find_im_session (sip
, from
);
3797 // TODO Let the user know the other user left the conversation?
3798 im_session_destroy(sip
, session
);
3803 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3805 } else { /* response */
3806 struct transaction
*trans
= transactions_find(sip
, msg
);
3808 if (msg
->response
== 407) {
3809 gchar
*resend
, *auth
, *ptmp
;
3811 if (sip
->proxy
.retries
> 30) return;
3812 sip
->proxy
.retries
++;
3813 /* do proxy authentication */
3815 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
3817 fill_auth(sip
, ptmp
, &sip
->proxy
);
3818 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
3819 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
3820 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
3822 resend
= sipmsg_to_string(trans
->msg
);
3823 /* resend request */
3824 sendout_pkt(sip
->gc
, resend
);
3827 if (msg
->response
== 100 || msg
->response
== 180) {
3828 /* ignore provisional response */
3829 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
3831 sip
->proxy
.retries
= 0;
3832 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
3833 if (msg
->response
== 401)
3835 sip
->registrar
.retries
++;
3836 sip
->registrar
.expires
= 0;
3840 sip
->registrar
.retries
= 0;
3842 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
3844 if (msg
->response
== 401) {
3845 gchar
*resend
, *auth
, *ptmp
;
3847 if (sip
->registrar
.retries
> 4) return;
3848 sip
->registrar
.retries
++;
3850 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3851 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
3853 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3856 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
3858 fill_auth(sip
, ptmp
, &sip
->registrar
);
3859 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
3860 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
3861 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
3863 //sipmsg_remove_header(trans->msg, "Authorization");
3864 //sipmsg_add_header(trans->msg, "Authorization", auth);
3866 resend
= sipmsg_to_string(trans
->msg
);
3867 /* resend request */
3868 sendout_pkt(sip
->gc
, resend
);
3873 if (trans
->callback
) {
3874 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
3875 /* call the callback to process response*/
3876 (trans
->callback
)(sip
, msg
, trans
);
3878 /* Not sure if this is needed or what needs to be done
3879 but transactions seem to be removed prematurely so
3880 this only removes them if the response is 200 OK */
3881 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
3882 /*Has a bug and it's unneccesary*/
3883 /*transactions_remove(sip, trans);*/
3889 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction");
3893 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
3897 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
3905 /* according to the RFC remove CRLF at the beginning */
3906 while (*cur
== '\r' || *cur
== '\n') {
3909 if (cur
!= conn
->inbuf
) {
3910 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
3911 conn
->inbufused
= strlen(conn
->inbuf
);
3914 /* Received a full Header? */
3915 sip
->processing_input
= TRUE
;
3916 while (sip
->processing_input
&&
3917 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
3918 time_t currtime
= time(NULL
);
3921 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
3922 msg
= sipmsg_parse_header(conn
->inbuf
);
3925 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
3926 if (restlen
>= msg
->bodylen
) {
3927 dummy
= g_malloc(msg
->bodylen
+ 1);
3928 memcpy(dummy
, cur
, msg
->bodylen
);
3929 dummy
[msg
->bodylen
] = '\0';
3931 cur
+= msg
->bodylen
;
3932 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
3933 conn
->inbufused
= strlen(conn
->inbuf
);
3935 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
3936 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
3942 purple_debug_info("sipe", "body:\n%s", msg->body);
3945 // Verify the signature before processing it
3946 if (sip
->registrar
.ntlm_key
) {
3947 struct sipmsg_breakdown msgbd
;
3948 gchar
*signature_input_str
;
3949 gchar
*signature
= NULL
;
3952 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
3953 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
3954 if (signature_input_str
!= NULL
) {
3955 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
3957 g_free(signature_input_str
);
3959 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
3961 if (signature
!= NULL
) {
3962 if (rspauth
!= NULL
) {
3963 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
3964 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
3965 process_input_message(sip
, msg
);
3967 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
3968 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
3969 sip
->gc
->wants_to_die
= TRUE
;
3971 } else if (msg
->response
== 401) {
3972 purple_connection_error(sip
->gc
, _("Wrong Password"));
3973 sip
->gc
->wants_to_die
= TRUE
;
3979 sipmsg_breakdown_free(&msgbd
);
3981 process_input_message(sip
, msg
);
3988 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
3990 PurpleConnection
*gc
= data
;
3991 struct sipe_account_data
*sip
= gc
->proto_data
;
3996 static char buffer
[65536];
3997 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
3999 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
4000 msg
= sipmsg_parse_msg(buffer
);
4001 if (msg
) process_input_message(sip
, msg
);
4005 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
4007 struct sipe_account_data
*sip
= gc
->proto_data
;
4008 PurpleSslConnection
*gsc
= sip
->gsc
;
4010 purple_debug_error("sipe", "%s",debug
);
4011 purple_connection_error(gc
, msg
);
4013 /* Invalidate this connection. Next send will open a new one */
4015 connection_remove(sip
, gsc
->fd
);
4016 purple_ssl_close(gsc
);
4022 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
4024 PurpleConnection
*gc
= data
;
4025 struct sipe_account_data
*sip
;
4026 struct sip_connection
*conn
;
4028 gboolean firstread
= TRUE
;
4030 /* NOTE: This check *IS* necessary */
4031 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
4032 purple_ssl_close(gsc
);
4036 sip
= gc
->proto_data
;
4037 conn
= connection_find(sip
, gsc
->fd
);
4039 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
4040 gc
->wants_to_die
= TRUE
;
4041 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
4045 /* Read all available data from the SSL connection */
4047 /* Increase input buffer size as needed */
4048 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
4049 conn
->inbuflen
+= SIMPLE_BUF_INC
;
4050 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
4051 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
4054 /* Try to read as much as there is space left in the buffer */
4055 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
4056 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
4058 if (len
< 0 && errno
== EAGAIN
) {
4059 /* Try again later */
4061 } else if (len
< 0) {
4062 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
4064 } else if (firstread
&& (len
== 0)) {
4065 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
4069 conn
->inbufused
+= len
;
4072 /* Equivalence indicates that there is possibly more data to read */
4073 } while (len
== readlen
);
4075 conn
->inbuf
[conn
->inbufused
] = '\0';
4076 process_input(sip
, conn
);
4080 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
4082 PurpleConnection
*gc
= data
;
4083 struct sipe_account_data
*sip
= gc
->proto_data
;
4085 struct sip_connection
*conn
= connection_find(sip
, source
);
4087 purple_debug_error("sipe", "Connection not found!\n");
4091 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
4092 conn
->inbuflen
+= SIMPLE_BUF_INC
;
4093 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
4096 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
4098 if (len
< 0 && errno
== EAGAIN
)
4100 else if (len
<= 0) {
4101 purple_debug_info("sipe", "sipe_input_cb: read error\n");
4102 connection_remove(sip
, source
);
4103 if (sip
->fd
== source
) sip
->fd
= -1;
4107 conn
->inbufused
+= len
;
4108 conn
->inbuf
[conn
->inbufused
] = '\0';
4110 process_input(sip
, conn
);
4113 /* Callback for new connections on incoming TCP port */
4114 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
4116 PurpleConnection
*gc
= data
;
4117 struct sipe_account_data
*sip
= gc
->proto_data
;
4118 struct sip_connection
*conn
;
4120 int newfd
= accept(source
, NULL
, NULL
);
4122 conn
= connection_create(sip
, newfd
);
4124 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4127 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
4129 PurpleConnection
*gc
= data
;
4130 struct sipe_account_data
*sip
;
4131 struct sip_connection
*conn
;
4133 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4141 purple_connection_error(gc
, _("Could not connect"));
4145 sip
= gc
->proto_data
;
4147 sip
->last_keepalive
= time(NULL
);
4149 conn
= connection_create(sip
, source
);
4153 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4156 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
4158 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
4159 if (sip
== NULL
) return;
4164 static guint
sipe_ht_hash_nick(const char *nick
)
4166 char *lc
= g_utf8_strdown(nick
, -1);
4167 guint bucket
= g_str_hash(lc
);
4173 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
4175 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
4178 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
4180 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4182 sip
->listen_data
= NULL
;
4184 if (listenfd
== -1) {
4185 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4191 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
4192 sip
->listenfd
= sip
->fd
;
4194 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
4196 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
4200 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
4202 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4205 sip
->query_data
= NULL
;
4207 if (!hosts
|| !hosts
->data
) {
4208 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
4212 addr_size
= GPOINTER_TO_INT(hosts
->data
);
4213 hosts
= g_slist_remove(hosts
, hosts
->data
);
4214 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
4215 g_free(hosts
->data
);
4216 hosts
= g_slist_remove(hosts
, hosts
->data
);
4218 hosts
= g_slist_remove(hosts
, hosts
->data
);
4219 g_free(hosts
->data
);
4220 hosts
= g_slist_remove(hosts
, hosts
->data
);
4223 /* create socket for incoming connections */
4224 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
4225 sipe_udp_host_resolved_listen_cb
, sip
);
4226 if (sip
->listen_data
== NULL
) {
4227 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4232 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
4235 PurpleConnection
*gc
= data
;
4236 struct sipe_account_data
*sip
;
4238 /* If the connection is already disconnected, we don't need to do anything else */
4239 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4242 sip
= gc
->proto_data
;
4247 case PURPLE_SSL_CONNECT_FAILED
:
4248 purple_connection_error(gc
, _("Connection Failed"));
4250 case PURPLE_SSL_HANDSHAKE_FAILED
:
4251 purple_connection_error(gc
, _("SSL Handshake Failed"));
4253 case PURPLE_SSL_CERTIFICATE_INVALID
:
4254 purple_connection_error(gc
, _("SSL Certificate Invalid"));
4260 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
4262 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4263 PurpleProxyConnectData
*connect_data
;
4265 sip
->listen_data
= NULL
;
4267 sip
->listenfd
= listenfd
;
4268 if (sip
->listenfd
== -1) {
4269 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4273 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
4274 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
4275 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
4276 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
4277 sipe_newconn_cb
, sip
->gc
);
4278 purple_debug_info("sipe", "connecting to %s port %d\n",
4279 sip
->realhostname
, sip
->realport
);
4280 /* open tcp connection to the server */
4281 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
4282 sip
->realport
, login_cb
, sip
->gc
);
4284 if (connect_data
== NULL
) {
4285 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
4290 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
4292 PurpleAccount
*account
= sip
->account
;
4293 PurpleConnection
*gc
= sip
->gc
;
4295 if (purple_account_get_bool(account
, "useport", FALSE
)) {
4296 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
4297 port
= purple_account_get_int(account
, "port", 0);
4299 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
4302 sip
->realhostname
= hostname
;
4303 sip
->realport
= port
;
4305 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
4308 /* TODO: is there a good default grow size? */
4309 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
4310 sip
->txbuf
= purple_circ_buffer_new(0);
4312 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
4314 if (!purple_ssl_is_supported()) {
4315 gc
->wants_to_die
= TRUE
;
4316 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
4320 purple_debug_info("sipe", "using SSL\n");
4322 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
4323 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
4324 if (sip
->gsc
== NULL
) {
4325 purple_connection_error(gc
, _("Could not create SSL context"));
4328 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
4330 purple_debug_info("sipe", "using UDP\n");
4332 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
4333 if (sip
->query_data
== NULL
) {
4334 purple_connection_error(gc
, _("Could not resolve hostname"));
4338 purple_debug_info("sipe", "using TCP\n");
4339 /* create socket for incoming connections */
4340 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
4341 sipe_tcp_connect_listen_cb
, sip
);
4342 if (sip
->listen_data
== NULL
) {
4343 purple_connection_error(gc
, _("Could not create listen socket"));
4349 /* Service list for autodection */
4350 static const struct sipe_service_data service_autodetect
[] = {
4351 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4352 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4353 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4354 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4358 /* Service list for SSL/TLS */
4359 static const struct sipe_service_data service_tls
[] = {
4360 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4361 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4365 /* Service list for TCP */
4366 static const struct sipe_service_data service_tcp
[] = {
4367 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4368 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4372 /* Service list for UDP */
4373 static const struct sipe_service_data service_udp
[] = {
4374 { "sip", "udp", SIPE_TRANSPORT_UDP
},
4378 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
4379 static void resolve_next_service(struct sipe_account_data
*sip
,
4380 const struct sipe_service_data
*start
)
4383 sip
->service_data
= start
;
4385 sip
->service_data
++;
4386 if (sip
->service_data
->service
== NULL
) {
4388 /* Try connecting to the SIP hostname directly */
4389 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
4390 if (sip
->auto_transport
) {
4391 // If SSL is supported, default to using it; OCS servers aren't configured
4392 // by default to accept TCP
4393 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
4394 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
4395 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
4398 hostname
= g_strdup(sip
->sipdomain
);
4399 create_connection(sip
, hostname
, 0);
4404 /* Try to resolve next service */
4405 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
4406 sip
->service_data
->transport
,
4411 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
4413 struct sipe_account_data
*sip
= data
;
4415 sip
->srv_query_data
= NULL
;
4417 /* find the host to connect to */
4419 gchar
*hostname
= g_strdup(resp
->hostname
);
4420 int port
= resp
->port
;
4421 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
4425 sip
->transport
= sip
->service_data
->type
;
4427 create_connection(sip
, hostname
, port
);
4429 resolve_next_service(sip
, NULL
);
4433 static void sipe_login(PurpleAccount
*account
)
4435 PurpleConnection
*gc
;
4436 struct sipe_account_data
*sip
;
4437 gchar
**signinname_login
, **userserver
, **domain_user
;
4438 const char *transport
;
4440 const char *username
= purple_account_get_username(account
);
4441 gc
= purple_account_get_connection(account
);
4443 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
4444 gc
->wants_to_die
= TRUE
;
4445 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
4449 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
4450 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
4451 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
4453 sip
->account
= account
;
4454 sip
->registerexpire
= 900;
4455 sip
->reregister_set
= FALSE
;
4456 sip
->reauthenticate_set
= FALSE
;
4457 sip
->subscribed
= FALSE
;
4458 sip
->subscribed_buddies
= FALSE
;
4460 signinname_login
= g_strsplit(username
, ",", 2);
4462 userserver
= g_strsplit(signinname_login
[0], "@", 2);
4463 purple_connection_set_display_name(gc
, userserver
[0]);
4464 sip
->username
= g_strjoin("@", userserver
[0], userserver
[1], NULL
);
4465 sip
->sipdomain
= g_strdup(userserver
[1]);
4467 domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
4468 sip
->authdomain
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[0]) : NULL
;
4469 sip
->authuser
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[1]) : (signinname_login
? g_strdup(signinname_login
[1]) : NULL
);
4471 sip
->password
= g_strdup(purple_connection_get_password(gc
));
4473 g_strfreev(userserver
);
4474 g_strfreev(domain_user
);
4475 g_strfreev(signinname_login
);
4477 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
4479 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
4481 /* TODO: Set the status correctly. */
4482 sip
->status
= g_strdup("available");
4484 transport
= purple_account_get_string(account
, "transport", "auto");
4485 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
4486 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
4489 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
4490 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
4491 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
4492 } else if (strcmp(transport
, "auto") == 0) {
4493 sip
->auto_transport
= TRUE
;
4494 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
4495 } else if (strcmp(transport
, "tls") == 0) {
4496 resolve_next_service(sip
, service_tls
);
4497 } else if (strcmp(transport
, "tcp") == 0) {
4498 resolve_next_service(sip
, service_tcp
);
4500 resolve_next_service(sip
, service_udp
);
4504 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
4506 connection_free_all(sip
);
4508 if (sip
->query_data
!= NULL
)
4509 purple_dnsquery_destroy(sip
->query_data
);
4510 sip
->query_data
== NULL
;
4512 if (sip
->srv_query_data
!= NULL
)
4513 purple_srv_cancel(sip
->srv_query_data
);
4514 sip
->srv_query_data
= NULL
;
4516 if (sip
->listen_data
!= NULL
)
4517 purple_network_listen_cancel(sip
->listen_data
);
4518 sip
->listen_data
= NULL
;
4520 if (sip
->gsc
!= NULL
)
4521 purple_ssl_close(sip
->gsc
);
4524 sipe_auth_free(&sip
->registrar
);
4525 sipe_auth_free(&sip
->proxy
);
4528 purple_circ_buffer_destroy(sip
->txbuf
);
4531 g_free(sip
->realhostname
);
4532 sip
->realhostname
= NULL
;
4535 purple_input_remove(sip
->listenpa
);
4537 if (sip
->tx_handler
)
4538 purple_input_remove(sip
->tx_handler
);
4539 sip
->tx_handler
= 0;
4540 if (sip
->resendtimeout
)
4541 purple_timeout_remove(sip
->resendtimeout
);
4542 sip
->resendtimeout
= 0;
4543 if (sip
->timeouts
) {
4544 GSList
*entry
= sip
->timeouts
;
4546 struct scheduled_action
*sched_action
= entry
->data
;
4547 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
4548 purple_timeout_remove(sched_action
->timeout_handler
);
4549 g_free(sched_action
->payload
);
4550 g_free(sched_action
->name
);
4551 g_free(sched_action
);
4552 entry
= entry
->next
;
4555 g_slist_free(sip
->timeouts
);
4558 g_free(sip
->contact
);
4559 sip
->contact
= NULL
;
4561 g_free(sip
->regcallid
);
4562 sip
->regcallid
= NULL
;
4565 sip
->processing_input
= FALSE
;
4568 static void sipe_close(PurpleConnection
*gc
)
4570 struct sipe_account_data
*sip
= gc
->proto_data
;
4573 /* leave all conversations */
4574 im_session_close_all(sip
);
4577 do_register_exp(sip
, 0);
4579 sipe_connection_cleanup(sip
);
4580 g_free(sip
->sipdomain
);
4581 g_free(sip
->username
);
4582 g_free(sip
->password
);
4583 g_free(sip
->authdomain
);
4584 g_free(sip
->authuser
);
4586 g_free(gc
->proto_data
);
4587 gc
->proto_data
= NULL
;
4590 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
4592 PurpleAccount
*acct
= purple_connection_get_account(gc
);
4593 char *id
= g_strdup_printf("sip:%s", (char *)g_list_nth_data(row
, 0));
4594 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
4596 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
4597 purple_conversation_present(conv
);
4601 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
4604 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
4605 g_list_nth_data(row
, 0), NULL
, g_list_nth_data(row
, 1));
4608 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,struct transaction
*tc
)
4610 PurpleNotifySearchResults
*results
;
4611 PurpleNotifySearchColumn
*column
;
4612 xmlnode
*searchResults
;
4614 int match_count
= 0;
4615 gboolean more
= FALSE
;
4618 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4619 if (!searchResults
) {
4620 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
4624 results
= purple_notify_searchresults_new();
4626 if (results
== NULL
) {
4627 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
4628 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
4630 xmlnode_free(searchResults
);
4634 column
= purple_notify_searchresults_column_new(_("User Name"));
4635 purple_notify_searchresults_column_add(results
, column
);
4637 column
= purple_notify_searchresults_column_new(_("Name"));
4638 purple_notify_searchresults_column_add(results
, column
);
4640 column
= purple_notify_searchresults_column_new(_("Company"));
4641 purple_notify_searchresults_column_add(results
, column
);
4643 column
= purple_notify_searchresults_column_new(_("Country"));
4644 purple_notify_searchresults_column_add(results
, column
);
4646 column
= purple_notify_searchresults_column_new(_("Email"));
4647 purple_notify_searchresults_column_add(results
, column
);
4649 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
4652 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
4653 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
4654 g_strfreev(uri_parts
);
4656 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
4657 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
4658 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
4659 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
4661 purple_notify_searchresults_row_add(results
, row
);
4665 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
4666 char *data
= xmlnode_get_data_unescaped(mrow
);
4667 more
= (g_strcasecmp(data
, "true") == 0);
4671 secondary
= g_strdup_printf(
4672 dngettext(GETTEXT_PACKAGE
,
4673 "Found %d contact%s:",
4674 "Found %d contacts%s:", match_count
),
4675 match_count
, more
? _(" (more matched your query)") : "");
4677 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
4678 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
4679 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
4682 xmlnode_free(searchResults
);
4686 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
4688 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
4689 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
4693 PurpleRequestField
*field
= entries
->data
;
4694 const char *id
= purple_request_field_get_id(field
);
4695 const char *value
= purple_request_field_string_get_value(field
);
4697 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
4699 if (value
!= NULL
) attrs
[i
++] = g_strdup_printf(SIPE_SOAP_SEARCH_ROW
, id
, value
);
4700 } while ((entries
= g_list_next(entries
)) != NULL
);
4704 gchar
*query
= g_strjoinv(NULL
, attrs
);
4705 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
4706 send_soap_request_with_cb(gc
->proto_data
, body
,
4707 (TransCallback
) process_search_contact_response
, NULL
);
4715 static void sipe_show_find_contact(PurplePluginAction
*action
)
4717 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
4718 PurpleRequestFields
*fields
;
4719 PurpleRequestFieldGroup
*group
;
4720 PurpleRequestField
*field
;
4722 fields
= purple_request_fields_new();
4723 group
= purple_request_field_group_new(NULL
);
4724 purple_request_fields_add_group(fields
, group
);
4726 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
4727 purple_request_field_group_add_field(group
, field
);
4728 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
4729 purple_request_field_group_add_field(group
, field
);
4730 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
4731 purple_request_field_group_add_field(group
, field
);
4732 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
4733 purple_request_field_group_add_field(group
, field
);
4735 purple_request_fields(gc
,
4737 _("Search for a Contact"),
4738 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
4740 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
4742 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
4745 GList
*sipe_actions(PurplePlugin
*plugin
, gpointer context
)
4748 PurplePluginAction
*act
;
4750 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
4751 menu
= g_list_prepend(menu
, act
);
4753 menu
= g_list_reverse(menu
);
4758 static void dummy_permit_deny(PurpleConnection
*gc
)
4762 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
4768 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
4774 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
4778 static char *sipe_status_text(PurpleBuddy
*buddy
)
4780 struct sipe_account_data
*sip
;
4781 struct sipe_buddy
*sbuddy
;
4784 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
4785 if (sip
) //happens on pidgin exit
4787 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
4788 if (sbuddy
&& sbuddy
->annotation
)
4790 text
= g_strdup(sbuddy
->annotation
);
4797 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
4799 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
4800 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
4801 struct sipe_account_data
*sip
;
4802 struct sipe_buddy
*sbuddy
;
4803 char *annotation
= NULL
;
4805 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
4806 if (sip
) //happens on pidgin exit
4808 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
4811 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
4816 if (purple_presence_is_online(presence
))
4818 purple_notify_user_info_add_pair(user_info
, _("Status"), purple_status_get_name(status
));
4823 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
4830 sipe_get_account_text_table(PurpleAccount
*account
)
4833 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
4834 g_hash_table_insert(table
, "login_label", (gpointer
)_("Sign-In Name..."));
4838 static PurpleBuddy
*
4839 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
4842 const gchar
*server_alias
, *email
;
4843 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
4845 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
4847 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
4849 server_alias
= g_strdup(purple_buddy_get_server_alias(buddy
));
4851 purple_blist_server_alias_buddy(clone
, server_alias
);
4854 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
4856 purple_blist_node_set_string((PurpleBlistNode
*)clone
, "email", email
);
4859 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
4861 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
4866 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
4868 PurpleBuddy
*buddy
, *b
;
4869 PurpleConnection
*gc
;
4870 PurpleGroup
* group
= purple_find_group(group_name
);
4872 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
4874 buddy
= (PurpleBuddy
*)node
;
4876 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
4877 gc
= purple_account_get_connection(buddy
->account
);
4879 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
4881 b
= purple_blist_add_buddy_clone(group
, buddy
);
4884 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
4888 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
4891 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
4893 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
4896 char *mailto
= g_strdup_printf("mailto:%s", email
);
4897 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
4901 char *const parmList
[] = {mailto
, NULL
};
4902 if ((pid
= fork()) == -1)
4904 purple_debug_info("sipe", "fork() error\n");
4908 execvp("xdg-email", parmList
);
4909 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
4917 //@TODO resolve env variable %WINDIR% first
4918 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
4921 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
4930 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
4935 * A menu which appear when right-clicking on buddy in contact list.
4938 sipe_buddy_menu(PurpleBuddy
*buddy
)
4940 PurpleBlistNode
*g_node
;
4941 PurpleGroup
*group
, *gr_parent
;
4942 PurpleMenuAction
*act
;
4944 GList
*menu_groups
= NULL
;
4946 act
= purple_menu_action_new(_("Send Email..."),
4947 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
4949 menu
= g_list_prepend(menu
, act
);
4951 gr_parent
= purple_buddy_get_group(buddy
);
4952 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
4953 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
4956 group
= (PurpleGroup
*)g_node
;
4957 if (group
== gr_parent
)
4960 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
4963 act
= purple_menu_action_new(purple_group_get_name(group
),
4964 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
4966 menu_groups
= g_list_prepend(menu_groups
, act
);
4968 menu_groups
= g_list_reverse(menu_groups
);
4970 act
= purple_menu_action_new(_("Copy to"),
4973 menu
= g_list_prepend(menu
, act
);
4974 menu
= g_list_reverse(menu
);
4979 GList
*sipe_blist_node_menu(PurpleBlistNode
*node
) {
4980 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
4981 return sipe_buddy_menu((PurpleBuddy
*) node
);
4987 static void sipe_get_info(PurpleConnection
*gc
, const char *username
) {
4988 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
4989 PurpleBuddy
*pbuddy
;
4990 struct sipe_account_data
*sip
;
4991 struct sipe_buddy
*sbuddy
;
4993 const char *device_name
= NULL
;
4995 const char *server_alias
;
4997 purple_debug_info("sipe", "Fetching %s's user info for %s\n", username
,
4998 gc
->account
->username
);
5000 pbuddy
= purple_find_buddy(gc
->account
, username
);
5001 alias
= purple_buddy_get_local_alias(pbuddy
);
5002 server_alias
= purple_buddy_get_server_alias(pbuddy
);
5004 sip
= (struct sipe_account_data
*)gc
->proto_data
;
5007 sbuddy
= g_hash_table_lookup(sip
->buddies
, username
);
5010 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
5017 purple_notify_user_info_add_pair(info
, _("Nickname"), server_alias
);
5020 // same as server alias, do not present
5021 alias
= alias
&& server_alias
&& !strcmp(alias
, server_alias
) ? NULL
: alias
;
5024 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
5027 email
= purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email");
5030 purple_notify_user_info_add_pair(info
, _("Email"), email
);
5035 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
5038 /* show a buddy's user info in a nice dialog box */
5039 purple_notify_userinfo(gc
, /* connection the buddy info came through */
5040 username
, /* buddy's username */
5042 NULL
, /* callback called when dialog closed */
5043 NULL
); /* userdata for callback */
5046 static PurplePlugin
*my_protocol
= NULL
;
5048 static PurplePluginProtocolInfo prpl_info
=
5051 NULL
, /* user_splits */
5052 NULL
, /* protocol_options */
5053 NO_BUDDY_ICONS
, /* icon_spec */
5054 sipe_list_icon
, /* list_icon */
5055 NULL
, /* list_emblems */
5056 sipe_status_text
, /* status_text */
5057 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
5058 sipe_status_types
, /* away_states */
5059 sipe_blist_node_menu
, /* blist_node_menu */
5060 NULL
, /* chat_info */
5061 NULL
, /* chat_info_defaults */
5062 sipe_login
, /* login */
5063 sipe_close
, /* close */
5064 sipe_im_send
, /* send_im */
5065 NULL
, /* set_info */ // TODO maybe
5066 sipe_send_typing
, /* send_typing */
5067 sipe_get_info
, /* get_info */
5068 sipe_set_status
, /* set_status */
5069 NULL
, /* set_idle */
5070 NULL
, /* change_passwd */
5071 sipe_add_buddy
, /* add_buddy */
5072 NULL
, /* add_buddies */
5073 sipe_remove_buddy
, /* remove_buddy */
5074 NULL
, /* remove_buddies */
5075 sipe_add_permit
, /* add_permit */
5076 sipe_add_deny
, /* add_deny */
5077 sipe_add_deny
, /* rem_permit */
5078 sipe_add_permit
, /* rem_deny */
5079 dummy_permit_deny
, /* set_permit_deny */
5080 NULL
, /* join_chat */
5081 NULL
, /* reject_chat */
5082 NULL
, /* get_chat_name */
5083 NULL
, /* chat_invite */
5084 NULL
, /* chat_leave */
5085 NULL
, /* chat_whisper */
5086 NULL
, /* chat_send */
5087 sipe_keep_alive
, /* keepalive */
5088 NULL
, /* register_user */
5089 NULL
, /* get_cb_info */ // deprecated
5090 NULL
, /* get_cb_away */ // deprecated
5091 sipe_alias_buddy
, /* alias_buddy */
5092 sipe_group_buddy
, /* group_buddy */
5093 sipe_rename_group
, /* rename_group */
5094 NULL
, /* buddy_free */
5095 sipe_convo_closed
, /* convo_closed */
5096 purple_normalize_nocase
, /* normalize */
5097 NULL
, /* set_buddy_icon */
5098 sipe_remove_group
, /* remove_group */
5099 NULL
, /* get_cb_real_name */ // TODO?
5100 NULL
, /* set_chat_topic */
5101 NULL
, /* find_blist_chat */
5102 NULL
, /* roomlist_get_list */
5103 NULL
, /* roomlist_cancel */
5104 NULL
, /* roomlist_expand_category */
5105 NULL
, /* can_receive_file */
5106 NULL
, /* send_file */
5107 NULL
, /* new_xfer */
5108 NULL
, /* offline_message */
5109 NULL
, /* whiteboard_prpl_ops */
5110 sipe_send_raw
, /* send_raw */
5111 NULL
, /* roomlist_room_serialize */
5112 NULL
, /* unregister_user */
5113 NULL
, /* send_attention */
5114 NULL
, /* get_attention_types */
5116 sizeof(PurplePluginProtocolInfo
), /* struct_size */
5117 sipe_get_account_text_table
, /* get_account_text_table */
5121 static PurplePluginInfo info
= {
5122 PURPLE_PLUGIN_MAGIC
,
5123 PURPLE_MAJOR_VERSION
,
5124 PURPLE_MINOR_VERSION
,
5125 PURPLE_PLUGIN_PROTOCOL
, /**< type */
5126 NULL
, /**< ui_requirement */
5128 NULL
, /**< dependencies */
5129 PURPLE_PRIORITY_DEFAULT
, /**< priority */
5130 "prpl-sipe", /**< id */
5131 "Microsoft LCS/OCS", /**< name */
5132 VERSION
, /**< version */
5133 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
5134 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
5135 "Anibal Avelar <avelar@gmail.com>, " /**< author */
5136 "Gabriel Burt <gburt@novell.com>", /**< author */
5137 PURPLE_WEBSITE
, /**< homepage */
5138 sipe_plugin_load
, /**< load */
5139 sipe_plugin_unload
, /**< unload */
5140 sipe_plugin_destroy
, /**< destroy */
5141 NULL
, /**< ui_info */
5142 &prpl_info
, /**< extra_info */
5151 static void init_plugin(PurplePlugin
*plugin
)
5153 PurpleAccountUserSplit
*split
;
5154 PurpleAccountOption
*option
;
5157 purple_debug_info(PACKAGE
, "bindtextdomain = %s", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
5158 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s",
5159 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
5162 purple_plugin_register(plugin
);
5164 split
= purple_account_user_split_new(_("Login \n domain\\user or\n someone@linux.com "), NULL
, ',');
5165 purple_account_user_split_set_reverse(split
, FALSE
);
5166 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
5168 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
5169 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5170 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
5171 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5173 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
5174 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5175 // Translators: noun (networking port)
5176 option
= purple_account_option_int_new(_("Port"), "port", 5061);
5177 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5179 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
5180 purple_account_option_add_list_item(option
, _("Auto"), "auto");
5181 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
5182 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
5183 purple_account_option_add_list_item(option
, _("UDP"), "udp");
5184 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5186 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
5187 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
5189 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
5190 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5192 // TODO commented out so won't show in the preferences until we fix krb message signing
5193 /*option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
5194 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5196 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
5197 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
5198 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5201 option
= purple_account_option_bool_new(_("Use Client-specified Keepalive"), "clientkeepalive", FALSE
);
5202 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5203 option
= purple_account_option_int_new(_("Keepalive Timeout"), "keepalive", 300);
5204 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5205 my_protocol
= plugin
;
5208 /* I had to redefined the function for it load, but works */
5209 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
5210 plugin
->info
= &(info
);
5211 init_plugin((plugin
));
5212 sipe_plugin_load((plugin
));
5213 return purple_plugin_register(plugin
);