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_name_batched(struct sipe_account_data
*sip
, const char * buddy_name
);
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
;
249 const char *authuser
;
250 //const char *krb5_realm;
252 //gchar *krb5_token = NULL;
254 authdomain
= sip
->authdomain
;
255 authuser
= sip
->authuser
;
257 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
258 // and do error checking
260 // KRB realm should always be uppercase
261 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
263 if (sip
->realhostname
) {
264 host
= sip
->realhostname
;
265 } else if (purple_account_get_bool(sip
->account
, "useproxy", TRUE
)) {
266 host
= purple_account_get_string(sip
->account
, "proxy", "");
268 host
= sip
->sipdomain
;
271 /*gboolean new_auth = krb5_auth.gss_context == NULL;
273 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
276 if (new_auth || force_reauth) {
277 krb5_token = krb5_auth.base64_token;
280 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
281 krb5_token = krb5_auth.base64_token;*/
283 if (!authuser
|| strlen(authuser
) < 1) {
284 authuser
= sip
->username
;
287 if (auth
->type
== AUTH_TYPE_DIGEST
) { /* Digest */
288 sprintf(noncecount
, "%08d", auth
->nc
++);
289 response
= purple_cipher_http_digest_calculate_response(
290 "md5", method
, target
, NULL
, NULL
,
291 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
292 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
294 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
);
297 } else if (auth
->type
== AUTH_TYPE_NTLM
) { /* NTLM */
298 // If we have a signature for the message, include that
299 if (msg
->signature
) {
300 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
);
304 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
305 const gchar
*ntlm_key
;
307 #if GLIB_CHECK_VERSION(2,8,0)
308 const gchar
* hostname
= g_get_host_name();
310 static char hostname
[256];
311 int ret
= gethostname(hostname
, sizeof(hostname
));
312 hostname
[sizeof(hostname
) - 1] = '\0';
313 if (ret
== -1 || hostname
[0] == '\0') {
314 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Error when getting host name. Using \"localhost.\"\n");
316 strcpy(hostname
, "localhost");
319 /*const gchar * hostname = purple_get_host_name();*/
321 gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, hostname
, authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
322 auth
->ntlm_key
= (gchar
*)ntlm_key
;
323 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
328 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
330 } else if (auth
->type
== AUTH_TYPE_KERBEROS
) {
333 /*if (new_auth || force_reauth) {
334 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
336 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);
338 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
341 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
342 gchar * mic = "MICTODO";
343 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
344 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
345 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
346 //auth->opaque ? auth->opaque : "", auth->target);
347 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
352 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
355 sprintf(noncecount
, "%08d", auth
->nc
++);
356 response
= purple_cipher_http_digest_calculate_response(
357 "md5", method
, target
, NULL
, NULL
,
358 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
359 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
361 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
);
366 static char *parse_attribute(const char *attrname
, const char *source
)
368 const char *tmp
, *tmp2
;
370 int len
= strlen(attrname
);
372 if (!strncmp(source
, attrname
, len
)) {
374 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
376 retval
= g_strndup(tmp
, tmp2
- tmp
);
378 retval
= g_strdup(tmp
);
384 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
387 const char *authuser
;
390 //const char *krb5_realm;
393 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
394 // and do error checking
396 // KRB realm should always be uppercase
397 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
399 if (sip->realhostname) {
400 host = sip->realhostname;
401 } else if (purple_account_get_bool(sip->account, "useproxy", TRUE)) {
402 host = purple_account_get_string(sip->account, "proxy", "");
404 host = sip->sipdomain;
407 authuser
= sip
->authuser
;
409 if (!authuser
|| strlen(authuser
) < 1) {
410 authuser
= sip
->username
;
414 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
418 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
419 auth
->type
= AUTH_TYPE_NTLM
;
420 parts
= g_strsplit(hdr
+5, "\", ", 0);
423 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
424 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
425 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
428 if ((tmp
= parse_attribute("targetname=\"",
432 else if ((tmp
= parse_attribute("realm=\"",
436 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
443 if (!strstr(hdr
, "gssapi-data")) {
451 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
452 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
453 auth
->type
= AUTH_TYPE_KERBEROS
;
454 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
455 parts
= g_strsplit(hdr
+9, "\", ", 0);
458 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
459 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
460 /*if (krb5_auth.gss_context == NULL) {
461 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
463 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
466 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
468 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
470 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
480 auth
->type
= AUTH_TYPE_DIGEST
;
481 parts
= g_strsplit(hdr
, " ", 0);
483 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
486 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
493 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
495 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
496 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
502 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
504 PurpleConnection
*gc
= data
;
505 struct sipe_account_data
*sip
= gc
->proto_data
;
509 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
511 if (max_write
== 0) {
512 if (sip
->tx_handler
!= 0){
513 purple_input_remove(sip
->tx_handler
);
519 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
521 if (written
< 0 && errno
== EAGAIN
)
523 else if (written
<= 0) {
524 /*TODO: do we really want to disconnect on a failure to write?*/
525 purple_connection_error(gc
, _("Could not write"));
529 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
532 static void sipe_canwrite_cb_ssl(gpointer data
, gint src
, PurpleInputCondition cond
)
534 PurpleConnection
*gc
= data
;
535 struct sipe_account_data
*sip
= gc
->proto_data
;
539 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
541 if (max_write
== 0) {
542 if (sip
->tx_handler
!= 0) {
543 purple_input_remove(sip
->tx_handler
);
549 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
551 if (written
< 0 && errno
== EAGAIN
)
553 else if (written
<= 0) {
554 /*TODO: do we really want to disconnect on a failure to write?*/
555 purple_connection_error(gc
, _("Could not write"));
559 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
562 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
564 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
566 PurpleConnection
*gc
= data
;
567 struct sipe_account_data
*sip
;
568 struct sip_connection
*conn
;
570 if (!PURPLE_CONNECTION_IS_VALID(gc
))
578 purple_connection_error(gc
, _("Could not connect"));
582 sip
= gc
->proto_data
;
584 sip
->connecting
= FALSE
;
585 sip
->last_keepalive
= time(NULL
);
587 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
589 /* If there is more to write now, we need to register a handler */
590 if (sip
->txbuf
->bufused
> 0)
591 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
593 conn
= connection_create(sip
, source
);
594 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
597 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
599 struct sipe_account_data
*sip
;
600 struct sip_connection
*conn
;
602 if (!PURPLE_CONNECTION_IS_VALID(gc
))
604 if (gsc
) purple_ssl_close(gsc
);
608 sip
= gc
->proto_data
;
611 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
612 sip
->connecting
= FALSE
;
613 sip
->last_keepalive
= time(NULL
);
615 conn
= connection_create(sip
, gsc
->fd
);
617 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
622 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
624 PurpleConnection
*gc
= data
;
625 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
626 if (sip
== NULL
) return;
628 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
630 /* If there is more to write now */
631 if (sip
->txbuf
->bufused
> 0) {
632 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
637 static void sendlater(PurpleConnection
*gc
, const char *buf
)
639 struct sipe_account_data
*sip
= gc
->proto_data
;
641 if (!sip
->connecting
) {
642 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
643 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
644 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
646 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
647 purple_connection_error(gc
, _("Couldn't create socket"));
650 sip
->connecting
= TRUE
;
653 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
654 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
656 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
659 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
661 struct sipe_account_data
*sip
= gc
->proto_data
;
662 time_t currtime
= time(NULL
);
663 int writelen
= strlen(buf
);
665 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
666 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
667 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
668 purple_debug_info("sipe", "could not send packet\n");
677 if (sip
->tx_handler
) {
682 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
684 ret
= write(sip
->fd
, buf
, writelen
);
688 if (ret
< 0 && errno
== EAGAIN
)
690 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
695 if (ret
< writelen
) {
696 if (!sip
->tx_handler
){
698 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
701 sip
->tx_handler
= purple_input_add(sip
->fd
,
702 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
707 /* XXX: is it OK to do this? You might get part of a request sent
708 with part of another. */
709 if (sip
->txbuf
->bufused
> 0)
710 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
712 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
718 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
720 sendout_pkt(gc
, buf
);
724 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
726 GSList
*tmp
= msg
->headers
;
729 GString
*outstr
= g_string_new("");
730 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
732 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
733 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
734 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
735 tmp
= g_slist_next(tmp
);
737 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
738 sendout_pkt(sip
->gc
, outstr
->str
);
739 g_string_free(outstr
, TRUE
);
742 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
745 if (sip
->registrar
.ntlm_key
) {
746 struct sipmsg_breakdown msgbd
;
747 gchar
*signature_input_str
;
749 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
750 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
751 sip
->registrar
.ntlm_num
++;
752 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
753 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
754 if (signature_input_str
!= NULL
) {
755 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
756 msg
->rand
= g_strdup(msgbd
.rand
);
757 msg
->num
= g_strdup(msgbd
.num
);
758 g_free(signature_input_str
);
760 sipmsg_breakdown_free(&msgbd
);
763 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
764 buf
= auth_header(sip
, &sip
->registrar
, msg
);
765 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
766 sipmsg_add_header(msg
, "Authorization", buf
);
768 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
771 } 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")) {
772 sip
->registrar
.nc
= 3;
773 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
775 buf
= auth_header(sip
, &sip
->registrar
, msg
);
776 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
779 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
783 static char *get_contact(struct sipe_account_data
*sip
)
785 return g_strdup(sip
->contact
);
790 static char *get_contact_service(struct sipe_account_data *sip)
792 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()));
793 //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);
797 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
798 const char *text
, const char *body
)
802 GString
*outstr
= g_string_new("");
803 struct sipe_account_data
*sip
= gc
->proto_data
;
807 sipmsg_remove_header(msg
, "ms-user-data");
809 contact
= get_contact(sip
);
810 sipmsg_remove_header(msg
, "Contact");
811 sipmsg_add_header(msg
, "Contact", contact
);
814 /* When sending the acknowlegements and errors, the content length from the original
815 message is still here, but there is no body; we need to make sure we're sending the
816 correct content length */
817 sipmsg_remove_header(msg
, "Content-Length");
820 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
821 sipmsg_add_header(msg
, "Content-Length", len
);
823 sipmsg_remove_header(msg
, "Content-Type");
824 sipmsg_add_header(msg
, "Content-Length", "0");
827 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
828 //gchar * mic = "MICTODO";
829 msg
->response
= code
;
831 sipmsg_remove_header(msg
, "Authentication-Info");
832 sign_outgoing_message(msg
, sip
, msg
->method
);
834 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
837 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
838 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
840 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
841 tmp
= g_slist_next(tmp
);
843 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
844 sendout_pkt(gc
, outstr
->str
);
845 g_string_free(outstr
, TRUE
);
848 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
850 if (trans
->msg
) sipmsg_free(trans
->msg
);
851 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
855 static struct transaction
*
856 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
858 struct transaction
*trans
= g_new0(struct transaction
, 1);
859 trans
->time
= time(NULL
);
860 trans
->msg
= (struct sipmsg
*)msg
;
861 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
862 trans
->callback
= callback
;
863 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
867 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
869 struct transaction
*trans
;
870 GSList
*transactions
= sip
->transactions
;
871 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
873 while (transactions
) {
874 trans
= transactions
->data
;
875 if (!strcmp(trans
->cseq
, cseq
)) {
878 transactions
= transactions
->next
;
884 static struct transaction
*
885 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
886 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
887 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
889 struct sipe_account_data
*sip
= gc
->proto_data
;
890 const char *addh
= "";
893 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
894 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
895 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
896 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
897 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
898 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
899 gchar
*route
= strdup("");
900 gchar
*epid
= get_epid(); // TODO generate one per account/login
901 struct transaction
*trans
;
903 if (dialog
&& dialog
->routes
)
905 GSList
*iter
= dialog
->routes
;
910 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
912 iter
= g_slist_next(iter
);
916 if (!ourtag
&& !dialog
) {
920 if (!strcmp(method
, "REGISTER")) {
921 if (sip
->regcallid
) {
923 callid
= g_strdup(sip
->regcallid
);
925 sip
->regcallid
= g_strdup(callid
);
929 if (addheaders
) addh
= addheaders
;
931 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
932 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
933 "From: <sip:%s>%s%s;epid=%s\r\n"
934 "To: <%s>%s%s%s%s\r\n"
935 "Max-Forwards: 70\r\n"
940 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
942 dialog
&& dialog
->request
? dialog
->request
: url
,
943 TRANSPORT_DESCRIPTOR
,
944 purple_network_get_my_ip(-1),
946 branch
? ";branch=" : "",
947 branch
? branch
: "",
949 ourtag
? ";tag=" : "",
950 ourtag
? ourtag
: "",
953 theirtag
? ";tag=" : "",
954 theirtag
? theirtag
: "",
955 theirepid
? ";epid=" : "",
956 theirepid
? theirepid
: "",
957 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
963 body
? strlen(body
) : 0,
967 //printf ("parsing msg buf:\n%s\n\n", buf);
968 msg
= sipmsg_parse_msg(buf
);
979 sign_outgoing_message (msg
, sip
, method
);
981 buf
= sipmsg_to_string (msg
);
983 /* add to ongoing transactions */
984 trans
= transactions_add_buf(sip
, msg
, tc
);
985 sendout_pkt(gc
, buf
);
991 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
993 gchar
*from
= g_strdup_printf("sip:%s", sip
->username
);
994 gchar
*contact
= get_contact(sip
);
995 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
996 "Content-Type: application/SOAP+xml\r\n",contact
);
998 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
999 tr
->payload
= payload
;
1005 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1007 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
1010 static char *get_contact_register(struct sipe_account_data
*sip
)
1012 char *epid
= get_epid();
1013 char *uuid
= generateUUIDfromEPID(epid
);
1014 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
);
1020 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1022 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
1023 char *to
= g_strdup_printf("sip:%s", sip
->username
);
1024 char *contact
= get_contact_register(sip
);
1025 char *hdr
= g_strdup_printf("Contact: %s\r\n"
1026 "Supported: gruu-10, adhoclist, msrtc-event-categories\r\n"
1027 "Event: registration\r\n"
1028 "Allow-Events: presence\r\n"
1029 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1030 "Expires: %d\r\n", contact
,expire
);
1033 sip
->registerstatus
= 1;
1035 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1036 process_register_response
);
1043 static void do_register_cb(struct sipe_account_data
*sip
)
1045 do_register_exp(sip
, sip
->registerexpire
);
1046 sip
->reregister_set
= FALSE
;
1049 static void do_register(struct sipe_account_data
*sip
)
1051 do_register_exp(sip
, sip
->registerexpire
);
1055 * Returns URI from provided To or From header.
1057 * Needs to g_free() after use.
1059 * @return URI with sip: prefix
1061 static gchar
*parse_from(const gchar
*hdr
)
1064 const gchar
*tmp
, *tmp2
= hdr
;
1066 if (!hdr
) return NULL
;
1067 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1068 tmp
= strchr(hdr
, '<');
1070 /* i hate the different SIP UA behaviours... */
1071 if (tmp
) { /* sip address in <...> */
1073 tmp
= strchr(tmp2
, '>');
1075 from
= g_strndup(tmp2
, tmp
- tmp2
);
1077 purple_debug_info("sipe", "found < without > in From\n");
1081 tmp
= strchr(tmp2
, ';');
1083 from
= g_strndup(tmp2
, tmp
- tmp2
);
1085 from
= g_strdup(tmp2
);
1088 purple_debug_info("sipe", "got %s\n", from
);
1092 static xmlnode
* xmlnode_get_descendant(xmlnode
* parent
, ...)
1095 xmlnode
* node
= NULL
;
1098 va_start(args
, parent
);
1099 while ((name
= va_arg(args
, const char *)) != NULL
) {
1100 node
= xmlnode_get_child(parent
, name
);
1101 if (node
== NULL
) return NULL
;
1111 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1113 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1114 send_soap_request(sip
, body
);
1119 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1122 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1124 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1127 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1131 void sipe_auth_user_cb(void * data
)
1133 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1136 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1141 void sipe_deny_user_cb(void * data
)
1143 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1146 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1151 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1153 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1154 sipe_contact_allow_deny(sip
, name
, TRUE
);
1158 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1160 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1161 sipe_contact_allow_deny(sip
, name
, FALSE
);
1165 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1167 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1168 sipe_contact_set_acl(sip, name, "");
1172 sipe_process_incoming_pending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1176 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1177 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1179 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1181 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1182 if (!watchers
) return;
1184 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1185 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1186 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1187 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1189 // TODO pull out optional displayName to pass as alias
1191 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1192 job
->who
= remote_user
;
1194 purple_account_request_authorization(
1208 xmlnode_free(watchers
);
1213 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1215 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1216 if (!purple_group
) {
1217 purple_group
= purple_group_new(group
->name
);
1218 purple_blist_add_group(purple_group
, NULL
);
1222 group
->purple_group
= purple_group
;
1223 sip
->groups
= g_slist_append(sip
->groups
, group
);
1224 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1226 purple_debug_info("sipe", "did not add group %s\n", group
->name
);
1230 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1232 struct sipe_group
*group
;
1238 entry
= sip
->groups
;
1240 group
= entry
->data
;
1241 if (group
->id
== id
) {
1244 entry
= entry
->next
;
1249 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, gchar
* name
)
1251 struct sipe_group
*group
;
1257 entry
= sip
->groups
;
1259 group
= entry
->data
;
1260 if (!strcmp(group
->name
, name
)) {
1263 entry
= entry
->next
;
1269 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1272 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1273 body
= g_strdup_printf(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1274 send_soap_request(sip
, body
);
1276 g_free(group
->name
);
1277 group
->name
= g_strdup(name
);
1281 * Only appends if no such value already stored.
1284 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1285 GSList
* res
= list
;
1286 if (!g_slist_find_custom(list
, data
, func
)) {
1287 res
= g_slist_insert_sorted(list
, data
, func
);
1293 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1294 return group1
->id
- group2
->id
;
1298 * Returns string like "2 4 7 8" - group ids buddy belong to.
1301 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1304 //creating array from GList, converting int to gchar*
1305 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1306 GSList
*entry
= buddy
->groups
;
1308 struct sipe_group
* group
= entry
->data
;
1309 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1310 entry
= entry
->next
;
1314 res
= g_strjoinv(" ", ids_arr
);
1315 g_strfreev(ids_arr
);
1320 * Sends buddy update to server
1323 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1325 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1326 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1328 if (buddy
&& purple_buddy
) {
1329 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1331 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1332 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1334 body
= g_strdup_printf(SIPE_SOAP_SET_CONTACT
,
1335 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1337 send_soap_request(sip
, body
);
1343 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1345 if (msg
->response
== 200) {
1346 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1348 struct group_user_context
* ctx
= (struct group_user_context
*)tc
->payload
;
1352 struct sipe_buddy
*buddy
;
1353 group
->name
= ctx
->group_name
;
1355 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1356 if (!xml
) return FALSE
;
1358 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1359 if (!node
) return FALSE
;
1361 group_id
= xmlnode_get_data(node
);
1362 if (!group_id
) return FALSE
;
1364 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1366 sipe_group_add(sip
, group
);
1368 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1370 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1373 sipe_group_set_user(sip
, ctx
->user_name
);
1382 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1384 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1386 ctx
->group_name
= g_strdup(name
);
1387 ctx
->user_name
= g_strdup(who
);
1389 body
= g_strdup_printf(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1390 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1396 * Should return FALSE if repetitive action is not needed
1398 gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1401 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1402 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1403 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1404 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1405 ret
= sched_action
->repetitive
;
1406 g_free(sched_action
->payload
);
1407 g_free(sched_action
->name
);
1408 g_free(sched_action
);
1413 * Do schedule action for execution in the future.
1414 * Non repetitive execution.
1416 * @param name of action (will be copied)
1417 * @param timeout in seconds
1418 * @action callback function
1419 * @payload callback data (can be NULL, otherwise caller must allocate memory)
1421 void sipe_schedule_action(gchar
*name
, int timeout
, Action action
, struct sipe_account_data
*sip
, void * payload
)
1423 struct scheduled_action
*sched_action
;
1425 purple_debug_info("sipe","scheduling action %s timeout:%d\n", name
, timeout
);
1426 sched_action
= g_new0(struct scheduled_action
, 1);
1427 sched_action
->repetitive
= FALSE
;
1428 sched_action
->name
= g_strdup(name
);
1429 sched_action
->action
= action
;
1430 sched_action
->sip
= sip
;
1431 sched_action
->payload
= payload
;
1432 sched_action
->timeout_handler
= purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1433 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1434 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1438 * Kills action timer effectively cancelling
1441 * @param name of action
1443 void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, gchar
*name
)
1447 if (!sip
->timeouts
|| !name
) return;
1449 entry
= sip
->timeouts
;
1451 struct scheduled_action
*sched_action
= entry
->data
;
1452 if(!strcmp(sched_action
->name
, name
)) {
1453 GSList
*to_delete
= entry
;
1454 entry
= entry
->next
;
1455 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1456 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1457 purple_timeout_remove(sched_action
->timeout_handler
);
1458 g_free(sched_action
->payload
);
1459 g_free(sched_action
->name
);
1460 g_free(sched_action
);
1462 entry
= entry
->next
;
1467 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1469 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1472 //purple_debug_info("sipe","process_subscribe_response: body:\n%s\n", msg->body);
1474 if (msg
->response
== 200 || msg
->response
== 202)
1476 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1478 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1483 /* we can not subscribe -> user is offline (TODO unknown status?) */
1484 to
= parse_from(sipmsg_find_header(tc
->msg
, "To")); /* can't be NULL since it is our own msg */
1485 purple_prpl_got_user_status(sip
->account
, to
, "offline", NULL
);
1492 * Batch Category SUBSCRIBE [SIP-PRES] - msrtc-event-categories+xml
1493 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list
1494 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1498 static void sipe_subscribe_to_name_batched(struct sipe_account_data
*sip
, const char * buddy_name
){
1499 gchar
*resource_uri
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1500 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1501 gchar
*tmp
= get_contact(sip
);
1504 request
= g_strdup_printf(
1505 "Require: adhoclist, categoryList\r\n"
1506 "Supported: eventlist\r\n"
1507 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1508 "Supported: ms-piggyback-first-notify\r\n"
1509 "Supported: com.microsoft.autoextend\r\n"
1510 "Supported: ms-benotify\r\n"
1511 "Proxy-Require: ms-benotify\r\n"
1512 "Event: presence\r\n"
1513 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1514 "Contact: %s\r\n", tmp
);
1516 content
= g_strdup_printf(
1517 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1518 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1519 "<resource uri=\"%s\"/>\n"
1521 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1522 "<category name=\"note\"/>\n"
1523 "<category name=\"state\"/>\n"
1526 "</batchSub>", sip
->username
, resource_uri
1531 /* subscribe to buddy presence */
1532 send_sip_request(sip
->gc
, "SUBSCRIBE", resource_uri
, resource_uri
, request
, content
, NULL
, process_subscribe_response
);
1540 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1541 * The user sends a single SUBSCRIBE request to the subscribed contact.
1542 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1546 static void sipe_subscribe_to_name_single(struct sipe_account_data
*sip
, const char * buddy_name
)
1548 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1549 gchar
*tmp
= get_contact(sip
);
1552 request
= g_strdup_printf(
1553 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1554 "Supported: ms-piggyback-first-notify\r\n"
1555 "Supported: com.microsoft.autoextend\r\n"
1556 "Supported: ms-benotify\r\n"
1557 "Proxy-Require: ms-benotify\r\n"
1558 "Event: presence\r\n"
1559 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1560 "Contact: %s\r\n", tmp
);
1562 content
= g_strdup_printf(
1563 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1564 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1565 "<resource uri=\"%s\"/>\n"
1567 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1568 "<category name=\"note\"/>\n"
1569 "<category name=\"state\"/>\n"
1572 "</batchSub>", sip
->username
, to
1577 /* subscribe to buddy presence */
1578 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1585 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1587 const char *status_id
= purple_status_get_id(status
);
1588 struct sipe_account_data
*sip
= NULL
;
1590 if (!purple_status_is_active(status
))
1594 sip
= account
->gc
->proto_data
;
1597 g_free(sip
->status
);
1598 sip
->status
= g_strdup(status_id
);
1599 send_presence_info(sip
);
1604 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
)
1606 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1607 sipe_group_set_user(sip
, name
);
1611 sipe_group_buddy(PurpleConnection
*gc
,
1613 const char *old_group_name
,
1614 const char *new_group_name
)
1616 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1617 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1618 struct sipe_group
* old_group
= NULL
;
1619 struct sipe_group
* new_group
;
1621 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1622 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1624 if(!buddy
) { // buddy not in roaming list
1628 if (old_group_name
) {
1629 old_group
= sipe_group_find_by_name(sip
, g_strdup(old_group_name
));
1631 new_group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1634 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1635 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1639 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1641 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1642 sipe_group_set_user(sip
, who
);
1646 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1648 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1649 struct sipe_buddy
*b
;
1651 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1653 // Prepend sip: if needed
1654 if (strncmp("sip:", buddy
->name
, 4)) {
1655 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
1656 purple_blist_rename_buddy(buddy
, buf
);
1660 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1661 b
= g_new0(struct sipe_buddy
, 1);
1662 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1663 b
->name
= g_strdup(buddy
->name
);
1664 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1665 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1666 sipe_subscribe_to_name_batched(sip
, b
->name
); //@TODO should go to callback
1668 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1673 * Unassociates buddy from group first.
1674 * Then see if no groups left, removes buddy completely.
1675 * Otherwise updates buddy groups on server.
1677 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1679 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1680 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1681 struct sipe_group
*g
= NULL
;
1683 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1688 g
= sipe_group_find_by_name(sip
, group
->name
);
1692 b
->groups
= g_slist_remove(b
->groups
, g
);
1693 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
1696 if (g_slist_length(b
->groups
) < 1) {
1697 gchar
*action_name
= g_strdup_printf("<%s><%s>", "presence", buddy
->name
);
1698 sipe_cancel_scheduled_action(sip
, action_name
);
1699 g_free(action_name
);
1701 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1704 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1705 send_soap_request(sip
, body
);
1710 g_free(b
->annotation
);
1711 g_free(b
->device_name
);
1712 g_slist_free(b
->groups
);
1715 //updates groups on server
1716 sipe_group_set_user(sip
, b
->name
);
1722 sipe_rename_group(PurpleConnection
*gc
,
1723 const char *old_name
,
1725 GList
*moved_buddies
)
1727 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1728 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1730 sipe_group_rename(sip
, s_group
, group
->name
);
1732 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1737 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1739 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1740 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1743 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1744 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1745 send_soap_request(sip
, body
);
1748 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1749 g_free(s_group
->name
);
1751 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1755 static GList
*sipe_status_types(PurpleAccount
*acc
)
1757 PurpleStatusType
*type
;
1758 GList
*types
= NULL
;
1761 type
= purple_status_type_new_with_attrs(
1762 PURPLE_STATUS_AVAILABLE
, NULL
, "Online", TRUE
, TRUE
, FALSE
,
1763 // Translators: noun
1764 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1766 types
= g_list_append(types
, type
);
1769 type
= purple_status_type_new_with_attrs(
1770 PURPLE_STATUS_UNAVAILABLE
, "busy", _("Busy"), TRUE
, TRUE
, FALSE
,
1771 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1773 types
= g_list_append(types
, type
);
1775 // Do Not Disturb (Not let user set it)
1776 type
= purple_status_type_new_with_attrs(
1777 PURPLE_STATUS_UNAVAILABLE
, "do-not-disturb", "Do Not Disturb", TRUE
, FALSE
, FALSE
,
1778 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1780 types
= g_list_append(types
, type
);
1783 type
= purple_status_type_new_with_attrs(
1784 PURPLE_STATUS_AWAY
, "be-right-back", _("Be Right Back"), TRUE
, TRUE
, FALSE
,
1785 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1787 types
= g_list_append(types
, type
);
1790 type
= purple_status_type_new_with_attrs(
1791 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1792 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1794 types
= g_list_append(types
, type
);
1797 type
= purple_status_type_new_with_attrs(
1798 PURPLE_STATUS_UNAVAILABLE
, "on-the-phone", _("On The Phone"), TRUE
, TRUE
, FALSE
,
1799 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1801 types
= g_list_append(types
, type
);
1804 type
= purple_status_type_new_with_attrs(
1805 PURPLE_STATUS_AWAY
, "out-to-lunch", "Out To Lunch", TRUE
, TRUE
, FALSE
,
1806 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1808 types
= g_list_append(types
, type
);
1811 type
= purple_status_type_new_full(
1812 PURPLE_STATUS_INVISIBLE
, NULL
, "Appear Offline", TRUE
, TRUE
, FALSE
);
1813 types
= g_list_append(types
, type
);
1816 type
= purple_status_type_new_full(
1817 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1818 types
= g_list_append(types
, type
);
1824 * A callback for g_hash_table_foreach
1826 static void sipe_buddy_subscribe_cb(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1828 sipe_subscribe_to_name_batched(sip
, buddy
->name
);
1832 * Removes entries from purple buddy list
1833 * that does not correspond ones in the roaming contact list.
1835 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
1836 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1837 GSList
*entry
= buddies
;
1838 struct sipe_buddy
*buddy
;
1842 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
1843 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
1846 g
= purple_buddy_get_group(b
);
1847 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
1849 gboolean in_sipe_groups
= FALSE
;
1850 GSList
*entry2
= buddy
->groups
;
1852 struct sipe_group
*group
= entry2
->data
;
1853 if (!strcmp(group
->name
, g
->name
)) {
1854 in_sipe_groups
= TRUE
;
1857 entry2
= entry2
->next
;
1859 if(!in_sipe_groups
) {
1860 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
1861 purple_blist_remove_buddy(b
);
1864 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
1865 purple_blist_remove_buddy(b
);
1867 entry
= entry
->next
;
1871 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1873 int len
= msg
->bodylen
;
1875 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1878 gchar
*contacts_delta
;
1879 xmlnode
*group_node
;
1880 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1884 purple_debug_info("sipe", "msg->body:%s\n", msg
->body
);
1886 /* Convert the contact from XML to Purple Buddies */
1887 isc
= xmlnode_from_str(msg
->body
, len
);
1892 contacts_delta
= g_strdup(xmlnode_get_attrib(isc
, "deltaNum"));
1893 if (contacts_delta
) {
1894 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1898 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1899 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1901 group
->name
= g_strdup(xmlnode_get_attrib(group_node
, "name"));
1902 if (!strncmp(group
->name
, "~", 1)){
1904 group
->name
= "Other Contacts";
1906 group
->name
= g_strdup(group
->name
);
1907 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1909 sipe_group_add(sip
, group
);
1912 // Make sure we have at least one group
1913 if (g_slist_length(sip
->groups
) == 0) {
1914 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1915 PurpleGroup
*purple_group
;
1917 group
->name
= g_strdup("Other Contacts");
1919 purple_group
= purple_group_new(group
->name
);
1920 purple_blist_add_group(purple_group
, NULL
);
1921 sip
->groups
= g_slist_append(sip
->groups
, group
);
1924 /* Parse contacts */
1925 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1926 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
1927 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
1928 gchar
* groups
= g_strdup(xmlnode_get_attrib(item
, "groups"));
1929 gchar
* buddy_name
= g_strdup_printf("sip:%s", uri
);
1930 gchar
**item_groups
;
1931 struct sipe_group
*group
= NULL
;
1932 struct sipe_buddy
*buddy
= NULL
;
1935 // assign to group Other Contacts if nothing else received
1936 if(!groups
|| !strcmp("", groups
) ) {
1937 group
= sipe_group_find_by_name(sip
, "Other Contacts");
1938 groups
= group
? g_strdup_printf("%d", group
->id
) : "1";
1941 item_groups
= g_strsplit(groups
, " ", 0);
1943 while (item_groups
[i
]) {
1944 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
1946 // If couldn't find the right group for this contact, just put them in the first group we have
1947 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1948 group
= sip
->groups
->data
;
1951 if (group
!= NULL
) {
1952 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1954 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1955 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1958 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
1959 if (name
!= NULL
&& strlen(name
) != 0) {
1960 purple_blist_alias_buddy(b
, name
);
1965 buddy
= g_new0(struct sipe_buddy
, 1);
1966 buddy
->name
= g_strdup(b
->name
);
1967 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
1970 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1972 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
1974 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
1979 } // while, contact groups
1980 g_strfreev(item_groups
);
1990 sipe_cleanup_local_blist(sip
);
1992 //subscribe to buddies
1993 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
1994 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
1995 sip
->subscribed_buddies
= TRUE
;
2004 * Subscribe roaming contacts
2006 static void sipe_subscribe_buddylist(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2008 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2009 gchar
*tmp
= get_contact(sip
);
2010 gchar
*hdr
= g_strdup_printf(
2011 "Event: vnd-microsoft-roaming-contacts\r\n"
2012 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2013 "Supported: com.microsoft.autoextend\r\n"
2014 "Supported: ms-benotify\r\n"
2015 "Proxy-Require: ms-benotify\r\n"
2016 "Supported: ms-piggyback-first-notify\r\n"
2017 "Contact: %s\r\n", tmp
);
2020 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, sipe_process_roaming_contacts
);
2026 sipe_process_pending_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2028 sipe_process_incoming_pending (sip
, msg
);
2032 static void sipe_subscribe_pending_buddies(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2034 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2035 gchar
*tmp
= get_contact(sip
);
2036 gchar
*hdr
= g_strdup_printf(
2037 "Event: presence.wpending\r\n"
2038 "Accept: text/xml+msrtc.wpending\r\n"
2039 "Supported: com.microsoft.autoextend\r\n"
2040 "Supported: ms-benotify\r\n"
2041 "Proxy-Require: ms-benotify\r\n"
2042 "Supported: ms-piggyback-first-notify\r\n"
2043 "Contact: %s\r\n", tmp
);
2046 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, sipe_process_pending_response
);
2051 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2053 const gchar
*contacts_delta
;
2056 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2062 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2065 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2072 sipe_process_acl_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2074 sipe_process_roaming_acl(sip
, msg
);
2079 * When we receive some self (BE) NOTIFY with a new subscriber
2080 * we sends a setSubscribers request to him [SIP-PRES]
2084 static void sipe_process_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2086 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2087 gchar
*tmp
= get_contact(sip
);
2092 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2094 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2097 node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
);
2100 user
= xmlnode_get_attrib(node
, "user");
2103 gchar
*hdr
= g_strdup_printf(
2105 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", tmp
);
2107 gchar
*body
=g_strdup_printf(
2108 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2109 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2110 "</setSubscribers>",user
);
2113 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
2119 static void sipe_subscribe_acl(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2121 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2122 gchar
*tmp
= get_contact(sip
);
2123 gchar
*hdr
= g_strdup_printf(
2124 "Event: vnd-microsoft-roaming-ACL\r\n"
2125 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
2126 "Supported: com.microsoft.autoextend\r\n"
2127 "Supported: ms-benotify\r\n"
2128 "Proxy-Require: ms-benotify\r\n"
2129 "Supported: ms-piggyback-first-notify\r\n"
2130 "Contact: %s\r\n", tmp
);
2133 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, sipe_process_acl_response
);
2139 * To request for presence information about the user, access level settings that have already been configured by the user
2140 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
2141 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
2144 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2146 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2147 gchar
*tmp
= get_contact(sip
);
2148 gchar
*hdr
= g_strdup_printf(
2149 "Event: vnd-microsoft-roaming-self\r\n"
2150 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
2151 "Supported: com.microsoft.autoextend\r\n"
2152 "Supported: ms-benotify\r\n"
2153 "Proxy-Require: ms-benotify\r\n"
2154 "Supported: ms-piggyback-first-notify\r\n"
2156 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
2158 gchar
*body
=g_strdup(
2159 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
2160 "<roaming type=\"categories\"/>"
2161 "<roaming type=\"containers\"/>"
2162 "<roaming type=\"subscribers\"/></roamingList>");
2165 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, NULL
);
2171 /** Subscription for provisioning information to help with initial
2172 * configuration. This subscription is a one-time query (denoted by the Expires header,
2173 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
2174 * configuration, meeting policies, and policy settings that Communicator must enforce.
2175 * TODO: for what we need this information.
2178 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2180 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2181 gchar
*tmp
= get_contact(sip
);
2182 gchar
*hdr
= g_strdup_printf(
2183 "Event: vnd-microsoft-provisioning-v2\r\n"
2184 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
2185 "Supported: com.microsoft.autoextend\r\n"
2186 "Supported: ms-benotify\r\n"
2187 "Proxy-Require: ms-benotify\r\n"
2188 "Supported: ms-piggyback-first-notify\r\n"
2191 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
2192 gchar
*body
= g_strdup(
2193 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
2194 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
2195 "<provisioningGroup name=\"ucPolicy\"/>"
2196 "</provisioningGroupList>");
2199 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, NULL
);
2205 /* IM Session (INVITE and MESSAGE methods) */
2207 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
2209 struct sip_im_session
*session
;
2211 if (sip
== NULL
|| who
== NULL
) {
2215 entry
= sip
->im_sessions
;
2217 session
= entry
->data
;
2218 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
2221 entry
= entry
->next
;
2226 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
2228 struct sip_im_session
*session
= find_im_session(sip
, who
);
2230 session
= g_new0(struct sip_im_session
, 1);
2231 session
->with
= g_strdup(who
);
2232 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
2237 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2239 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
2240 // TODO free session resources
2243 static void sipe_present_message_undelivered_err(gchar
*with
, struct sipe_account_data
*sip
, gchar
*message
)
2245 char *msg
, *msg_tmp
;
2246 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
2247 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2249 msg_tmp
= g_strdup_printf( _("The following message could not be delivered to all recipients, "\
2250 "possibly because one or more persons are offline:\n%s") ,
2252 purple_conv_present_error(with
, sip
->account
, msg_tmp
);
2257 static void sipe_im_remove_first_from_queue (struct sip_im_session
* session
);
2258 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
);
2261 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2263 gboolean ret
= TRUE
;
2264 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
2265 struct sip_im_session
* session
= find_im_session(sip
, with
);
2266 struct sip_dialog
*dialog
;
2269 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2273 if (msg
->response
!= 200) {
2274 gchar
*queued_msg
= NULL
;
2275 purple_debug_info("sipe", "process_message_response: MESSAGE response not 200\n");
2277 if (session
->outgoing_message_queue
) {
2278 queued_msg
= session
->outgoing_message_queue
->data
;
2280 sipe_present_message_undelivered_err(with
, sip
, queued_msg
);
2281 im_session_destroy(sip
, session
);
2285 dialog
= session
->dialog
;
2287 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2291 sipe_im_remove_first_from_queue(session
);
2292 sipe_im_process_queue(sip
, session
);
2297 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
2307 if (strncmp("sip:", session
->with
, 4)) {
2308 fullto
= g_strdup_printf("sip:%s", session
->with
);
2310 fullto
= g_strdup(session
->with
);
2313 sipe_parse_html(msg
, &msgformat
, &msgtext
);
2314 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
2316 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2320 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2324 hdr
= g_strdup_printf("Content-Type: text/plain; charset=UTF-8%s\r\n", msgr
);
2326 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
2327 //hdr = g_strdup("Content-Type: text/rtf\r\n");
2328 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
2330 tmp
= get_contact(sip
);
2331 hdr
= g_strdup_printf("Contact: %s\r\n%s", tmp
, hdr
);
2334 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msgtext
, session
->dialog
, process_message_response
);
2343 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
2345 GSList
*entry
= session
->outgoing_message_queue
;
2347 char *queued_msg
= entry
->data
;
2348 sipe_send_message(sip
, session
, queued_msg
);
2353 sipe_im_remove_first_from_queue (struct sip_im_session
* session
)
2355 if (session
&& session
->outgoing_message_queue
) {
2356 char *queued_msg
= session
->outgoing_message_queue
->data
;
2357 // Remove from the queue and free the string
2358 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2364 sipe_get_route_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2366 GSList
*hdr
= msg
->headers
;
2367 struct siphdrelement
*elem
;
2373 if(!strcmp(elem
->name
, "Record-Route"))
2375 gchar
*route
= sipmsg_find_part_of_header(elem
->value
, "<", ">", NULL
);
2376 dialog
->routes
= g_slist_append(dialog
->routes
, route
);
2378 hdr
= g_slist_next(hdr
);
2383 dialog
->routes
= g_slist_reverse(dialog
->routes
);
2388 dialog
->request
= dialog
->routes
->data
;
2389 dialog
->routes
= g_slist_remove(dialog
->routes
, dialog
->routes
->data
);
2392 contact
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Contact"), "<", ">", NULL
);
2393 dialog
->routes
= g_slist_append(dialog
->routes
, contact
);
2397 sipe_get_supported_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2399 GSList
*hdr
= msg
->headers
;
2400 struct siphdrelement
*elem
;
2404 if(!strcmp(elem
->name
, "Supported")
2405 && !g_slist_find_custom(dialog
->supported
, elem
->value
, (GCompareFunc
)strcmp
))
2407 dialog
->supported
= g_slist_append(dialog
->supported
, g_strdup(elem
->value
));
2410 hdr
= g_slist_next(hdr
);
2415 sipe_parse_dialog(struct sipmsg
* msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2417 gchar
*us
= outgoing
? "From" : "To";
2418 gchar
*them
= outgoing
? "To" : "From";
2420 dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
2421 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, us
));
2422 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, them
));
2423 if (!dialog
->theirepid
) {
2424 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", ";", NULL
);
2426 if (!dialog
->theirepid
) {
2427 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", NULL
, NULL
);
2430 sipe_get_route_header(msg
, dialog
, outgoing
);
2431 sipe_get_supported_header(msg
, dialog
, outgoing
);
2436 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2438 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
2439 struct sip_im_session
* session
= find_im_session(sip
, with
);
2440 struct sip_dialog
*dialog
;
2443 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2448 if (msg
->response
!= 200) {
2449 gchar
*queued_msg
= NULL
;
2450 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2452 if (session
->outgoing_message_queue
) {
2453 queued_msg
= session
->outgoing_message_queue
->data
;
2455 sipe_present_message_undelivered_err(with
, sip
, queued_msg
);
2457 im_session_destroy(sip
, session
);
2462 dialog
= session
->dialog
;
2464 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2469 sipe_parse_dialog(msg
, dialog
, TRUE
);
2472 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
2473 session
->outgoing_invite
= NULL
;
2474 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)strcmp
)) {
2475 sipe_im_remove_first_from_queue(session
);
2477 sipe_im_process_queue(sip
, session
);
2485 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
* session
, gchar
* msg_body
)
2494 char *ms_text_format
;
2498 if (session
->dialog
) {
2499 purple_debug_info("sipe", "session with %s already has a dialog open\n", session
->with
);
2503 session
->dialog
= g_new0(struct sip_dialog
, 1);
2505 if (strstr(session
->with
, "sip:")) {
2506 to
= g_strdup(session
->with
);
2508 to
= g_strdup_printf("sip:%s", session
->with
);
2511 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
2512 purple_debug_info("sipe", "sipe_invite: msgformat=%s", msgformat
);
2514 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2518 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2522 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
2523 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
2528 contact
= get_contact(sip
);
2529 hdr
= g_strdup_printf(
2531 "Content-Type: application/sdp\r\n",
2532 contact
, ms_text_format
);
2533 g_free(ms_text_format
);
2535 body
= g_strdup_printf(
2537 "o=- 0 0 IN IP4 %s\r\n"
2541 "m=message %d sip null\r\n"
2542 "a=accept-types:text/plain text/html image/gif "
2543 "multipart/alternative application/im-iscomposing+xml\r\n",
2544 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
2546 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
2547 to
, to
, hdr
, body
, session
->dialog
, process_invite_response
);
2556 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2559 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->dialog
, NULL
);
2560 im_session_destroy(sip
, session
);
2565 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
2567 struct sipe_account_data
*sip
= gc
->proto_data
;
2569 purple_debug_info("sipe", "conversation with %s closed\n", who
);
2570 im_session_close(sip
, find_im_session(sip
, who
));
2574 im_session_close_all (struct sipe_account_data
*sip
)
2576 GSList
*entry
= sip
->im_sessions
;
2578 im_session_close (sip
, entry
->data
);
2579 entry
= sip
->im_sessions
;
2583 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
2585 struct sipe_account_data
*sip
;
2588 struct sip_im_session
*session
;
2590 purple_debug_info("sipe", "sipe_im_send what=%s\n", what
);
2592 sip
= gc
->proto_data
;
2594 text
= g_strdup(what
);
2596 session
= find_or_create_im_session(sip
, who
);
2598 // Queue the message
2599 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, text
);
2601 if (session
->dialog
&& session
->dialog
->callid
) {
2602 sipe_im_process_queue(sip
, session
);
2603 } else if (!session
->outgoing_invite
) {
2604 // Need to send the INVITE to get the outgoing dialog setup
2605 sipe_invite(sip
, session
, text
);
2613 /* End IM Session (INVITE and MESSAGE methods) */
2616 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
2618 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2619 struct sip_im_session
*session
;
2621 if (state
== PURPLE_NOT_TYPING
)
2624 session
= find_im_session(sip
, who
);
2626 if (session
&& session
->dialog
) {
2627 send_sip_request(gc
, "INFO", who
, who
,
2628 "Content-Type: application/xml\r\n",
2629 SIPE_SEND_TYPING
, session
->dialog
, NULL
);
2632 return SIPE_TYPING_SEND_TIMEOUT
;
2635 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
2637 GSList
*tmp
= sip
->transactions
;
2638 time_t currtime
= time(NULL
);
2640 struct transaction
*trans
= tmp
->data
;
2642 purple_debug_info("sipe", "have open transaction age: %ld\n", currtime
-trans
->time
);
2643 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
2646 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
2648 sendout_sipmsg(sip
, trans
->msg
);
2655 static void do_reauthenticate_cb(struct sipe_account_data
*sip
)
2657 /* register again when security token expires */
2658 /* we have to start a new authentication as the security token
2659 * is almost expired by sending a not signed REGISTER message */
2660 purple_debug_info("sipe", "do a full reauthentication\n");
2661 sipe_auth_free(&sip
->registrar
);
2662 sip
->registerstatus
= 0;
2664 sip
->reauthenticate_set
= FALSE
;
2667 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2671 gboolean found
= FALSE
;
2673 from
= parse_from(sipmsg_find_header(msg
, "From"));
2677 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
2679 contenttype
= sipmsg_find_header(msg
, "Content-Type");
2680 if (!contenttype
|| !strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
2681 gchar
*msgr
= sipmsg_find_part_of_header(contenttype
, "msgr=", NULL
, NULL
);
2682 gchar
*x_mms_im_format
= sipmsg_get_x_mms_im_format(msgr
);
2684 gchar
*body_esc
= g_markup_escape_text(msg
->body
, -1);
2685 gchar
*body_html
= sipmsg_apply_x_mms_im_format(x_mms_im_format
, body_esc
);
2688 g_free(x_mms_im_format
);
2690 serv_got_im(sip
->gc
, from
, body_html
, 0, time(NULL
));
2692 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2695 if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
2696 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2701 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
2705 state
= xmlnode_get_child(isc
, "state");
2708 purple_debug_info("sipe", "process_incoming_message: no state found\n");
2713 statedata
= xmlnode_get_data(state
);
2715 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
2716 else serv_got_typing_stopped(sip
->gc
, from
);
2721 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2725 purple_debug_info("sipe", "got unknown mime-type");
2726 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
2731 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2733 gchar
*ms_text_format
;
2735 struct sip_im_session
*session
;
2736 // Only accept text invitations
2737 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
2738 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
2742 from
= parse_from(sipmsg_find_header(msg
, "From"));
2743 session
= find_or_create_im_session (sip
, from
);
2745 if (session
->dialog
) {
2746 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
2748 session
->dialog
= g_new0(struct sip_dialog
, 1);
2750 sipe_parse_dialog(msg
, session
->dialog
, FALSE
);
2752 session
->dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
2753 session
->dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
2754 session
->dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
2755 session
->dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
2758 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
2761 //ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk=
2762 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
2763 if (ms_text_format
&& !strncmp(ms_text_format
, "text/plain", 10)) {
2764 gchar
*msgr
= sipmsg_find_part_of_header(ms_text_format
, "msgr=", ";", NULL
);
2765 gchar
*x_mms_im_format
= sipmsg_get_x_mms_im_format(msgr
);
2767 gchar
*ms_body
= sipmsg_find_part_of_header(ms_text_format
, "ms-body=", NULL
, NULL
);
2770 gchar
*body
= purple_base64_decode(ms_body
, NULL
);
2771 gchar
*body_esc
= g_markup_escape_text(body
, -1);
2772 gchar
*body_html
= sipmsg_apply_x_mms_im_format(x_mms_im_format
, body_esc
);
2776 serv_got_im(sip
->gc
, from
, body_html
, 0, time(NULL
));
2778 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message reciept
2780 g_free(x_mms_im_format
);
2784 sipmsg_remove_header(msg
, "Ms-Conversation-ID");
2785 sipmsg_remove_header(msg
, "Ms-Text-Format");
2786 sipmsg_remove_header(msg
, "EndPoints");
2787 sipmsg_remove_header(msg
, "User-Agent");
2788 sipmsg_remove_header(msg
, "Roster-Manager");
2790 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
2791 //sipmsg_add_header(msg, "Supported", "ms-renders-gif");
2793 send_sip_response(sip
->gc
, msg
, 200, "OK", g_strdup_printf(
2795 "o=- 0 0 IN IP4 %s\r\n"
2799 "m=message %d sip sip:%s\r\n"
2800 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
2801 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
2802 sip
->realport
, sip
->username
));
2805 static void sipe_connection_cleanup(struct sipe_account_data
*);
2806 static void create_connection(struct sipe_account_data
*, gchar
*, int);
2808 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2812 const gchar
*expires_header
;
2815 expires_header
= sipmsg_find_header(msg
, "Expires");
2816 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
2817 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
2819 switch (msg
->response
) {
2822 sip
->registerstatus
= 0;
2825 gchar
*contact_hdr
= NULL
;
2830 sip
->registerexpire
= expires
;
2832 if (!sip
->reregister_set
) {
2833 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
2834 sipe_schedule_action(action_name
, expires
, (Action
) do_register_cb
, sip
, NULL
);
2835 g_free(action_name
);
2836 sip
->reregister_set
= TRUE
;
2839 sip
->registerstatus
= 3;
2841 if (!sip
->reauthenticate_set
) {
2842 /* we have to reauthenticate as our security token expires
2843 after eight hours (be five minutes early) */
2844 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
2845 guint reauth_timeout
= (8 * 3600) - 360;
2846 sipe_schedule_action(action_name
, reauth_timeout
, (Action
) do_reauthenticate_cb
, sip
, NULL
);
2847 g_free(action_name
);
2848 sip
->reauthenticate_set
= TRUE
;
2851 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
2854 uuid
= generateUUIDfromEPID(epid
);
2857 // There can be multiple Contact headers (one per location where the user is logged in) so
2858 // make sure to only get the one for this uuid
2859 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
2860 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
2861 if (valid_contact
) {
2862 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
2863 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
2864 g_free(valid_contact
);
2867 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
2873 sip
->contact
= g_strdup_printf("<%s>", gruu
);
2876 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
2877 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
);
2880 if (!sip
->subscribed
) { //do it just once, not every re-register
2881 tmp
= sipmsg_find_header(msg
, "Allow-Events");
2882 if (tmp
&& strstr(tmp
, "vnd-microsoft-provisioning")){
2883 sipe_subscribe_buddylist(sip
, msg
);
2885 sipe_subscribe_acl(sip
, msg
);
2886 sipe_subscribe_roaming_self(sip
, msg
);
2887 sipe_subscribe_roaming_provisioning(sip
, msg
);
2888 sipe_subscribe_pending_buddies(sip
, msg
);
2889 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
2890 sip
->subscribed
= TRUE
;
2893 if (purple_account_get_bool(sip
->account
, "clientkeepalive", FALSE
)) {
2894 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Setting user defined keepalive\n");
2895 sip
->keepalive_timeout
= purple_account_get_int(sip
->account
, "keepalive", 0);
2897 tmp
= sipmsg_find_header(msg
, "ms-keep-alive");
2899 sipe_keep_alive_timeout(sip
, tmp
);
2903 // Should we remove the transaction here?
2904 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
2905 transactions_remove(sip
, tc
);
2910 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
2912 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
2913 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
2917 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
2920 tmp
= g_strsplit(parts
[0], ":", 0);
2921 hostname
= g_strdup(tmp
[0]);
2922 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
2926 tmp
= g_strsplit(parts
[i
], "=", 0);
2928 if (g_strcasecmp("transport", tmp
[0]) == 0) {
2929 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
2930 transport
= SIPE_TRANSPORT_TCP
;
2931 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
2932 transport
= SIPE_TRANSPORT_UDP
;
2941 /* Close old connection */
2942 sipe_connection_cleanup(sip
);
2944 /* Create new connection */
2945 sip
->transport
= transport
;
2946 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
2947 hostname
, port
, TRANSPORT_DESCRIPTOR
);
2948 create_connection(sip
, hostname
, port
);
2953 if (sip
->registerstatus
!= 2) {
2954 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
2955 if (sip
->registrar
.retries
> 3) {
2956 sip
->gc
->wants_to_die
= TRUE
;
2957 purple_connection_error(sip
->gc
, _("Wrong Password"));
2960 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
2961 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
2963 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
2965 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
2966 fill_auth(sip
, tmp
, &sip
->registrar
);
2967 sip
->registerstatus
= 2;
2968 if (sip
->account
->disconnecting
) {
2969 do_register_exp(sip
, 0);
2977 const gchar
*warning
= sipmsg_find_header(msg
, "Warning");
2978 if (warning
!= NULL
) {
2980 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
2982 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
2983 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
2986 warning
= _("You have been rejected by the server");
2989 sip
->gc
->wants_to_die
= TRUE
;
2990 purple_connection_error(sip
->gc
, warning
);
2996 const gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2997 if (warning
!= NULL
) {
2998 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2999 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
3002 warning
= _("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator");
3005 sip
->gc
->wants_to_die
= TRUE
;
3006 purple_connection_error(sip
->gc
, warning
);
3012 const gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3013 if (warning
!= NULL
) {
3014 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3015 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
3018 warning
= _("Service unavailable: no reason given");
3021 sip
->gc
->wants_to_die
= TRUE
;
3022 purple_connection_error(sip
->gc
, warning
);
3030 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3033 xmlnode
*xn_categories
;
3034 xmlnode
*xn_category
;
3037 const char *activity
= NULL
;
3039 xn_categories
= xmlnode_from_str(data
, len
);
3040 uri
= xmlnode_get_attrib(xn_categories
, "uri");
3042 purple_debug_info("sipe", "process_incoming_notify_rlmi\n");
3044 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
3046 xn_category
= xmlnode_get_next_twin(xn_category
) )
3048 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
3050 if (!strcmp(attrVar
, "note"))
3053 struct sipe_buddy
*sbuddy
;
3054 xn_node
= xmlnode_get_child(xn_category
, "note");
3055 if (!xn_node
) continue;
3056 xn_node
= xmlnode_get_child(xn_node
, "body");
3057 if (!xn_node
) continue;
3059 note
= xmlnode_get_data(xn_node
);
3061 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3064 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3065 sbuddy
->annotation
= g_strdup(note
);
3071 else if(!strcmp(attrVar
, "state"))
3075 xn_node
= xmlnode_get_child(xn_category
, "state");
3076 if (!xn_node
) continue;
3077 xn_node
= xmlnode_get_child(xn_node
, "availability");
3078 if (!xn_node
) continue;
3080 data
= xmlnode_get_data(xn_node
);
3085 activity
= "unknown";
3086 else if (avail
< 4500)
3087 activity
= "available";
3088 else if (avail
< 6000)
3090 else if (avail
< 7500)
3092 else if (avail
< 9000)
3094 else if (avail
< 12000)
3096 else if (avail
< 18000)
3099 activity
= "offline";
3106 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
3109 xmlnode_free(xn_categories
);
3112 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3116 gchar
*getbasic
= g_strdup("closed");
3117 gchar
*activity
= g_strdup("available");
3119 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
3120 gboolean isonline
= FALSE
;
3121 xmlnode
*display_name_node
;
3123 fromhdr
= sipmsg_find_header(msg
, "From");
3124 from
= parse_from(fromhdr
);
3130 pidf
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3132 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",msg
->body
);
3136 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
3138 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3139 basicstatus
= xmlnode_get_child(status
, "basic");
3144 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
3149 getbasic
= xmlnode_get_data(basicstatus
);
3151 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
3156 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
3157 if (strstr(getbasic
, "open")) {
3161 display_name_node
= xmlnode_get_child(pidf
, "display-name");
3162 if (display_name_node
) {
3163 PurpleBuddy
* buddy
= purple_find_buddy (sip
->account
, from
);
3164 char * display_name
= xmlnode_get_data(display_name_node
);
3165 if (buddy
&& display_name
) {
3166 purple_blist_server_alias_buddy (buddy
, g_strdup(display_name
));
3170 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
3171 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3172 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
3173 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
3174 activity
= xmlnode_get_data(basicstatus
);
3180 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
3183 gchar
* status_id
= NULL
;
3185 if (strstr(activity
, "busy")) {
3187 } else if (strstr(activity
, "away")) {
3193 status_id
= "available";
3196 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
3197 purple_prpl_got_user_status(sip
->account
, from
, status_id
, NULL
);
3199 purple_prpl_got_user_status(sip
->account
, from
, "offline", NULL
);
3208 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3210 const char *availability
;
3211 const char *activity
;
3212 const char *display_name
= NULL
;
3213 const char *activity_name
;
3218 struct sipe_buddy
*sbuddy
;
3220 xmlnode
*xn_presentity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3222 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
3223 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
3224 xmlnode
*xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
3225 xmlnode
*xn_email
= xmlnode_get_child(xn_presentity
, "email");
3226 const char *email
= xn_email
? xmlnode_get_attrib(xn_email
, "email") : NULL
;
3227 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
3228 xmlnode
*xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
3229 const char *note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
3230 xmlnode
*xn_devices
= xmlnode_get_child(xn_presentity
, "devices");
3231 xmlnode
*xn_device_presence
= xn_devices
? xmlnode_get_child(xn_devices
, "devicePresence") : NULL
;
3232 xmlnode
*xn_device_name
= xn_device_presence
? xmlnode_get_child(xn_device_presence
, "deviceName") : NULL
;
3233 const char *device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
3235 name
= xmlnode_get_attrib(xn_presentity
, "uri");
3236 uri
= g_strdup_printf("sip:%s", name
);
3237 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
3238 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
3240 // updating display name if alias was just URI
3241 if (xn_display_name
) {
3242 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
3243 GSList
*entry
= buddies
;
3244 PurpleBuddy
*p_buddy
;
3245 display_name
= xmlnode_get_attrib(xn_display_name
, "displayName");
3248 const char *email_str
, *server_alias
;
3250 p_buddy
= entry
->data
;
3252 if (!g_ascii_strcasecmp(name
, purple_buddy_get_alias(p_buddy
))) {
3253 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
3254 purple_blist_alias_buddy(p_buddy
, display_name
);
3257 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3259 ( (server_alias
&& strcmp(display_name
, server_alias
))
3260 || !server_alias
|| strlen(server_alias
) == 0 )
3262 purple_blist_server_alias_buddy(p_buddy
, display_name
);
3266 email_str
= purple_blist_node_get_string((PurpleBlistNode
*)p_buddy
, "email");
3267 if (!email_str
|| g_ascii_strcasecmp(email_str
, email
)) {
3268 purple_blist_node_set_string((PurpleBlistNode
*)p_buddy
, "email", email
);
3272 entry
= entry
->next
;
3276 avl
= atoi(availability
);
3277 act
= atoi(activity
);
3280 activity_name
= "away";
3281 else if (act
<= 150)
3282 activity_name
= "out-to-lunch";
3283 else if (act
<= 300)
3284 activity_name
= "be-right-back";
3285 else if (act
<= 400)
3286 activity_name
= "available";
3287 else if (act
<= 500)
3288 activity_name
= "on-the-phone";
3289 else if (act
<= 600)
3290 activity_name
= "busy";
3292 activity_name
= "available";
3295 activity_name
= "offline";
3297 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3300 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3301 sbuddy
->annotation
= NULL
;
3302 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
3304 if (sbuddy
->device_name
) { g_free(sbuddy
->device_name
); }
3305 sbuddy
->device_name
= NULL
;
3306 if (device_name
) { sbuddy
->device_name
= g_strdup(device_name
); }
3309 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
3310 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
3311 xmlnode_free(xn_presentity
);
3315 static void process_incoming_notify_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3317 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
3319 purple_debug_info("sipe", "process_incoming_notify_presence: Content-Type: %s\n", ctype
? ctype
: "");
3321 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
3322 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
3324 const char *content
= msg
->body
;
3325 unsigned length
= msg
->bodylen
;
3326 PurpleMimeDocument
*mime
= NULL
;
3327 //char *subscription_state;
3329 if (strstr(ctype
, "multipart"))
3331 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
3333 mime
= purple_mime_document_parse(doc
);
3334 parts
= purple_mime_document_get_parts(mime
);
3335 content
= purple_mime_part_get_data(parts
->data
);
3336 length
= purple_mime_part_get_length(parts
->data
);
3340 process_incoming_notify_rlmi(sip
, content
, length
);
3344 purple_mime_document_free(mime
);
3347 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
3349 process_incoming_notify_msrtc(sip
, msg
);
3353 process_incoming_notify_pidf(sip
, msg
);
3358 * Dispatcher for all incoming subscription information
3359 * whether it comes from NOTIFY, BENOTIFY requests or
3360 * piggy-backed to subscription's OK responce.
3362 * @param request whether initiated from BE/NOTIFY request or OK-response message.
3363 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
3365 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
3367 gchar
*event
= sipmsg_find_header(msg
, "Event");
3368 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
3369 const char *uri
,*state
;
3371 xmlnode
*xn_resource
;
3372 xmlnode
*xn_instance
;
3376 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event
? event
: "", msg
->body
);
3377 purple_debug_info("sipe", "process_incoming_notify: subscription_state:%s\n\n", subscription_state
);
3381 const gchar
*expires_header
;
3382 expires_header
= sipmsg_find_header(msg
, "Expires");
3383 expires
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
3384 purple_debug_info("sipe", "process_incoming_notify: expires:%d\n\n", expires
);
3387 if (!subscription_state
|| strstr(subscription_state
, "active"))
3389 if (event
&& strstr(event
, "presence"))
3391 process_incoming_notify_presence(sip
, msg
);
3393 else if (event
&& strstr(event
, "vnd-microsoft-roaming-contacts"))
3395 sipe_process_roaming_contacts(sip
, msg
, NULL
);
3397 else if (event
&& strstr(event
, "vnd-microsoft-roaming-self"))
3399 sipe_process_roaming_self(sip
, msg
);
3401 else if (event
&& strstr(event
, "vnd-microsoft-roaming-ACL"))
3403 sipe_process_roaming_acl(sip
, msg
);
3405 else if (event
&& strstr(event
, "presence.wpending"))
3407 sipe_process_incoming_pending(sip
, msg
);
3411 purple_debug_info("sipe", "Unable to process (BE)NOTIFY. Event is not supported:%s\n", event
? event
: "");
3415 //Subscription terminated and is not a (BE)NOTIFY; we need resub
3416 if ( !request
&& !benotify
&& subscription_state
&& strstr(subscription_state
, "terminated"))
3418 if(event
&& strstr(event
, "presence")){
3420 const char *content
= msg
->body
;
3421 unsigned length
= msg
->bodylen
;
3422 PurpleMimeDocument
*mime
= NULL
;
3423 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
3425 purple_debug_info("sipe", "process_incoming_notify: ctype(%s)\n",ctype
);
3427 if (ctype
&& strstr(ctype
, "multipart"))
3429 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
3431 mime
= purple_mime_document_parse(doc
);
3432 parts
= purple_mime_document_get_parts(mime
);
3433 content
= purple_mime_part_get_data(parts
->data
);
3434 length
= purple_mime_part_get_length(parts
->data
);
3438 xn_list
= xmlnode_from_str(content
, length
);
3439 xn_resource
= xmlnode_get_child(xn_list
, "resource");
3440 if (!xn_resource
) return;
3441 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
3442 if (!xn_instance
) return;
3443 state
= xmlnode_get_attrib(xn_instance
, "state");
3444 uri
= xmlnode_get_attrib(xn_instance
, "cid");
3446 purple_debug_info("sipe", "process_incoming_notify: cid(%s),state(%s)\n",uri
,state
);
3448 int timeout
= expires
? expires
: 3;
3449 gchar
*action_name
= g_strdup_printf("<%s><%s>", "presence", uri
);
3451 if(strstr(state
, "resubscribe"))
3453 sipe_cancel_scheduled_action(sip
, action_name
);
3454 purple_debug_info("sipe", "process_incoming_notify: Subscription to buddy %s was terminated. Resubscribing\n", uri
);
3455 sipe_schedule_action(action_name
, timeout
, (Action
) sipe_subscribe_to_name_single
, sip
, g_strdup(uri
));
3456 g_free(action_name
);
3461 purple_debug_info("sipe", "Unable to process subscription termination. Event is not supported:%s\n",
3462 event
? event
: "");
3466 //The server sends a (BE)NOTIFY with the status 'terminated'
3467 if(request
&& subscription_state
&& strstr(subscription_state
, "terminated") )
3469 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3470 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from
);
3474 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
3475 if (request
&& !benotify
)
3477 sipmsg_remove_header(msg
, "Expires");
3478 sipmsg_remove_header(msg
, "subscription-state");
3479 sipmsg_remove_header(msg
, "Event");
3480 sipmsg_remove_header(msg
, "Require");
3481 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3488 static gchar* gen_xpidf(struct sipe_account_data *sip)
3490 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3492 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
3493 "<display name=\"sip:%s\"/>\r\n"
3494 "<atom id=\"1234\">\r\n"
3495 "<address uri=\"sip:%s\">\r\n"
3496 "<status status=\"%s\"/>\r\n"
3509 static gchar* gen_pidf(struct sipe_account_data *sip)
3511 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3512 "<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"
3513 "<tuple id=\"0\">\r\n"
3515 "<basic>open</basic>\r\n"
3516 "<ep:activities>\r\n"
3517 " <ep:activity>%s</ep:activity>\r\n"
3521 "<ci:display-name>%s</ci:display-name>\r\n"
3531 process_send_presence_info_v0_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3533 if (msg
->response
== 488) {
3534 sip
->presence_method_version
= 1;
3535 send_presence_info(sip
);
3540 static void send_presence_info_v0(struct sipe_account_data
*sip
, char * note
)
3542 int availability
= 300; // online
3543 int activity
= 400; // Available
3546 if (!strcmp(sip
->status
, "away")) {
3548 } else if (!strcmp(sip
->status
, "out-to-lunch")) {
3550 } else if (!strcmp(sip
->status
, "be-right-back")) {
3552 } else if (!strcmp(sip
->status
, "on-the-phone")) {
3554 } else if (!strcmp(sip
->status
, "do-not-disturb")) {
3556 } else if (!strcmp(sip
->status
, "busy")) {
3558 } else if (!strcmp(sip
->status
, "invisible")) {
3559 availability
= 0; // offline
3563 name
= g_strdup_printf("sip: sip:%s", sip
->username
);
3564 //@TODO: send user data - state; add hostname in upper case
3565 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
3566 send_soap_request_with_cb(sip
, body
, process_send_presence_info_v0_response
, NULL
);
3572 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3574 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
3575 if (msg
->response
== 200) {
3576 sip
->status_version
= 0;
3577 send_presence_info(sip
);
3583 process_send_presence_info_v1_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3585 if (msg
->response
== 409) {
3586 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
3587 // TODO need to parse the version #'s?
3588 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
3589 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
3591 purple_debug_info("sipe", "process_send_presence_info_v1_response = %s\n", msg
->body
);
3593 gchar
*tmp
= get_contact(sip
);
3594 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
3595 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
3597 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
3607 static void send_presence_info_v1(struct sipe_account_data
*sip
, char * note
)
3614 if (!strcmp(sip
->status
, "away")) {
3616 } else if (!strcmp(sip
->status
, "busy")) {
3623 uri
= g_strdup_printf("sip:%s", sip
->username
);
3624 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
3625 sip
->status_version
, code
,
3626 sip
->status_version
, code
,
3627 sip
->status_version
, note
? note
: "",
3628 sip
->status_version
, note
? note
: "",
3629 sip
->status_version
, note
? note
: ""
3631 sip
->status_version
++;
3633 tmp
= get_contact(sip
);
3634 hdr
= g_strdup_printf("Contact: %s\r\n"
3635 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
3637 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_info_v1_response
);
3645 static void send_presence_info(struct sipe_account_data
*sip
)
3647 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
3649 if (!status
) return;
3651 note
= g_strdup(purple_status_get_attr_string(status
, "message"));
3653 purple_debug_info("sipe", "sending presence info, version = %d\n", sip
->presence_method_version
);
3654 if (sip
->presence_method_version
!= 1) {
3655 send_presence_info_v0(sip
, note
);
3657 send_presence_info_v1(sip
, note
);
3661 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
3663 gboolean found
= FALSE
;
3664 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
3665 if (msg
->response
== 0) { /* request */
3666 if (!strcmp(msg
->method
, "MESSAGE")) {
3667 process_incoming_message(sip
, msg
);
3669 } else if (!strcmp(msg
->method
, "NOTIFY")) {
3670 purple_debug_info("sipe","send->process_incoming_notify\n");
3671 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
3673 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
3674 purple_debug_info("sipe","send->process_incoming_benotify\n");
3675 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
3677 } else if (!strcmp(msg
->method
, "INVITE")) {
3678 process_incoming_invite(sip
, msg
);
3680 } else if (!strcmp(msg
->method
, "INFO")) {
3682 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3684 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
3687 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3689 } else if (!strcmp(msg
->method
, "ACK")) {
3690 // ACK's don't need any response
3692 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
3693 // LCS 2005 sends us these - just respond 200 OK
3695 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3696 } else if (!strcmp(msg
->method
, "BYE")) {
3697 struct sip_im_session
*session
;
3699 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3701 from
= parse_from(sipmsg_find_header(msg
, "From"));
3702 session
= find_im_session (sip
, from
);
3706 // TODO Let the user know the other user left the conversation?
3707 im_session_destroy(sip
, session
);
3712 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3714 } else { /* response */
3715 struct transaction
*trans
= transactions_find(sip
, msg
);
3717 if (msg
->response
== 407) {
3718 gchar
*resend
, *auth
, *ptmp
;
3720 if (sip
->proxy
.retries
> 30) return;
3721 sip
->proxy
.retries
++;
3722 /* do proxy authentication */
3724 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
3726 fill_auth(sip
, ptmp
, &sip
->proxy
);
3727 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
3728 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
3729 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
3731 resend
= sipmsg_to_string(trans
->msg
);
3732 /* resend request */
3733 sendout_pkt(sip
->gc
, resend
);
3736 if (msg
->response
== 100 || msg
->response
== 180) {
3737 /* ignore provisional response */
3738 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
3740 sip
->proxy
.retries
= 0;
3741 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
3742 if (msg
->response
== 401)
3744 sip
->registrar
.retries
++;
3745 sip
->registrar
.expires
= 0;
3749 sip
->registrar
.retries
= 0;
3751 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
3753 if (msg
->response
== 401) {
3754 gchar
*resend
, *auth
, *ptmp
;
3756 if (sip
->registrar
.retries
> 4) return;
3757 sip
->registrar
.retries
++;
3759 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3760 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
3762 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3765 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
3767 fill_auth(sip
, ptmp
, &sip
->registrar
);
3768 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
3769 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
3770 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
3772 //sipmsg_remove_header(trans->msg, "Authorization");
3773 //sipmsg_add_header(trans->msg, "Authorization", auth);
3775 resend
= sipmsg_to_string(trans
->msg
);
3776 /* resend request */
3777 sendout_pkt(sip
->gc
, resend
);
3782 if (trans
->callback
) {
3783 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
3784 /* call the callback to process response*/
3785 (trans
->callback
)(sip
, msg
, trans
);
3787 /* Not sure if this is needed or what needs to be done
3788 but transactions seem to be removed prematurely so
3789 this only removes them if the response is 200 OK */
3790 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
3791 /*Has a bug and it's unneccesary*/
3792 /*transactions_remove(sip, trans);*/
3798 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction");
3802 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
3806 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
3814 /* according to the RFC remove CRLF at the beginning */
3815 while (*cur
== '\r' || *cur
== '\n') {
3818 if (cur
!= conn
->inbuf
) {
3819 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
3820 conn
->inbufused
= strlen(conn
->inbuf
);
3823 /* Received a full Header? */
3824 sip
->processing_input
= TRUE
;
3825 while (sip
->processing_input
&&
3826 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
3827 time_t currtime
= time(NULL
);
3830 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
3831 msg
= sipmsg_parse_header(conn
->inbuf
);
3834 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
3835 if (restlen
>= msg
->bodylen
) {
3836 dummy
= g_malloc(msg
->bodylen
+ 1);
3837 memcpy(dummy
, cur
, msg
->bodylen
);
3838 dummy
[msg
->bodylen
] = '\0';
3840 cur
+= msg
->bodylen
;
3841 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
3842 conn
->inbufused
= strlen(conn
->inbuf
);
3844 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
3845 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
3851 purple_debug_info("sipe", "body:\n%s", msg->body);
3854 // Verify the signature before processing it
3855 if (sip
->registrar
.ntlm_key
) {
3856 struct sipmsg_breakdown msgbd
;
3857 gchar
*signature_input_str
;
3858 gchar
*signature
= NULL
;
3861 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
3862 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
3863 if (signature_input_str
!= NULL
) {
3864 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
3866 g_free(signature_input_str
);
3868 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
3870 if (signature
!= NULL
) {
3871 if (rspauth
!= NULL
) {
3872 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
3873 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
3874 process_input_message(sip
, msg
);
3876 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
3877 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
3878 sip
->gc
->wants_to_die
= TRUE
;
3880 } else if (msg
->response
== 401) {
3881 purple_connection_error(sip
->gc
, _("Wrong Password"));
3882 sip
->gc
->wants_to_die
= TRUE
;
3888 sipmsg_breakdown_free(&msgbd
);
3890 process_input_message(sip
, msg
);
3897 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
3899 PurpleConnection
*gc
= data
;
3900 struct sipe_account_data
*sip
= gc
->proto_data
;
3905 static char buffer
[65536];
3906 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
3908 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
3909 msg
= sipmsg_parse_msg(buffer
);
3910 if (msg
) process_input_message(sip
, msg
);
3914 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
3916 struct sipe_account_data
*sip
= gc
->proto_data
;
3917 PurpleSslConnection
*gsc
= sip
->gsc
;
3919 purple_debug_error("sipe", "%s",debug
);
3920 purple_connection_error(gc
, msg
);
3922 /* Invalidate this connection. Next send will open a new one */
3924 connection_remove(sip
, gsc
->fd
);
3925 purple_ssl_close(gsc
);
3931 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
3933 PurpleConnection
*gc
= data
;
3934 struct sipe_account_data
*sip
;
3935 struct sip_connection
*conn
;
3937 gboolean firstread
= TRUE
;
3939 /* NOTE: This check *IS* necessary */
3940 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
3941 purple_ssl_close(gsc
);
3945 sip
= gc
->proto_data
;
3946 conn
= connection_find(sip
, gsc
->fd
);
3948 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
3949 gc
->wants_to_die
= TRUE
;
3950 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
3954 /* Read all available data from the SSL connection */
3956 /* Increase input buffer size as needed */
3957 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
3958 conn
->inbuflen
+= SIMPLE_BUF_INC
;
3959 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
3960 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
3963 /* Try to read as much as there is space left in the buffer */
3964 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
3965 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
3967 if (len
< 0 && errno
== EAGAIN
) {
3968 /* Try again later */
3970 } else if (len
< 0) {
3971 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
3973 } else if (firstread
&& (len
== 0)) {
3974 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
3978 conn
->inbufused
+= len
;
3981 /* Equivalence indicates that there is possibly more data to read */
3982 } while (len
== readlen
);
3984 conn
->inbuf
[conn
->inbufused
] = '\0';
3985 process_input(sip
, conn
);
3989 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
3991 PurpleConnection
*gc
= data
;
3992 struct sipe_account_data
*sip
= gc
->proto_data
;
3994 struct sip_connection
*conn
= connection_find(sip
, source
);
3996 purple_debug_error("sipe", "Connection not found!\n");
4000 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
4001 conn
->inbuflen
+= SIMPLE_BUF_INC
;
4002 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
4005 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
4007 if (len
< 0 && errno
== EAGAIN
)
4009 else if (len
<= 0) {
4010 purple_debug_info("sipe", "sipe_input_cb: read error\n");
4011 connection_remove(sip
, source
);
4012 if (sip
->fd
== source
) sip
->fd
= -1;
4016 conn
->inbufused
+= len
;
4017 conn
->inbuf
[conn
->inbufused
] = '\0';
4019 process_input(sip
, conn
);
4022 /* Callback for new connections on incoming TCP port */
4023 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
4025 PurpleConnection
*gc
= data
;
4026 struct sipe_account_data
*sip
= gc
->proto_data
;
4027 struct sip_connection
*conn
;
4029 int newfd
= accept(source
, NULL
, NULL
);
4031 conn
= connection_create(sip
, newfd
);
4033 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4036 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
4038 PurpleConnection
*gc
= data
;
4039 struct sipe_account_data
*sip
;
4040 struct sip_connection
*conn
;
4042 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4050 purple_connection_error(gc
, _("Could not connect"));
4054 sip
= gc
->proto_data
;
4056 sip
->last_keepalive
= time(NULL
);
4058 conn
= connection_create(sip
, source
);
4062 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4065 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
4067 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
4068 if (sip
== NULL
) return;
4073 static guint
sipe_ht_hash_nick(const char *nick
)
4075 char *lc
= g_utf8_strdown(nick
, -1);
4076 guint bucket
= g_str_hash(lc
);
4082 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
4084 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
4087 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
4089 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4091 sip
->listen_data
= NULL
;
4093 if (listenfd
== -1) {
4094 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4100 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
4101 sip
->listenfd
= sip
->fd
;
4103 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
4105 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
4109 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
4111 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4114 sip
->query_data
= NULL
;
4116 if (!hosts
|| !hosts
->data
) {
4117 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
4121 addr_size
= GPOINTER_TO_INT(hosts
->data
);
4122 hosts
= g_slist_remove(hosts
, hosts
->data
);
4123 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
4124 g_free(hosts
->data
);
4125 hosts
= g_slist_remove(hosts
, hosts
->data
);
4127 hosts
= g_slist_remove(hosts
, hosts
->data
);
4128 g_free(hosts
->data
);
4129 hosts
= g_slist_remove(hosts
, hosts
->data
);
4132 /* create socket for incoming connections */
4133 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
4134 sipe_udp_host_resolved_listen_cb
, sip
);
4135 if (sip
->listen_data
== NULL
) {
4136 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4141 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
4144 PurpleConnection
*gc
= data
;
4145 struct sipe_account_data
*sip
;
4147 /* If the connection is already disconnected, we don't need to do anything else */
4148 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4151 sip
= gc
->proto_data
;
4156 case PURPLE_SSL_CONNECT_FAILED
:
4157 purple_connection_error(gc
, _("Connection Failed"));
4159 case PURPLE_SSL_HANDSHAKE_FAILED
:
4160 purple_connection_error(gc
, _("SSL Handshake Failed"));
4162 case PURPLE_SSL_CERTIFICATE_INVALID
:
4163 purple_connection_error(gc
, _("SSL Certificate Invalid"));
4169 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
4171 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4172 PurpleProxyConnectData
*connect_data
;
4174 sip
->listen_data
= NULL
;
4176 sip
->listenfd
= listenfd
;
4177 if (sip
->listenfd
== -1) {
4178 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4182 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
4183 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
4184 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
4185 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
4186 sipe_newconn_cb
, sip
->gc
);
4187 purple_debug_info("sipe", "connecting to %s port %d\n",
4188 sip
->realhostname
, sip
->realport
);
4189 /* open tcp connection to the server */
4190 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
4191 sip
->realport
, login_cb
, sip
->gc
);
4193 if (connect_data
== NULL
) {
4194 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
4199 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
4201 PurpleAccount
*account
= sip
->account
;
4202 PurpleConnection
*gc
= sip
->gc
;
4204 if (purple_account_get_bool(account
, "useport", FALSE
)) {
4205 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
4206 port
= purple_account_get_int(account
, "port", 0);
4208 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
4211 sip
->realhostname
= hostname
;
4212 sip
->realport
= port
;
4214 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
4217 /* TODO: is there a good default grow size? */
4218 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
4219 sip
->txbuf
= purple_circ_buffer_new(0);
4221 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
4223 if (!purple_ssl_is_supported()) {
4224 gc
->wants_to_die
= TRUE
;
4225 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
4229 purple_debug_info("sipe", "using SSL\n");
4231 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
4232 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
4233 if (sip
->gsc
== NULL
) {
4234 purple_connection_error(gc
, _("Could not create SSL context"));
4237 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
4239 purple_debug_info("sipe", "using UDP\n");
4241 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
4242 if (sip
->query_data
== NULL
) {
4243 purple_connection_error(gc
, _("Could not resolve hostname"));
4247 purple_debug_info("sipe", "using TCP\n");
4248 /* create socket for incoming connections */
4249 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
4250 sipe_tcp_connect_listen_cb
, sip
);
4251 if (sip
->listen_data
== NULL
) {
4252 purple_connection_error(gc
, _("Could not create listen socket"));
4258 /* Service list for autodection */
4259 static const struct sipe_service_data service_autodetect
[] = {
4260 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4261 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4262 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4263 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4267 /* Service list for SSL/TLS */
4268 static const struct sipe_service_data service_tls
[] = {
4269 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4270 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4274 /* Service list for TCP */
4275 static const struct sipe_service_data service_tcp
[] = {
4276 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4277 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4281 /* Service list for UDP */
4282 static const struct sipe_service_data service_udp
[] = {
4283 { "sip", "udp", SIPE_TRANSPORT_UDP
},
4287 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
4288 static void resolve_next_service(struct sipe_account_data
*sip
,
4289 const struct sipe_service_data
*start
)
4292 sip
->service_data
= start
;
4294 sip
->service_data
++;
4295 if (sip
->service_data
->service
== NULL
) {
4297 /* Try connecting to the SIP hostname directly */
4298 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
4299 if (sip
->auto_transport
) {
4300 // If SSL is supported, default to using it; OCS servers aren't configured
4301 // by default to accept TCP
4302 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
4303 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
4304 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
4307 hostname
= g_strdup(sip
->sipdomain
);
4308 create_connection(sip
, hostname
, 0);
4313 /* Try to resolve next service */
4314 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
4315 sip
->service_data
->transport
,
4320 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
4322 struct sipe_account_data
*sip
= data
;
4324 sip
->srv_query_data
= NULL
;
4326 /* find the host to connect to */
4328 gchar
*hostname
= g_strdup(resp
->hostname
);
4329 int port
= resp
->port
;
4330 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
4334 sip
->transport
= sip
->service_data
->type
;
4336 create_connection(sip
, hostname
, port
);
4338 resolve_next_service(sip
, NULL
);
4342 static void sipe_login(PurpleAccount
*account
)
4344 PurpleConnection
*gc
;
4345 struct sipe_account_data
*sip
;
4346 gchar
**signinname_login
, **userserver
, **domain_user
;
4347 const char *transport
;
4349 const char *username
= purple_account_get_username(account
);
4350 gc
= purple_account_get_connection(account
);
4352 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
4353 gc
->wants_to_die
= TRUE
;
4354 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
4358 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
4359 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
4360 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
4362 sip
->account
= account
;
4363 sip
->registerexpire
= 900;
4364 sip
->reregister_set
= FALSE
;
4365 sip
->reauthenticate_set
= FALSE
;
4366 sip
->subscribed
= FALSE
;
4367 sip
->subscribed_buddies
= FALSE
;
4369 signinname_login
= g_strsplit(username
, ",", 2);
4371 userserver
= g_strsplit(signinname_login
[0], "@", 2);
4372 purple_connection_set_display_name(gc
, userserver
[0]);
4373 sip
->username
= g_strjoin("@", userserver
[0], userserver
[1], NULL
);
4374 sip
->sipdomain
= g_strdup(userserver
[1]);
4376 domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
4377 sip
->authdomain
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[0]) : "";
4378 sip
->authuser
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[1]) : (signinname_login
? g_strdup(signinname_login
[1]) : NULL
);
4380 sip
->password
= g_strdup(purple_connection_get_password(gc
));
4382 g_strfreev(userserver
);
4383 g_strfreev(domain_user
);
4384 g_strfreev(signinname_login
);
4386 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
4388 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
4390 /* TODO: Set the status correctly. */
4391 sip
->status
= g_strdup("available");
4393 transport
= purple_account_get_string(account
, "transport", "auto");
4394 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
4395 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
4398 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
4399 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
4400 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
4401 } else if (strcmp(transport
, "auto") == 0) {
4402 sip
->auto_transport
= TRUE
;
4403 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
4404 } else if (strcmp(transport
, "tls") == 0) {
4405 resolve_next_service(sip
, service_tls
);
4406 } else if (strcmp(transport
, "tcp") == 0) {
4407 resolve_next_service(sip
, service_tcp
);
4409 resolve_next_service(sip
, service_udp
);
4413 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
4415 connection_free_all(sip
);
4417 if (sip
->query_data
!= NULL
)
4418 purple_dnsquery_destroy(sip
->query_data
);
4419 sip
->query_data
== NULL
;
4421 if (sip
->srv_query_data
!= NULL
)
4422 purple_srv_cancel(sip
->srv_query_data
);
4423 sip
->srv_query_data
= NULL
;
4425 if (sip
->listen_data
!= NULL
)
4426 purple_network_listen_cancel(sip
->listen_data
);
4427 sip
->listen_data
= NULL
;
4429 if (sip
->gsc
!= NULL
)
4430 purple_ssl_close(sip
->gsc
);
4433 sipe_auth_free(&sip
->registrar
);
4434 sipe_auth_free(&sip
->proxy
);
4437 purple_circ_buffer_destroy(sip
->txbuf
);
4440 g_free(sip
->realhostname
);
4441 sip
->realhostname
= NULL
;
4444 purple_input_remove(sip
->listenpa
);
4446 if (sip
->tx_handler
)
4447 purple_input_remove(sip
->tx_handler
);
4448 sip
->tx_handler
= 0;
4449 if (sip
->resendtimeout
)
4450 purple_timeout_remove(sip
->resendtimeout
);
4451 sip
->resendtimeout
= 0;
4452 if (sip
->timeouts
) {
4453 GSList
*entry
= sip
->timeouts
;
4455 struct scheduled_action
*sched_action
= entry
->data
;
4456 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
4457 purple_timeout_remove(sched_action
->timeout_handler
);
4458 g_free(sched_action
->payload
);
4459 g_free(sched_action
->name
);
4460 g_free(sched_action
);
4461 entry
= entry
->next
;
4464 g_slist_free(sip
->timeouts
);
4467 g_free(sip
->contact
);
4468 sip
->contact
= NULL
;
4471 sip
->processing_input
= FALSE
;
4474 static void sipe_close(PurpleConnection
*gc
)
4476 struct sipe_account_data
*sip
= gc
->proto_data
;
4479 /* leave all conversations */
4480 im_session_close_all(sip
);
4483 do_register_exp(sip
, 0);
4485 sipe_connection_cleanup(sip
);
4486 g_free(sip
->sipdomain
);
4487 g_free(sip
->username
);
4488 g_free(sip
->password
);
4490 g_free(gc
->proto_data
);
4491 gc
->proto_data
= NULL
;
4494 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
4496 PurpleAccount
*acct
= purple_connection_get_account(gc
);
4497 char *id
= g_strdup_printf("sip:%s", (char *)g_list_nth_data(row
, 0));
4498 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
4500 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
4501 purple_conversation_present(conv
);
4505 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
4508 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
4509 g_list_nth_data(row
, 0), NULL
, g_list_nth_data(row
, 1));
4512 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,struct transaction
*tc
)
4514 PurpleNotifySearchResults
*results
;
4515 PurpleNotifySearchColumn
*column
;
4516 xmlnode
*searchResults
;
4518 int match_count
= 0;
4519 gboolean more
= FALSE
;
4522 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4523 if (!searchResults
) {
4524 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
4528 results
= purple_notify_searchresults_new();
4530 if (results
== NULL
) {
4531 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
4532 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
4534 xmlnode_free(searchResults
);
4538 column
= purple_notify_searchresults_column_new(_("User Name"));
4539 purple_notify_searchresults_column_add(results
, column
);
4541 column
= purple_notify_searchresults_column_new(_("Name"));
4542 purple_notify_searchresults_column_add(results
, column
);
4544 column
= purple_notify_searchresults_column_new(_("Company"));
4545 purple_notify_searchresults_column_add(results
, column
);
4547 column
= purple_notify_searchresults_column_new(_("Country"));
4548 purple_notify_searchresults_column_add(results
, column
);
4550 column
= purple_notify_searchresults_column_new(_("Email"));
4551 purple_notify_searchresults_column_add(results
, column
);
4553 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
4556 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
4557 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
4558 g_strfreev(uri_parts
);
4560 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
4561 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
4562 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
4563 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
4565 purple_notify_searchresults_row_add(results
, row
);
4569 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
4570 char *data
= xmlnode_get_data_unescaped(mrow
);
4571 more
= (g_strcasecmp(data
, "true") == 0);
4575 secondary
= g_strdup_printf(
4576 dngettext(GETTEXT_PACKAGE
,
4577 "Found %d contact%s:",
4578 "Found %d contacts%s:", match_count
),
4579 match_count
, more
? _(" (more matched your query)") : "");
4581 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
4582 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
4583 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
4586 xmlnode_free(searchResults
);
4590 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
4592 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
4593 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
4597 PurpleRequestField
*field
= entries
->data
;
4598 const char *id
= purple_request_field_get_id(field
);
4599 const char *value
= purple_request_field_string_get_value(field
);
4601 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
4603 if (value
!= NULL
) attrs
[i
++] = g_strdup_printf(SIPE_SOAP_SEARCH_ROW
, id
, value
);
4604 } while ((entries
= g_list_next(entries
)) != NULL
);
4608 gchar
*query
= g_strjoinv(NULL
, attrs
);
4609 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
4610 send_soap_request_with_cb(gc
->proto_data
, body
,
4611 (TransCallback
) process_search_contact_response
, NULL
);
4619 static void sipe_show_find_contact(PurplePluginAction
*action
)
4621 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
4622 PurpleRequestFields
*fields
;
4623 PurpleRequestFieldGroup
*group
;
4624 PurpleRequestField
*field
;
4626 fields
= purple_request_fields_new();
4627 group
= purple_request_field_group_new(NULL
);
4628 purple_request_fields_add_group(fields
, group
);
4630 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
4631 purple_request_field_group_add_field(group
, field
);
4632 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
4633 purple_request_field_group_add_field(group
, field
);
4634 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
4635 purple_request_field_group_add_field(group
, field
);
4636 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
4637 purple_request_field_group_add_field(group
, field
);
4639 purple_request_fields(gc
,
4641 _("Search for a Contact"),
4642 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
4644 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
4646 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
4649 GList
*sipe_actions(PurplePlugin
*plugin
, gpointer context
)
4652 PurplePluginAction
*act
;
4654 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
4655 menu
= g_list_prepend(menu
, act
);
4657 menu
= g_list_reverse(menu
);
4662 static void dummy_permit_deny(PurpleConnection
*gc
)
4666 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
4672 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
4678 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
4682 static char *sipe_status_text(PurpleBuddy
*buddy
)
4684 struct sipe_account_data
*sip
;
4685 struct sipe_buddy
*sbuddy
;
4688 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
4689 if (sip
) //happens on pidgin exit
4691 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
4692 if (sbuddy
&& sbuddy
->annotation
)
4694 text
= g_strdup(sbuddy
->annotation
);
4701 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
4703 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
4704 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
4705 struct sipe_account_data
*sip
;
4706 struct sipe_buddy
*sbuddy
;
4708 char *annotation
= NULL
;
4709 char *device_name
= NULL
;
4711 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
4712 if (sip
) //happens on pidgin exit
4714 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
4717 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
4718 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
4723 if (purple_presence_is_online(presence
))
4725 purple_notify_user_info_add_pair(user_info
, _("Status"), purple_status_get_name(status
));
4730 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
4734 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
4737 purple_notify_user_info_add_pair(user_info
, _("Email"), email
);
4742 purple_notify_user_info_add_pair(user_info
, _("Device"), device_name
);
4747 sipe_get_account_text_table(PurpleAccount
*account
)
4750 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
4751 g_hash_table_insert(table
, "login_label", (gpointer
)_("Sign-In Name..."));
4755 static PurpleBuddy
*
4756 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
4759 const gchar
*server_alias
, *email
;
4760 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
4762 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
4764 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
4766 server_alias
= g_strdup(purple_buddy_get_server_alias(buddy
));
4768 purple_blist_server_alias_buddy(clone
, server_alias
);
4771 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
4773 purple_blist_node_set_string((PurpleBlistNode
*)clone
, "email", email
);
4776 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
4778 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
4783 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
4785 PurpleBuddy
*buddy
, *b
;
4786 PurpleConnection
*gc
;
4787 PurpleGroup
* group
= purple_find_group(group_name
);
4789 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
4791 buddy
= (PurpleBuddy
*)node
;
4793 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
4794 gc
= purple_account_get_connection(buddy
->account
);
4796 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
4798 b
= purple_blist_add_buddy_clone(group
, buddy
);
4801 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
4805 * A menu which appear when right-clicking on buddy in contact list.
4808 sipe_buddy_menu(PurpleBuddy
*buddy
)
4810 PurpleBlistNode
*g_node
;
4811 PurpleGroup
*group
, *gr_parent
;
4812 PurpleMenuAction
*act
;
4814 GList
*menu_groups
= NULL
;
4816 gr_parent
= purple_buddy_get_group(buddy
);
4817 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
4818 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
4821 group
= (PurpleGroup
*)g_node
;
4822 if (group
== gr_parent
)
4825 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
4828 act
= purple_menu_action_new(purple_group_get_name(group
),
4829 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
4831 menu_groups
= g_list_prepend(menu_groups
, act
);
4833 menu_groups
= g_list_reverse(menu_groups
);
4835 act
= purple_menu_action_new(_("Copy to"),
4838 menu
= g_list_prepend(menu
, act
);
4839 menu
= g_list_reverse(menu
);
4844 GList
*sipe_blist_node_menu(PurpleBlistNode
*node
) {
4845 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
4846 return sipe_buddy_menu((PurpleBuddy
*) node
);
4852 static PurplePlugin
*my_protocol
= NULL
;
4854 static PurplePluginProtocolInfo prpl_info
=
4857 NULL
, /* user_splits */
4858 NULL
, /* protocol_options */
4859 NO_BUDDY_ICONS
, /* icon_spec */
4860 sipe_list_icon
, /* list_icon */
4861 NULL
, /* list_emblems */
4862 sipe_status_text
, /* status_text */
4863 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
4864 sipe_status_types
, /* away_states */
4865 sipe_blist_node_menu
, /* blist_node_menu */
4866 NULL
, /* chat_info */
4867 NULL
, /* chat_info_defaults */
4868 sipe_login
, /* login */
4869 sipe_close
, /* close */
4870 sipe_im_send
, /* send_im */
4871 NULL
, /* set_info */ // TODO maybe
4872 sipe_send_typing
, /* send_typing */
4873 NULL
, /* get_info */ // TODO maybe
4874 sipe_set_status
, /* set_status */
4875 NULL
, /* set_idle */
4876 NULL
, /* change_passwd */
4877 sipe_add_buddy
, /* add_buddy */
4878 NULL
, /* add_buddies */
4879 sipe_remove_buddy
, /* remove_buddy */
4880 NULL
, /* remove_buddies */
4881 sipe_add_permit
, /* add_permit */
4882 sipe_add_deny
, /* add_deny */
4883 sipe_add_deny
, /* rem_permit */
4884 sipe_add_permit
, /* rem_deny */
4885 dummy_permit_deny
, /* set_permit_deny */
4886 NULL
, /* join_chat */
4887 NULL
, /* reject_chat */
4888 NULL
, /* get_chat_name */
4889 NULL
, /* chat_invite */
4890 NULL
, /* chat_leave */
4891 NULL
, /* chat_whisper */
4892 NULL
, /* chat_send */
4893 sipe_keep_alive
, /* keepalive */
4894 NULL
, /* register_user */
4895 NULL
, /* get_cb_info */ // deprecated
4896 NULL
, /* get_cb_away */ // deprecated
4897 sipe_alias_buddy
, /* alias_buddy */
4898 sipe_group_buddy
, /* group_buddy */
4899 sipe_rename_group
, /* rename_group */
4900 NULL
, /* buddy_free */
4901 sipe_convo_closed
, /* convo_closed */
4902 purple_normalize_nocase
, /* normalize */
4903 NULL
, /* set_buddy_icon */
4904 sipe_remove_group
, /* remove_group */
4905 NULL
, /* get_cb_real_name */ // TODO?
4906 NULL
, /* set_chat_topic */
4907 NULL
, /* find_blist_chat */
4908 NULL
, /* roomlist_get_list */
4909 NULL
, /* roomlist_cancel */
4910 NULL
, /* roomlist_expand_category */
4911 NULL
, /* can_receive_file */
4912 NULL
, /* send_file */
4913 NULL
, /* new_xfer */
4914 NULL
, /* offline_message */
4915 NULL
, /* whiteboard_prpl_ops */
4916 sipe_send_raw
, /* send_raw */
4917 NULL
, /* roomlist_room_serialize */
4918 NULL
, /* unregister_user */
4919 NULL
, /* send_attention */
4920 NULL
, /* get_attention_types */
4922 sizeof(PurplePluginProtocolInfo
), /* struct_size */
4923 sipe_get_account_text_table
, /* get_account_text_table */
4927 static PurplePluginInfo info
= {
4928 PURPLE_PLUGIN_MAGIC
,
4929 PURPLE_MAJOR_VERSION
,
4930 PURPLE_MINOR_VERSION
,
4931 PURPLE_PLUGIN_PROTOCOL
, /**< type */
4932 NULL
, /**< ui_requirement */
4934 NULL
, /**< dependencies */
4935 PURPLE_PRIORITY_DEFAULT
, /**< priority */
4936 "prpl-sipe", /**< id */
4937 "Microsoft LCS/OCS", /**< name */
4938 VERSION
, /**< version */
4939 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
4940 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
4941 "Anibal Avelar <avelar@gmail.com>, " /**< author */
4942 "Gabriel Burt <gburt@novell.com>", /**< author */
4943 PURPLE_WEBSITE
, /**< homepage */
4944 sipe_plugin_load
, /**< load */
4945 sipe_plugin_unload
, /**< unload */
4946 sipe_plugin_destroy
, /**< destroy */
4947 NULL
, /**< ui_info */
4948 &prpl_info
, /**< extra_info */
4957 static void init_plugin(PurplePlugin
*plugin
)
4959 PurpleAccountUserSplit
*split
;
4960 PurpleAccountOption
*option
;
4963 purple_debug_info(PACKAGE
, "bindtextdomain = %s", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
4964 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s",
4965 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
4968 purple_plugin_register(plugin
);
4970 split
= purple_account_user_split_new(_("Login \n domain\\user or\n someone@linux.com "), NULL
, ',');
4971 purple_account_user_split_set_reverse(split
, FALSE
);
4972 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
4974 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
4975 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4976 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
4977 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4979 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
4980 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4981 // Translators: noun (networking port)
4982 option
= purple_account_option_int_new(_("Port"), "port", 5061);
4983 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4985 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
4986 purple_account_option_add_list_item(option
, _("Auto"), "auto");
4987 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
4988 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
4989 purple_account_option_add_list_item(option
, _("UDP"), "udp");
4990 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4992 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
4993 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
4995 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
4996 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4998 // TODO commented out so won't show in the preferences until we fix krb message signing
4999 /*option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
5000 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5002 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
5003 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
5004 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5007 option
= purple_account_option_bool_new(_("Use Client-specified Keepalive"), "clientkeepalive", FALSE
);
5008 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5009 option
= purple_account_option_int_new(_("Keepalive Timeout"), "keepalive", 300);
5010 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5011 my_protocol
= plugin
;
5014 /* I had to redefined the function for it load, but works */
5015 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
5016 plugin
->info
= &(info
);
5017 init_plugin((plugin
));
5018 sipe_plugin_load((plugin
));
5019 return purple_plugin_register(plugin
);