6 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
7 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
8 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
9 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
12 * Thanks to Google's Summer of Code Program and the helpful mentors
15 * Session-based SIP MESSAGE documentation:
16 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 #include <sys/socket.h>
35 #include <sys/ioctl.h>
36 #include <sys/types.h>
37 #include <netinet/in.h>
43 #define _LIBC_INTERNAL_
56 #include "accountopt.h"
58 #include "conversation.h"
72 #include "sipe-conf.h"
73 #include "sipe-dialog.h"
75 #include "sipe-utils.h"
77 #include "sipe-sign.h"
81 /* Keep in sync with sipe_transport_type! */
82 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
83 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
85 /* Status identifiers (see also: sipe_status_types()) */
86 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
87 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
88 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
89 /* PURPLE_STATUS_UNAVAILABLE: */
90 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
91 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
92 #define SIPE_STATUS_ID_ONPHONE "on-the-phone" /* On The Phone */
93 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
94 /* PURPLE_STATUS_AWAY: */
95 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
96 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
97 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
98 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
99 /* ??? PURPLE_STATUS_MOBILE */
100 /* ??? PURPLE_STATUS_TUNE */
102 /* Action name templates */
103 #define ACTION_NAME_PRESENCE "<presence><%s>"
105 static gchar
*get_epid(struct sipe_account_data
*sip
)
108 sip
->epid
= sipe_uuid_get_macaddr(purple_network_get_my_ip(-1));
110 return g_strdup(sip
->epid
);
113 static char *genbranch()
115 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
116 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
117 rand() & 0xFFFF, rand() & 0xFFFF);
120 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
125 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
127 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
129 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
130 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
133 static void sipe_close(PurpleConnection
*gc
);
135 static void send_presence_status(struct sipe_account_data
*sip
);
137 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
139 static void sipe_keep_alive(PurpleConnection
*gc
)
141 struct sipe_account_data
*sip
= gc
->proto_data
;
142 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
143 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
144 gchar buf
[2] = {0, 0};
145 purple_debug_info("sipe", "sending keep alive\n");
146 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
148 time_t now
= time(NULL
);
149 if ((sip
->keepalive_timeout
> 0) &&
150 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
151 #if PURPLE_VERSION_CHECK(2,4,0)
152 && ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
155 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
156 sendout_pkt(gc
, "\r\n\r\n");
157 sip
->last_keepalive
= now
;
162 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
164 struct sip_connection
*ret
= NULL
;
165 GSList
*entry
= sip
->openconns
;
168 if (ret
->fd
== fd
) return ret
;
174 static void sipe_auth_free(struct sip_auth
*auth
)
176 g_free(auth
->opaque
);
180 g_free(auth
->target
);
182 auth
->type
= AUTH_TYPE_UNSET
;
185 g_free(auth
->gssapi_data
);
186 auth
->gssapi_data
= NULL
;
187 sip_sec_destroy_context(auth
->gssapi_context
);
188 auth
->gssapi_context
= NULL
;
191 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
193 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
195 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
199 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
201 struct sip_connection
*conn
= connection_find(sip
, fd
);
203 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
204 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
210 static void connection_free_all(struct sipe_account_data
*sip
)
212 struct sip_connection
*ret
= NULL
;
213 GSList
*entry
= sip
->openconns
;
216 connection_remove(sip
, ret
->fd
);
217 entry
= sip
->openconns
;
221 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
224 const char *authuser
= sip
->authuser
;
228 if (!authuser
|| strlen(authuser
) < 1) {
229 authuser
= sip
->username
;
232 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
233 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
235 // If we have a signature for the message, include that
236 if (msg
->signature
) {
237 return g_strdup_printf("%s qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", crand=\"%s\", cnum=\"%s\", response=\"%s\"", auth_protocol
, auth
->opaque
, auth
->realm
, auth
->target
, msg
->rand
, msg
->num
, msg
->signature
);
240 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
241 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
245 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
248 purple_account_get_bool(sip
->account
, "sso", TRUE
),
249 sip
->authdomain
? sip
->authdomain
: "",
254 if (!gssapi_data
|| !auth
->gssapi_context
)
257 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
258 ret
= g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth_protocol
, opaque
, auth
->realm
, auth
->target
, gssapi_data
);
264 return g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth_protocol
, auth
->realm
, auth
->target
);
266 } else { /* Digest */
268 /* Calculate new session key */
270 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
271 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
272 authuser
, auth
->realm
, sip
->password
,
273 auth
->gssapi_data
, NULL
);
276 sprintf(noncecount
, "%08d", auth
->nc
++);
277 response
= purple_cipher_http_digest_calculate_response("md5",
278 msg
->method
, msg
->target
, NULL
, NULL
,
279 auth
->gssapi_data
, noncecount
, NULL
,
281 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
283 ret
= g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"", authuser
, auth
->realm
, auth
->gssapi_data
, msg
->target
, noncecount
, response
);
289 static char *parse_attribute(const char *attrname
, const char *source
)
291 const char *tmp
, *tmp2
;
293 int len
= strlen(attrname
);
295 if (!strncmp(source
, attrname
, len
)) {
297 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
299 retval
= g_strndup(tmp
, tmp2
- tmp
);
301 retval
= g_strdup(tmp
);
307 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
313 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
317 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
318 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
319 auth
->type
= AUTH_TYPE_NTLM
;
322 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
323 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
324 auth
->type
= AUTH_TYPE_KERBEROS
;
328 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
329 auth
->type
= AUTH_TYPE_DIGEST
;
333 parts
= g_strsplit(hdr
, "\", ", 0);
334 for (i
= 0; parts
[i
]; i
++) {
337 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
339 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
340 g_free(auth
->gssapi_data
);
341 auth
->gssapi_data
= tmp
;
343 if (auth
->type
== AUTH_TYPE_NTLM
) {
344 /* NTLM module extracts nonce from gssapi-data */
348 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
349 /* Only used with AUTH_TYPE_DIGEST */
350 g_free(auth
->gssapi_data
);
351 auth
->gssapi_data
= tmp
;
352 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
353 g_free(auth
->opaque
);
355 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
359 if (auth
->type
== AUTH_TYPE_DIGEST
) {
360 /* Throw away old session key */
361 g_free(auth
->opaque
);
366 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
367 g_free(auth
->target
);
376 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
378 PurpleConnection
*gc
= data
;
379 struct sipe_account_data
*sip
= gc
->proto_data
;
383 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
385 if (max_write
== 0) {
386 if (sip
->tx_handler
!= 0){
387 purple_input_remove(sip
->tx_handler
);
393 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
395 if (written
< 0 && errno
== EAGAIN
)
397 else if (written
<= 0) {
398 /*TODO: do we really want to disconnect on a failure to write?*/
399 purple_connection_error(gc
, _("Could not write"));
403 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
406 static void sipe_canwrite_cb_ssl(gpointer data
, gint src
, PurpleInputCondition cond
)
408 PurpleConnection
*gc
= data
;
409 struct sipe_account_data
*sip
= gc
->proto_data
;
413 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
415 if (max_write
== 0) {
416 if (sip
->tx_handler
!= 0) {
417 purple_input_remove(sip
->tx_handler
);
423 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
425 if (written
< 0 && errno
== EAGAIN
)
427 else if (written
<= 0) {
428 /*TODO: do we really want to disconnect on a failure to write?*/
429 purple_connection_error(gc
, _("Could not write"));
433 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
436 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
438 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
440 PurpleConnection
*gc
= data
;
441 struct sipe_account_data
*sip
;
442 struct sip_connection
*conn
;
444 if (!PURPLE_CONNECTION_IS_VALID(gc
))
452 purple_connection_error(gc
, _("Could not connect"));
456 sip
= gc
->proto_data
;
458 sip
->connecting
= FALSE
;
459 sip
->last_keepalive
= time(NULL
);
461 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
463 /* If there is more to write now, we need to register a handler */
464 if (sip
->txbuf
->bufused
> 0)
465 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
467 conn
= connection_create(sip
, source
);
468 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
471 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
473 struct sipe_account_data
*sip
;
474 struct sip_connection
*conn
;
476 if (!PURPLE_CONNECTION_IS_VALID(gc
))
478 if (gsc
) purple_ssl_close(gsc
);
482 sip
= gc
->proto_data
;
485 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
486 sip
->connecting
= FALSE
;
487 sip
->last_keepalive
= time(NULL
);
489 conn
= connection_create(sip
, gsc
->fd
);
491 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
496 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
498 PurpleConnection
*gc
= data
;
499 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
500 if (sip
== NULL
) return;
502 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
504 /* If there is more to write now */
505 if (sip
->txbuf
->bufused
> 0) {
506 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
511 static void sendlater(PurpleConnection
*gc
, const char *buf
)
513 struct sipe_account_data
*sip
= gc
->proto_data
;
515 if (!sip
->connecting
) {
516 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
517 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
518 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
520 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
521 purple_connection_error(gc
, _("Couldn't create socket"));
524 sip
->connecting
= TRUE
;
527 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
528 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
530 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
533 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
535 struct sipe_account_data
*sip
= gc
->proto_data
;
536 time_t currtime
= time(NULL
);
537 int writelen
= strlen(buf
);
539 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
540 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
541 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
542 purple_debug_info("sipe", "could not send packet\n");
551 if (sip
->tx_handler
) {
556 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
558 ret
= write(sip
->fd
, buf
, writelen
);
562 if (ret
< 0 && errno
== EAGAIN
)
564 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
569 if (ret
< writelen
) {
570 if (!sip
->tx_handler
){
572 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
575 sip
->tx_handler
= purple_input_add(sip
->fd
,
576 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
581 /* XXX: is it OK to do this? You might get part of a request sent
582 with part of another. */
583 if (sip
->txbuf
->bufused
> 0)
584 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
586 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
592 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
594 sendout_pkt(gc
, buf
);
598 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
600 GSList
*tmp
= msg
->headers
;
603 GString
*outstr
= g_string_new("");
604 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
606 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
607 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
608 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
609 tmp
= g_slist_next(tmp
);
611 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
612 sendout_pkt(sip
->gc
, outstr
->str
);
613 g_string_free(outstr
, TRUE
);
616 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
619 if (sip
->registrar
.gssapi_context
) {
620 struct sipmsg_breakdown msgbd
;
621 gchar
*signature_input_str
;
623 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
624 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
625 sip
->registrar
.ntlm_num
++;
626 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
627 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
628 if (signature_input_str
!= NULL
) {
629 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
630 msg
->signature
= signature_hex
;
631 msg
->rand
= g_strdup(msgbd
.rand
);
632 msg
->num
= g_strdup(msgbd
.num
);
633 g_free(signature_input_str
);
635 sipmsg_breakdown_free(&msgbd
);
638 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
639 buf
= auth_header(sip
, &sip
->registrar
, msg
);
640 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
642 } 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") || !strcmp(method
, "OPTIONS") || !strcmp(method
, "REFER")) {
643 sip
->registrar
.nc
= 3;
645 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
647 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
650 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
655 buf
= auth_header(sip
, &sip
->registrar
, msg
);
656 sipmsg_add_header_now_pos(msg
, "Proxy-Authorization", buf
, 5);
659 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
665 static char *get_contact_service(struct sipe_account_data *sip)
667 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()));
668 //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);
672 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
673 const char *text
, const char *body
)
677 GString
*outstr
= g_string_new("");
678 struct sipe_account_data
*sip
= gc
->proto_data
;
681 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
683 contact
= get_contact(sip
);
684 sipmsg_add_header(msg
, "Contact", contact
);
689 sprintf(len
, "%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
690 sipmsg_add_header(msg
, "Content-Length", len
);
692 sipmsg_add_header(msg
, "Content-Length", "0");
695 msg
->response
= code
;
697 sipmsg_strip_headers(msg
, keepers
);
698 sipmsg_merge_new_headers(msg
);
699 sign_outgoing_message(msg
, sip
, msg
->method
);
701 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
704 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
705 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
707 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
708 tmp
= g_slist_next(tmp
);
710 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
711 sendout_pkt(gc
, outstr
->str
);
712 g_string_free(outstr
, TRUE
);
715 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
717 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
718 if (trans
->msg
) sipmsg_free(trans
->msg
);
723 static struct transaction
*
724 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
726 gchar
*call_id
= NULL
;
728 struct transaction
*trans
= g_new0(struct transaction
, 1);
730 trans
->time
= time(NULL
);
731 trans
->msg
= (struct sipmsg
*)msg
;
732 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
733 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
734 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
735 trans
->callback
= callback
;
736 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
740 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
742 struct transaction
*trans
;
743 GSList
*transactions
= sip
->transactions
;
744 gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
745 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
746 gchar
*key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
748 while (transactions
) {
749 trans
= transactions
->data
;
750 if (!g_strcasecmp(trans
->key
, key
)) {
754 transactions
= transactions
->next
;
762 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
763 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
764 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
766 struct sipe_account_data
*sip
= gc
->proto_data
;
767 const char *addh
= "";
770 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
771 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
772 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
773 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
774 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
775 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
776 gchar
*route
= strdup("");
777 gchar
*epid
= get_epid(sip
); // TODO generate one per account/login
778 int cseq
= dialog
? ++dialog
->cseq
:
779 /* This breaks OCS2007: own presence, contact search, ?
780 1 .* as Call-Id is new in this case */
782 struct transaction
*trans
;
784 if (dialog
&& dialog
->routes
)
786 GSList
*iter
= dialog
->routes
;
791 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
793 iter
= g_slist_next(iter
);
797 if (!ourtag
&& !dialog
) {
801 if (!strcmp(method
, "REGISTER")) {
802 if (sip
->regcallid
) {
804 callid
= g_strdup(sip
->regcallid
);
806 sip
->regcallid
= g_strdup(callid
);
810 if (addheaders
) addh
= addheaders
;
812 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
813 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
814 "From: <sip:%s>%s%s;epid=%s\r\n"
815 "To: <%s>%s%s%s%s\r\n"
816 "Max-Forwards: 70\r\n"
821 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
823 dialog
&& dialog
->request
? dialog
->request
: url
,
824 TRANSPORT_DESCRIPTOR
,
825 purple_network_get_my_ip(-1),
827 branch
? ";branch=" : "",
828 branch
? branch
: "",
830 ourtag
? ";tag=" : "",
831 ourtag
? ourtag
: "",
834 theirtag
? ";tag=" : "",
835 theirtag
? theirtag
: "",
836 theirepid
? ";epid=" : "",
837 theirepid
? theirepid
: "",
844 body
? (gsize
) strlen(body
) : 0,
848 //printf ("parsing msg buf:\n%s\n\n", buf);
849 msg
= sipmsg_parse_msg(buf
);
860 sign_outgoing_message (msg
, sip
, method
);
862 buf
= sipmsg_to_string (msg
);
864 /* add to ongoing transactions */
865 trans
= transactions_add_buf(sip
, msg
, tc
);
866 sendout_pkt(gc
, buf
);
872 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
874 gchar
*from
= g_strdup_printf("sip:%s", sip
->username
);
875 gchar
*contact
= get_contact(sip
);
876 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
877 "Content-Type: application/SOAP+xml\r\n",contact
);
879 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
880 tr
->payload
= payload
;
887 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
889 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
892 static char *get_contact_register(struct sipe_account_data
*sip
)
894 char *epid
= get_epid(sip
);
895 char *uuid
= generateUUIDfromEPID(epid
);
896 char *buf
= g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY\";proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip
->listenport
, TRANSPORT_DESCRIPTOR
, uuid
);
902 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
904 char *expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
905 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
906 char *to
= g_strdup_printf("sip:%s", sip
->username
);
907 char *contact
= get_contact_register(sip
);
908 char *hdr
= g_strdup_printf("Contact: %s\r\n"
909 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
910 "Event: registration\r\n"
911 "Allow-Events: presence\r\n"
912 "ms-keep-alive: UAC;hop-hop=yes\r\n"
913 "%s", contact
, expires
);
917 sip
->registerstatus
= 1;
919 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
920 process_register_response
);
927 static void do_register_cb(struct sipe_account_data
*sip
, void *unused
)
929 do_register_exp(sip
, -1);
930 sip
->reregister_set
= FALSE
;
933 static void do_register(struct sipe_account_data
*sip
)
935 do_register_exp(sip
, -1);
939 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
941 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
942 send_soap_request(sip
, body
);
947 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
950 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
952 purple_debug_info("sipe", "Blocking contact %s\n", who
);
955 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
959 void sipe_auth_user_cb(void * data
)
961 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
964 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
969 void sipe_deny_user_cb(void * data
)
971 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
974 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
979 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
981 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
982 sipe_contact_allow_deny(sip
, name
, TRUE
);
986 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
988 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
989 sipe_contact_allow_deny(sip
, name
, FALSE
);
993 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
995 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
996 sipe_contact_set_acl(sip, name, "");
1000 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1004 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1005 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1007 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1009 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1010 if (!watchers
) return;
1012 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1013 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1014 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1015 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1017 // TODO pull out optional displayName to pass as alias
1019 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1020 job
->who
= remote_user
;
1022 purple_account_request_authorization(
1036 xmlnode_free(watchers
);
1041 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1043 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1044 if (!purple_group
) {
1045 purple_group
= purple_group_new(group
->name
);
1046 purple_blist_add_group(purple_group
, NULL
);
1050 group
->purple_group
= purple_group
;
1051 sip
->groups
= g_slist_append(sip
->groups
, group
);
1052 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1054 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1058 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1060 struct sipe_group
*group
;
1066 entry
= sip
->groups
;
1068 group
= entry
->data
;
1069 if (group
->id
== id
) {
1072 entry
= entry
->next
;
1077 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1079 struct sipe_group
*group
;
1085 entry
= sip
->groups
;
1087 group
= entry
->data
;
1088 if (!strcmp(group
->name
, name
)) {
1091 entry
= entry
->next
;
1097 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1100 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1101 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1102 send_soap_request(sip
, body
);
1104 g_free(group
->name
);
1105 group
->name
= g_strdup(name
);
1109 * Only appends if no such value already stored.
1112 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1113 GSList
* res
= list
;
1114 if (!g_slist_find_custom(list
, data
, func
)) {
1115 res
= g_slist_insert_sorted(list
, data
, func
);
1121 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1122 return group1
->id
- group2
->id
;
1126 * Returns string like "2 4 7 8" - group ids buddy belong to.
1129 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1132 //creating array from GList, converting int to gchar*
1133 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1134 GSList
*entry
= buddy
->groups
;
1136 struct sipe_group
* group
= entry
->data
;
1137 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1138 entry
= entry
->next
;
1142 res
= g_strjoinv(" ", ids_arr
);
1143 g_strfreev(ids_arr
);
1148 * Sends buddy update to server
1151 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1153 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1154 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1156 if (buddy
&& purple_buddy
) {
1157 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1159 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1160 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1162 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1163 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1165 send_soap_request(sip
, body
);
1171 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1173 if (msg
->response
== 200) {
1174 struct sipe_group
*group
;
1175 struct group_user_context
*ctx
= (struct group_user_context
*)tc
->payload
;
1179 struct sipe_buddy
*buddy
;
1181 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1187 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1194 group_id
= xmlnode_get_data(node
);
1201 group
= g_new0(struct sipe_group
, 1);
1202 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1204 group
->name
= ctx
->group_name
;
1206 sipe_group_add(sip
, group
);
1208 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1210 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1213 sipe_group_set_user(sip
, ctx
->user_name
);
1222 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1224 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1226 ctx
->group_name
= g_strdup(name
);
1227 ctx
->user_name
= g_strdup(who
);
1229 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1230 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1235 * Data structure for scheduled actions
1237 typedef void (*Action
) (struct sipe_account_data
*, void *);
1239 struct scheduled_action
{
1242 * Format is <Event>[<Data>...]
1243 * Example: <presence><sip:user@domain.com> or <registration>
1246 guint timeout_handler
;
1247 gboolean repetitive
;
1249 GDestroyNotify destroy
;
1250 struct sipe_account_data
*sip
;
1256 * Should return FALSE if repetitive action is not needed
1258 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1261 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1262 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1263 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1264 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1265 ret
= sched_action
->repetitive
;
1266 (*sched_action
->destroy
)(sched_action
->payload
);
1267 g_free(sched_action
->name
);
1268 g_free(sched_action
);
1273 * Kills action timer effectively cancelling
1276 * @param name of action
1278 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1282 if (!sip
->timeouts
|| !name
) return;
1284 entry
= sip
->timeouts
;
1286 struct scheduled_action
*sched_action
= entry
->data
;
1287 if(!strcmp(sched_action
->name
, name
)) {
1288 GSList
*to_delete
= entry
;
1289 entry
= entry
->next
;
1290 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1291 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1292 purple_timeout_remove(sched_action
->timeout_handler
);
1293 (*sched_action
->destroy
)(sched_action
->payload
);
1294 g_free(sched_action
->name
);
1295 g_free(sched_action
);
1297 entry
= entry
->next
;
1303 sipe_schedule_action0(const gchar
*name
,
1307 GDestroyNotify destroy
,
1308 struct sipe_account_data
*sip
,
1311 struct scheduled_action
*sched_action
;
1313 /* Make sure each action only exists once */
1314 sipe_cancel_scheduled_action(sip
, name
);
1316 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1317 sched_action
= g_new0(struct scheduled_action
, 1);
1318 sched_action
->repetitive
= FALSE
;
1319 sched_action
->name
= g_strdup(name
);
1320 sched_action
->action
= action
;
1321 sched_action
->destroy
= destroy
? destroy
: g_free
;
1322 sched_action
->sip
= sip
;
1323 sched_action
->payload
= payload
;
1324 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1325 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1326 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1327 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1331 * Do schedule action for execution in the future.
1332 * Non repetitive execution.
1334 * @param name of action (will be copied)
1335 * @param timeout in seconds
1336 * @action callback function
1337 * @payload callback data (can be NULL, otherwise caller must allocate memory)
1340 sipe_schedule_action(const gchar
*name
,
1343 GDestroyNotify destroy
,
1344 struct sipe_account_data
*sip
,
1347 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1351 * Same as sipe_schedule_action() but timeout is in milliseconds.
1354 sipe_schedule_action_msec(const gchar
*name
,
1357 GDestroyNotify destroy
,
1358 struct sipe_account_data
*sip
,
1361 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1365 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1367 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1369 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1371 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1376 static void sipe_subscribe_resource_uri(const char *name
, gpointer value
, gchar
**resources_uri
)
1378 gchar
*tmp
= *resources_uri
;
1379 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1383 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1385 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1386 if (sbuddy
&& !sbuddy
->resubscribed
) { // Only not resubscribed contacts; the first time everybody are included
1387 gchar
*tmp
= *resources_uri
;
1388 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"><context/></resource>\n", tmp
, name
);
1394 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1395 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1396 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1397 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1398 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1401 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1403 gchar
*contact
= get_contact(sip
);
1406 gchar
*require
= "";
1408 gchar
*autoextend
= "";
1409 gchar
*content_type
;
1411 if (sip
->msrtc_event_categories
) {
1412 require
= ", categoryList";
1413 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1414 content_type
= "application/msrtc-adrl-categorylist+xml";
1415 content
= g_strdup_printf(
1416 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1417 "<action name=\"subscribe\" id=\"63792024\">\n"
1418 "<adhocList>\n%s</adhocList>\n"
1419 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1420 "<category name=\"note\"/>\n"
1421 "<category name=\"state\"/>\n"
1424 "</batchSub>", sip
->username
, resources_uri
);
1426 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1427 content_type
= "application/adrl+xml";
1428 content
= g_strdup_printf(
1429 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1430 "<create xmlns=\"\">\n%s</create>\n"
1431 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1433 g_free(resources_uri
);
1435 request
= g_strdup_printf(
1436 "Require: adhoclist%s\r\n"
1437 "Supported: eventlist\r\n"
1438 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1439 "Supported: ms-piggyback-first-notify\r\n"
1440 "%sSupported: ms-benotify\r\n"
1441 "Proxy-Require: ms-benotify\r\n"
1442 "Event: presence\r\n"
1443 "Content-Type: %s\r\n"
1444 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1447 /* subscribe to buddy presence */
1448 //send_sip_request(sip->gc, "SUBSCRIBE", resource_uri, resource_uri, request, content, NULL, process_subscribe_response);
1449 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1456 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
, void *unused
)
1458 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1459 gchar
*resources_uri
= g_strdup("");
1460 if (sip
->msrtc_event_categories
) {
1461 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1463 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1465 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1468 struct presence_batched_routed
{
1473 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1475 struct presence_batched_routed
*data
= payload
;
1476 GSList
*buddies
= data
->buddies
;
1478 g_free(buddies
->data
);
1479 buddies
= buddies
->next
;
1481 g_slist_free(data
->buddies
);
1486 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
1488 struct presence_batched_routed
*data
= payload
;
1489 GSList
*buddies
= data
->buddies
;
1490 gchar
*resources_uri
= g_strdup("");
1492 gchar
*tmp
= resources_uri
;
1493 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
1495 buddies
= buddies
->next
;
1497 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
1498 g_strdup(data
->host
));
1502 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1503 * The user sends a single SUBSCRIBE request to the subscribed contact.
1504 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1508 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
1510 gchar
*to
= strstr((char *)buddy_name
, "sip:") ? g_strdup((char *)buddy_name
) : g_strdup_printf("sip:%s", (char *)buddy_name
);
1511 gchar
*tmp
= get_contact(sip
);
1514 gchar
*autoextend
= "";
1516 if (!sip
->msrtc_event_categories
)
1517 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1519 request
= g_strdup_printf(
1520 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1521 "Supported: ms-piggyback-first-notify\r\n"
1522 "%sSupported: ms-benotify\r\n"
1523 "Proxy-Require: ms-benotify\r\n"
1524 "Event: presence\r\n"
1525 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1526 "Contact: %s\r\n", autoextend
,tmp
);
1528 content
= g_strdup_printf(
1529 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1530 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1531 "<resource uri=\"%s\"/>\n"
1533 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1534 "<category name=\"note\"/>\n"
1535 "<category name=\"state\"/>\n"
1538 "</batchSub>", sip
->username
, to
1543 /* subscribe to buddy presence */
1544 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1551 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1553 if (!purple_status_is_active(status
))
1557 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
1560 g_free(sip
->status
);
1561 sip
->status
= g_strdup(purple_status_get_id(status
));
1562 send_presence_status(sip
);
1568 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
)
1570 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1571 sipe_group_set_user(sip
, name
);
1575 sipe_group_buddy(PurpleConnection
*gc
,
1577 const char *old_group_name
,
1578 const char *new_group_name
)
1580 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1581 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1582 struct sipe_group
* old_group
= NULL
;
1583 struct sipe_group
* new_group
;
1585 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1586 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1588 if(!buddy
) { // buddy not in roaming list
1592 if (old_group_name
) {
1593 old_group
= sipe_group_find_by_name(sip
, g_strdup(old_group_name
));
1595 new_group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1598 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1599 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1603 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1605 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1606 sipe_group_set_user(sip
, who
);
1610 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1612 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1613 struct sipe_buddy
*b
;
1615 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1617 // Prepend sip: if needed
1618 if (strncmp("sip:", buddy
->name
, 4)) {
1619 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
1620 purple_blist_rename_buddy(buddy
, buf
);
1624 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1625 b
= g_new0(struct sipe_buddy
, 1);
1626 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1627 b
->name
= g_strdup(buddy
->name
);
1628 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1629 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1630 sipe_subscribe_presence_single(sip
, b
->name
); //@TODO should go to callback
1632 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1636 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1640 * We are calling g_hash_table_foreach_steal(). That means that no
1641 * key/value deallocation functions are called. Therefore the glib
1642 * hash code does not touch the key (buddy->name) or value (buddy)
1643 * of the to-be-deleted hash node at all. It follows that we
1645 * - MUST free the memory for the key ourselves and
1646 * - ARE allowed to do it in this function
1648 * Conclusion: glib must be broken on the Windows platform if sipe
1649 * crashes with SIGTRAP when closing. You'll have to live
1650 * with the memory leak until this is fixed.
1652 g_free(buddy
->name
);
1654 g_free(buddy
->annotation
);
1655 g_free(buddy
->device_name
);
1656 g_slist_free(buddy
->groups
);
1661 * Unassociates buddy from group first.
1662 * Then see if no groups left, removes buddy completely.
1663 * Otherwise updates buddy groups on server.
1665 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1667 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1668 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1669 struct sipe_group
*g
= NULL
;
1671 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1676 g
= sipe_group_find_by_name(sip
, group
->name
);
1680 b
->groups
= g_slist_remove(b
->groups
, g
);
1681 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
1684 if (g_slist_length(b
->groups
) < 1) {
1685 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
1686 sipe_cancel_scheduled_action(sip
, action_name
);
1687 g_free(action_name
);
1689 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1692 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1693 send_soap_request(sip
, body
);
1699 //updates groups on server
1700 sipe_group_set_user(sip
, b
->name
);
1706 sipe_rename_group(PurpleConnection
*gc
,
1707 const char *old_name
,
1709 GList
*moved_buddies
)
1711 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1712 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1714 sipe_group_rename(sip
, s_group
, group
->name
);
1716 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1721 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1723 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1724 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1727 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1728 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1729 send_soap_request(sip
, body
);
1732 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1733 g_free(s_group
->name
);
1736 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1740 static GList
*sipe_status_types(PurpleAccount
*acc
)
1742 PurpleStatusType
*type
;
1743 GList
*types
= NULL
;
1746 type
= purple_status_type_new_with_attrs(
1747 PURPLE_STATUS_AVAILABLE
, NULL
, _("Online"), TRUE
, TRUE
, FALSE
,
1748 // Translators: noun
1749 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1751 types
= g_list_append(types
, type
);
1754 type
= purple_status_type_new_with_attrs(
1755 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_BUSY
, _("Busy"), TRUE
, TRUE
, FALSE
,
1756 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1758 types
= g_list_append(types
, type
);
1760 // Do Not Disturb (not user settable)
1761 type
= purple_status_type_new_with_attrs(
1762 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_DND
, _("Do Not Disturb"), TRUE
, FALSE
, FALSE
,
1763 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1765 types
= g_list_append(types
, type
);
1768 type
= purple_status_type_new_with_attrs(
1769 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_BRB
, _("Be Right Back"), TRUE
, TRUE
, FALSE
,
1770 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1772 types
= g_list_append(types
, type
);
1775 type
= purple_status_type_new_with_attrs(
1776 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1777 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1779 types
= g_list_append(types
, type
);
1782 type
= purple_status_type_new_with_attrs(
1783 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_ONPHONE
, _("On The Phone"), TRUE
, TRUE
, FALSE
,
1784 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1786 types
= g_list_append(types
, type
);
1789 type
= purple_status_type_new_with_attrs(
1790 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_LUNCH
, _("Out To Lunch"), TRUE
, TRUE
, FALSE
,
1791 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1793 types
= g_list_append(types
, type
);
1796 type
= purple_status_type_new_full(
1797 PURPLE_STATUS_INVISIBLE
, NULL
, _("Appear Offline"), TRUE
, TRUE
, FALSE
);
1798 types
= g_list_append(types
, type
);
1801 type
= purple_status_type_new_full(
1802 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1803 types
= g_list_append(types
, type
);
1809 * A callback for g_hash_table_foreach
1811 static void sipe_buddy_subscribe_cb(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1813 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
1814 int time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1815 int timeout
= (time_range
* rand()) / RAND_MAX
; /* random period within the range */
1816 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, NULL
, sip
, buddy
->name
);
1820 * Removes entries from purple buddy list
1821 * that does not correspond ones in the roaming contact list.
1823 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
1824 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1825 GSList
*entry
= buddies
;
1826 struct sipe_buddy
*buddy
;
1830 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
1831 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
1834 g
= purple_buddy_get_group(b
);
1835 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
1837 gboolean in_sipe_groups
= FALSE
;
1838 GSList
*entry2
= buddy
->groups
;
1840 struct sipe_group
*group
= entry2
->data
;
1841 if (!strcmp(group
->name
, g
->name
)) {
1842 in_sipe_groups
= TRUE
;
1845 entry2
= entry2
->next
;
1847 if(!in_sipe_groups
) {
1848 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
1849 purple_blist_remove_buddy(b
);
1852 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
1853 purple_blist_remove_buddy(b
);
1855 entry
= entry
->next
;
1859 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1861 int len
= msg
->bodylen
;
1863 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1866 const gchar
*contacts_delta
;
1867 xmlnode
*group_node
;
1868 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1872 /* Convert the contact from XML to Purple Buddies */
1873 isc
= xmlnode_from_str(msg
->body
, len
);
1878 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
1879 if (contacts_delta
) {
1880 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1883 if (!strcmp(isc
->name
, "contactList")) {
1886 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1887 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1888 const char *name
= xmlnode_get_attrib(group_node
, "name");
1890 if (!strncmp(name
, "~", 1)) {
1891 name
= _("Other Contacts");
1893 group
->name
= g_strdup(name
);
1894 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1896 sipe_group_add(sip
, group
);
1899 // Make sure we have at least one group
1900 if (g_slist_length(sip
->groups
) == 0) {
1901 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1902 PurpleGroup
*purple_group
;
1903 group
->name
= g_strdup(_("Other Contacts"));
1905 purple_group
= purple_group_new(group
->name
);
1906 purple_blist_add_group(purple_group
, NULL
);
1907 sip
->groups
= g_slist_append(sip
->groups
, group
);
1910 /* Parse contacts */
1911 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1912 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
1913 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
1914 gchar
* groups
= g_strdup(xmlnode_get_attrib(item
, "groups"));
1915 gchar
* buddy_name
= g_strdup_printf("sip:%s", uri
);
1916 gchar
**item_groups
;
1917 struct sipe_group
*group
= NULL
;
1918 struct sipe_buddy
*buddy
= NULL
;
1921 // assign to group Other Contacts if nothing else received
1922 if(!groups
|| !strcmp("", groups
) ) {
1923 group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
1924 groups
= group
? g_strdup_printf("%d", group
->id
) : "1";
1927 item_groups
= g_strsplit(groups
, " ", 0);
1929 while (item_groups
[i
]) {
1930 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
1932 // If couldn't find the right group for this contact, just put them in the first group we have
1933 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1934 group
= sip
->groups
->data
;
1937 if (group
!= NULL
) {
1938 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1940 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1941 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1944 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
1945 if (name
!= NULL
&& strlen(name
) != 0) {
1946 purple_blist_alias_buddy(b
, name
);
1951 buddy
= g_new0(struct sipe_buddy
, 1);
1952 buddy
->name
= g_strdup(b
->name
);
1953 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
1956 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1958 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
1960 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
1965 } // while, contact groups
1966 g_strfreev(item_groups
);
1974 sipe_cleanup_local_blist(sip
);
1978 //subscribe to buddies
1979 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
1980 if(sip
->batched_support
){
1981 sipe_subscribe_presence_batched(sip
, NULL
);
1984 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
1986 sip
->subscribed_buddies
= TRUE
;
1993 * Subscribe roaming contacts
1995 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1997 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1998 gchar
*tmp
= get_contact(sip
);
1999 gchar
*hdr
= g_strdup_printf(
2000 "Event: vnd-microsoft-roaming-contacts\r\n"
2001 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2002 "Supported: com.microsoft.autoextend\r\n"
2003 "Supported: ms-benotify\r\n"
2004 "Proxy-Require: ms-benotify\r\n"
2005 "Supported: ms-piggyback-first-notify\r\n"
2006 "Contact: %s\r\n", tmp
);
2009 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2014 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
, void *unused
)
2016 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2017 gchar
*tmp
= get_contact(sip
);
2018 gchar
*hdr
= g_strdup_printf(
2019 "Event: presence.wpending\r\n"
2020 "Accept: text/xml+msrtc.wpending\r\n"
2021 "Supported: com.microsoft.autoextend\r\n"
2022 "Supported: ms-benotify\r\n"
2023 "Proxy-Require: ms-benotify\r\n"
2024 "Supported: ms-piggyback-first-notify\r\n"
2025 "Contact: %s\r\n", tmp
);
2028 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2034 * Fires on deregistration event initiated by server.
2035 * [MS-SIPREGE] SIP extension.
2040 // Content-Type: text/registration-event
2041 // subscription-state: terminated;expires=0
2042 // ms-diagnostics-public: 4141;reason="User disabled"
2044 // deregistered;event=rejected
2046 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2048 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2049 gchar
*event
= NULL
;
2050 gchar
*reason
= NULL
;
2051 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2053 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2054 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2056 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2057 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2058 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2059 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2061 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2065 if (warning
!= NULL
) {
2066 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2067 } else { // for LCS2005
2069 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2070 error_id
= 4140; // [MS-SIPREGE]
2071 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2072 reason
= g_strdup(_("You have been signed off because you've signed in at another location"));
2073 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2075 reason
= g_strdup(_("User disabled")); // [MS-OCER]
2076 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2078 reason
= g_strdup(_("User moved")); // [MS-OCER]
2082 warning
= g_strdup_printf(_("Unregistered by Server: %s."), reason
? reason
: _("no reason given"));
2085 sip
->gc
->wants_to_die
= TRUE
;
2086 purple_connection_error(sip
->gc
, warning
);
2091 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2093 const gchar
*contacts_delta
;
2096 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2102 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2105 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2112 free_container(struct sipe_container
*container
)
2116 if (!container
) return;
2118 entry
= container
->members
;
2120 g_free(entry
->data
);
2121 entry
= g_slist_remove(entry
, entry
->data
);
2127 * Finds locally stored MS-PRES container member
2129 static struct sipe_container_member
*
2130 sipe_find_container_member(struct sipe_container
*container
,
2134 struct sipe_container_member
*member
;
2137 if (container
== NULL
|| type
== NULL
) {
2141 entry
= container
->members
;
2143 member
= entry
->data
;
2144 if (!g_strcasecmp(member
->type
, type
)
2145 && ((!member
->value
&& !value
)
2146 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2150 entry
= entry
->next
;
2156 * Finds locally stored MS-PRES container by id
2158 static struct sipe_container
*
2159 sipe_find_container(struct sipe_account_data
*sip
,
2162 struct sipe_container
*container
;
2169 entry
= sip
->containers
;
2171 container
= entry
->data
;
2172 if (id
== container
->id
) {
2175 entry
= entry
->next
;
2189 sipe_find_access_level(struct sipe_account_data
*sip
,
2193 guint containers
[] = {32000, 400, 300, 200, 100};
2196 for (i
= 0; i
< 5; i
++) {
2197 struct sipe_container_member
*member
;
2198 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2199 if (!container
) continue;
2201 member
= sipe_find_container_member(container
, type
, value
);
2203 return containers
[i
];
2211 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2213 guint container_version
,
2214 const gchar
* action
,
2218 gchar
*self
= g_strdup_printf("sip:%s", sip
->username
);
2219 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2222 gchar
*body
= g_strdup_printf(
2223 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2224 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2225 "</setContainerMembers>",
2233 contact
= get_contact(sip
);
2234 hdr
= g_strdup_printf("Contact: %s\r\n"
2235 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2238 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2247 * When we receive some self (BE) NOTIFY with a new subscriber
2248 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2251 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2258 char *display_name
= NULL
;
2259 PurpleBuddy
*pbuddy
;
2264 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2266 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2269 contact
= get_contact(sip
);
2270 to
= g_strdup_printf("sip:%s", sip
->username
);
2273 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2274 guint id
= atoi(xmlnode_get_attrib(node
, "id"));
2275 struct sipe_container
*container
= sipe_find_container(sip
, id
);
2278 sip
->containers
= g_slist_remove(sip
->containers
, container
);
2279 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
2280 free_container(container
);
2282 container
= g_new0(struct sipe_container
, 1);
2284 container
->version
= atoi(xmlnode_get_attrib(node
, "version"));
2285 sip
->containers
= g_slist_append(sip
->containers
, container
);
2286 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
2288 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
2289 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
2290 member
->type
= xmlnode_get_attrib(node2
, "type");
2291 member
->value
= xmlnode_get_attrib(node2
, "value");
2292 container
->members
= g_slist_append(container
->members
, member
);
2293 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
2294 member
->type
, member
->value
? member
->value
: "");
2298 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
2299 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
2300 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
2301 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
2302 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
2303 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
2304 /* initial set-up to let counterparties see your status */
2305 if (sameEnterpriseAL
< 0) {
2306 struct sipe_container
*container
= sipe_find_container(sip
, 200);
2307 guint version
= container
? container
->version
: 0;
2308 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
2310 if (federatedAL
< 0) {
2311 struct sipe_container
*container
= sipe_find_container(sip
, 100);
2312 guint version
= container
? container
->version
: 0;
2313 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
2315 sip
->access_level_set
= TRUE
;
2319 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2321 const char *acknowledged
;
2325 user
= xmlnode_get_attrib(node
, "user");
2326 if (!user
) continue;
2327 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
2328 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
2329 uri_user
= g_strdup_printf("sip:%s", user
);
2330 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri_user
);
2332 alias
= purple_buddy_get_local_alias(pbuddy
);
2333 uri_alias
= g_strdup_printf("sip:%s", alias
);
2334 if (display_name
&& !g_ascii_strcasecmp(uri_user
, uri_alias
)) { // 'bad' alias
2335 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri_user
, display_name
);
2336 purple_blist_alias_buddy(pbuddy
, display_name
);
2341 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
2342 if(!g_ascii_strcasecmp(acknowledged
,"false")){
2343 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
2344 if (!purple_find_buddy(sip
->account
, uri_user
)) {
2345 purple_account_request_add(sip
->account
, uri_user
, _("you"), display_name
, NULL
);
2348 hdr
= g_strdup_printf(
2350 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2352 body
= g_strdup_printf(
2353 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2354 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2355 "</setSubscribers>", user
);
2357 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
2361 g_free(display_name
);
2370 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2372 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2373 gchar
*tmp
= get_contact(sip
);
2374 gchar
*hdr
= g_strdup_printf(
2375 "Event: vnd-microsoft-roaming-ACL\r\n"
2376 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
2377 "Supported: com.microsoft.autoextend\r\n"
2378 "Supported: ms-benotify\r\n"
2379 "Proxy-Require: ms-benotify\r\n"
2380 "Supported: ms-piggyback-first-notify\r\n"
2381 "Contact: %s\r\n", tmp
);
2384 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2390 * To request for presence information about the user, access level settings that have already been configured by the user
2391 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
2392 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
2395 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2397 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2398 gchar
*tmp
= get_contact(sip
);
2399 gchar
*hdr
= g_strdup_printf(
2400 "Event: vnd-microsoft-roaming-self\r\n"
2401 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
2402 "Supported: ms-benotify\r\n"
2403 "Proxy-Require: ms-benotify\r\n"
2404 "Supported: ms-piggyback-first-notify\r\n"
2406 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
2408 gchar
*body
=g_strdup(
2409 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
2410 "<roaming type=\"categories\"/>"
2411 "<roaming type=\"containers\"/>"
2412 "<roaming type=\"subscribers\"/></roamingList>");
2415 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2424 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2426 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2427 gchar
*tmp
= get_contact(sip
);
2428 gchar
*hdr
= g_strdup_printf(
2429 "Event: vnd-microsoft-provisioning\r\n"
2430 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
2431 "Supported: com.microsoft.autoextend\r\n"
2432 "Supported: ms-benotify\r\n"
2433 "Proxy-Require: ms-benotify\r\n"
2434 "Supported: ms-piggyback-first-notify\r\n"
2436 "Contact: %s\r\n", tmp
);
2439 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
2444 /** Subscription for provisioning information to help with initial
2445 * configuration. This subscription is a one-time query (denoted by the Expires header,
2446 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
2447 * configuration, meeting policies, and policy settings that Communicator must enforce.
2448 * TODO: for what we need this information.
2451 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2453 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2454 gchar
*tmp
= get_contact(sip
);
2455 gchar
*hdr
= g_strdup_printf(
2456 "Event: vnd-microsoft-provisioning-v2\r\n"
2457 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
2458 "Supported: com.microsoft.autoextend\r\n"
2459 "Supported: ms-benotify\r\n"
2460 "Proxy-Require: ms-benotify\r\n"
2461 "Supported: ms-piggyback-first-notify\r\n"
2464 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
2465 gchar
*body
= g_strdup(
2466 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
2467 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
2468 "<provisioningGroup name=\"ucPolicy\"/>"
2469 "</provisioningGroupList>");
2472 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2478 /* IM Session (INVITE and MESSAGE methods) */
2481 get_dialog (struct sip_im_session
*session
,
2484 struct sip_dialog
*dialog
;
2486 if (session
== NULL
|| who
== NULL
) {
2490 entry
= session
->dialogs
;
2492 dialog
= entry
->data
;
2493 if (dialog
->with
&& !strcmp(who
, dialog
->with
)) {
2496 entry
= entry
->next
;
2501 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
2503 get_end_points (struct sipe_account_data
*sip
,
2504 struct sip_im_session
*session
)
2507 gchar
*res
= g_strdup_printf("<sip:%s>", sip
->username
);
2508 struct sip_dialog
*dialog
;
2510 if (session
== NULL
) {
2514 entry
= session
->dialogs
;
2516 dialog
= entry
->data
;
2519 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
2522 if (dialog
->theirepid
) {
2524 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
2528 entry
= entry
->next
;
2533 static struct sip_im_session
*
2534 find_chat_session_by_id (struct sipe_account_data
*sip
,
2537 struct sip_im_session
*session
;
2543 entry
= sip
->im_sessions
;
2545 session
= entry
->data
;
2546 if (id
== session
->chat_id
) {
2549 entry
= entry
->next
;
2554 static struct sip_im_session
*
2555 find_chat_session_by_name (struct sipe_account_data
*sip
,
2556 const char *chat_name
)
2558 struct sip_im_session
*session
;
2560 if (sip
== NULL
|| chat_name
== NULL
) {
2564 entry
= sip
->im_sessions
;
2566 session
= entry
->data
;
2567 if (session
->chat_name
&& !g_strcasecmp(chat_name
, session
->chat_name
)) {
2570 entry
= entry
->next
;
2575 static struct sip_im_session
*
2576 find_chat_session (struct sipe_account_data
*sip
,
2579 struct sip_im_session
*session
;
2581 if (sip
== NULL
|| callid
== NULL
) {
2585 entry
= sip
->im_sessions
;
2587 session
= entry
->data
;
2588 if (session
->callid
&& !g_strcasecmp(callid
, session
->callid
)) {
2591 entry
= entry
->next
;
2596 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
2598 struct sip_im_session
*session
;
2600 if (sip
== NULL
|| who
== NULL
) {
2604 entry
= sip
->im_sessions
;
2606 session
= entry
->data
;
2607 if (session
->with
&& !strcmp(who
, session
->with
)) {
2610 entry
= entry
->next
;
2615 struct sip_im_session
*
2616 create_chat_session (struct sipe_account_data
*sip
)
2618 struct sip_im_session
*session
= g_new0(struct sip_im_session
, 1);
2619 session
->callid
= gencallid();
2620 session
->is_multiparty
= TRUE
;
2621 session
->chat_id
= rand();
2622 session
->unconfirmed_messages
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
2623 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
2627 static struct sip_im_session
* find_or_create_chat_session (struct sipe_account_data
*sip
, const char *callid
)
2629 struct sip_im_session
*session
= find_chat_session(sip
, callid
);
2631 session
= create_chat_session(sip
);
2632 session
->callid
= g_strdup(callid
);
2637 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
2639 struct sip_im_session
*session
= find_im_session(sip
, who
);
2641 session
= g_new0(struct sip_im_session
, 1);
2642 session
->is_multiparty
= FALSE
;
2643 session
->with
= g_strdup(who
);
2644 session
->unconfirmed_messages
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
2645 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
2650 void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2654 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
2656 entry
= session
->dialogs
;
2658 free_dialog(entry
->data
);
2659 entry
= g_slist_remove(entry
, entry
->data
);
2662 entry
= session
->outgoing_message_queue
;
2664 g_free(entry
->data
);
2665 entry
= g_slist_remove(entry
, entry
->data
);
2668 entry
= session
->pending_invite_queue
;
2670 g_free(entry
->data
);
2671 entry
= g_slist_remove(entry
, entry
->data
);
2674 g_hash_table_destroy(session
->unconfirmed_messages
);
2676 g_free(session
->with
);
2677 g_free(session
->chat_name
);
2678 g_free(session
->callid
);
2679 g_free(session
->roster_manager
);
2680 g_free(session
->focus_uri
);
2681 g_free(session
->im_mcu_uri
);
2686 process_options_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2688 gboolean ret
= TRUE
;
2690 if (msg
->response
!= 200) {
2691 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
2695 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
2701 * Asks UA/proxy about its capabilities.
2703 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
2705 gchar
*to
= strstr(who
, "sip:") ? g_strdup(who
) : g_strdup_printf("sip:%s", who
);
2706 gchar
*contact
= get_contact(sip
);
2708 request
= g_strdup_printf(
2709 "Accept: application/sdp\r\n"
2710 "Contact: %s\r\n", contact
);
2714 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
2720 static void sipe_present_message_undelivered_err(gchar
*with
, struct sipe_account_data
*sip
, gchar
*message
)
2722 char *msg
, *msg_tmp
;
2723 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
2724 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2726 msg_tmp
= g_strdup_printf( _("The following message could not be delivered to all recipients, "\
2727 "possibly because one or more persons are offline:\n%s") ,
2729 purple_conv_present_error(with
, sip
->account
, msg_tmp
);
2734 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
);
2737 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2739 gboolean ret
= TRUE
;
2740 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
2741 struct sip_im_session
* session
= find_im_session(sip
, with
);
2742 struct sip_dialog
*dialog
;
2748 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2753 dialog
= get_dialog(session
, with
);
2756 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2761 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2762 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
2764 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2766 if (msg
->response
!= 200) {
2767 purple_debug_info("sipe", "process_message_response: MESSAGE response not 200\n");
2769 sipe_present_message_undelivered_err(with
, sip
, message
);
2770 im_session_destroy(sip
, session
);
2773 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2774 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
2775 key
, g_hash_table_size(session
->unconfirmed_messages
));
2781 if (ret
) sipe_im_process_queue(sip
, session
);
2786 sipe_is_election_finished(struct sipe_account_data
*sip
,
2787 struct sip_im_session
*session
);
2790 sipe_election_result(struct sipe_account_data
*sip
,
2794 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2796 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2797 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2798 struct sip_dialog
*dialog
;
2799 struct sip_im_session
*session
;
2801 session
= find_chat_session(sip
, callid
);
2803 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
2807 if (msg
->response
== 200 && !strncmp(contenttype
, "application/x-ms-mim", 20)) {
2808 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2809 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
2810 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
2812 if (xn_request_rm_response
) {
2813 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
2814 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
2816 dialog
= get_dialog(session
, with
);
2818 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
2822 if (allow
&& !g_strcasecmp(allow
, "true")) {
2823 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
2824 dialog
->election_vote
= 1;
2825 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
2826 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
2827 dialog
->election_vote
= -1;
2830 if (sipe_is_election_finished(sip
, session
)) {
2831 sipe_election_result(sip
, session
);
2834 } else if (xn_set_rm_response
) {
2837 xmlnode_free(xn_action
);
2844 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
)
2853 sipe_parse_html(msg
, &msgformat
, &msgtext
);
2854 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
2856 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2859 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2862 msgr
= g_strdup("");
2865 tmp
= get_contact(sip
);
2866 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
2867 //hdr = g_strdup("Content-Type: text/rtf\r\n");
2868 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
2869 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n", tmp
, msgr
);
2873 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
2880 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
2882 GSList
*entry2
= session
->outgoing_message_queue
;
2884 char *queued_msg
= entry2
->data
;
2885 struct sip_dialog
*dialog
;
2886 GSList
*entry
= session
->dialogs
;
2888 if (session
->is_multiparty
) {
2889 gchar
*who
= g_strdup_printf("sip:%s", sip
->username
);
2890 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
2891 PURPLE_MESSAGE_SEND
, queued_msg
, time(NULL
));
2898 dialog
= entry
->data
;
2899 entry
= entry
->next
;
2900 if (dialog
->outgoing_invite
) continue; //do not send messages as INVITE is not responded.
2902 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
2903 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
2904 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
2905 key
, g_hash_table_size(session
->unconfirmed_messages
));
2907 sipe_send_message(sip
, dialog
, queued_msg
);
2910 entry2
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2916 sipe_refer_notify(struct sipe_account_data
*sip
,
2917 struct sip_im_session
*session
,
2924 struct sip_dialog
*dialog
= get_dialog(session
, who
);
2926 hdr
= g_strdup_printf(
2928 "Subscription-State: %s\r\n"
2929 "Content-Type: message/sipfrag\r\n",
2930 status
>= 200 ? "terminated" : "active");
2932 body
= g_strdup_printf(
2933 "SIP/2.0 %d %s\r\n",
2936 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "NOTIFY",
2937 who
, who
, hdr
, body
, dialog
, NULL
);
2944 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2946 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2947 struct sip_im_session
*session
;
2948 struct sip_dialog
*dialog
;
2952 struct sipmsg
*request_msg
= trans
->msg
;
2954 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2957 session
= find_chat_session(sip
, callid
);
2959 session
= find_im_session(sip
, with
);
2963 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2968 dialog
= get_dialog(session
, with
);
2970 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2975 sipe_parse_dialog(msg
, dialog
, TRUE
);
2977 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2978 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
2980 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2982 if (msg
->response
!= 200) {
2983 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2985 sipe_present_message_undelivered_err(with
, sip
, message
);
2986 im_session_destroy(sip
, session
);
2993 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
2994 dialog
->outgoing_invite
= NULL
;
2995 dialog
->is_established
= TRUE
;
2997 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
2999 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
3000 g_free(referred_by
);
3003 /* add user to chat if it is a multiparty session */
3004 if (session
->is_multiparty
) {
3005 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3007 PURPLE_CBFLAGS_NONE
, TRUE
);
3010 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
3011 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
3012 if (session
->outgoing_message_queue
) {
3013 char *queued_msg
= session
->outgoing_message_queue
->data
;
3014 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
3019 sipe_im_process_queue(sip
, session
);
3021 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3022 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
3023 key
, g_hash_table_size(session
->unconfirmed_messages
));
3032 sipe_invite(struct sipe_account_data
*sip
,
3033 struct sip_im_session
*session
,
3035 const gchar
*msg_body
,
3036 const gchar
*referred_by
,
3037 const gboolean is_triggered
)
3044 char *ms_text_format
= g_strdup("");
3045 gchar
*roster_manager
;
3047 gchar
*referred_by_str
;
3048 struct sip_dialog
*dialog
= get_dialog(session
, who
);
3050 if (dialog
&& dialog
->is_established
) {
3051 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
3056 dialog
= g_new0(struct sip_dialog
, 1);
3057 session
->dialogs
= g_slist_append(session
->dialogs
, dialog
);
3059 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
3060 dialog
->with
= g_strdup(who
);
3063 if (!(dialog
->ourtag
)) {
3064 dialog
->ourtag
= gentag();
3068 if (strstr(who
, "sip:")) {
3071 to
= g_strdup_printf("sip:%s", who
);
3082 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
3083 purple_debug_info("sipe", "sipe_invite: msgformat=%s", msgformat
);
3085 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3089 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
3093 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
3094 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
3099 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
3100 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
3101 purple_debug_info("sipe", "sipe_im_send: added message %s to unconfirmed_messages(count=%d)\n",
3102 key
, g_hash_table_size(session
->unconfirmed_messages
));
3106 contact
= get_contact(sip
);
3107 end_points
= get_end_points(sip
, session
);
3108 self
= g_strdup_printf("sip:%s", sip
->username
);
3109 roster_manager
= g_strdup_printf(
3110 "Roster-Manager: %s\r\n"
3111 "EndPoints: %s\r\n",
3114 referred_by_str
= referred_by
?
3116 "Referred-By: %s\r\n",
3119 hdr
= g_strdup_printf(
3120 "Supported: ms-sender\r\n"
3126 "Content-Type: application/sdp\r\n",
3127 (session
->roster_manager
&& !strcmp(session
->roster_manager
, self
)) ? roster_manager
: "",
3129 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
3130 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
3133 g_free(ms_text_format
);
3136 body
= g_strdup_printf(
3138 "o=- 0 0 IN IP4 %s\r\n"
3142 "m=message %d sip null\r\n"
3143 "a=accept-types:text/plain text/html image/gif "
3144 "multipart/related application/im-iscomposing+xml\r\n",
3145 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
3147 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
3148 to
, to
, hdr
, body
, dialog
, process_invite_response
);
3151 g_free(roster_manager
);
3153 g_free(referred_by_str
);
3160 sipe_refer(struct sipe_account_data
*sip
,
3161 struct sip_im_session
*session
,
3166 struct sip_dialog
*dialog
= get_dialog(session
, session
->roster_manager
);
3168 contact
= get_contact(sip
);
3169 hdr
= g_strdup_printf(
3171 "Refer-to: <%s>\r\n"
3172 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
3173 "Require: com.microsoft.rtc-multiparty\r\n",
3177 dialog
->ourtag
? ";tag=" : "",
3178 dialog
->ourtag
? dialog
->ourtag
: "",
3181 send_sip_request(sip
->gc
, "REFER",
3182 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
3189 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
3190 struct sip_im_session
*session
,
3196 struct sip_dialog
*dialog
= get_dialog(session
, who
);
3198 hdr
= "Content-Type: application/x-ms-mim\r\n";
3200 body
= g_strdup_printf(
3201 "<?xml version=\"1.0\"?>\r\n"
3202 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3203 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
3204 sip
->username
, bid
);
3206 send_sip_request(sip
->gc
, "INFO",
3207 who
, who
, hdr
, body
, dialog
, process_info_response
);
3213 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
3214 struct sip_im_session
*session
,
3219 struct sip_dialog
*dialog
= get_dialog(session
, who
);
3221 hdr
= "Content-Type: application/x-ms-mim\r\n";
3223 body
= g_strdup_printf(
3224 "<?xml version=\"1.0\"?>\r\n"
3225 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3226 "<SetRM uri=\"sip:%s\"/></action>\r\n",
3229 send_sip_request(sip
->gc
, "INFO",
3230 who
, who
, hdr
, body
, dialog
, process_info_response
);
3236 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
3239 struct sip_dialog
*dialog
;
3241 entry
= session
->dialogs
;
3243 dialog
= entry
->data
;
3244 /* @TODO slow down BYE message sending rate */
3245 /* @see single subscription code */
3246 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
3247 entry
= entry
->next
;
3250 im_session_destroy(sip
, session
);
3255 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
3257 struct sipe_account_data
*sip
= gc
->proto_data
;
3259 purple_debug_info("sipe", "conversation with %s closed\n", who
);
3260 im_session_close(sip
, find_im_session(sip
, who
));
3264 sipe_chat_leave (PurpleConnection
*gc
, int id
)
3266 struct sipe_account_data
*sip
= gc
->proto_data
;
3267 struct sip_im_session
* session
= find_chat_session_by_id(sip
, id
);
3268 if (session
->focus_uri
) {
3269 conf_session_close(sip
, session
);
3271 im_session_close(sip
, session
);
3275 im_session_close_all (struct sipe_account_data
*sip
)
3277 GSList
*entry
= sip
->im_sessions
;
3279 im_session_close (sip
, entry
->data
);
3280 entry
= sip
->im_sessions
;
3284 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
3286 struct sipe_account_data
*sip
= gc
->proto_data
;
3287 struct sip_im_session
*session
;
3288 struct sip_dialog
*dialog
;
3290 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
3292 session
= find_or_create_im_session(sip
, who
);
3293 dialog
= get_dialog(session
, who
);
3295 // Queue the message
3296 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
3298 if (dialog
&& dialog
->callid
) {
3299 sipe_im_process_queue(sip
, session
);
3300 } else if (!dialog
|| !dialog
->outgoing_invite
) {
3301 // Need to send the INVITE to get the outgoing dialog setup
3302 sipe_invite(sip
, session
, who
, what
, NULL
, FALSE
);
3308 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
, PurpleMessageFlags flags
)
3310 struct sipe_account_data
*sip
= gc
->proto_data
;
3311 struct sip_im_session
*session
;
3313 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
3315 session
= find_chat_session_by_id(sip
, id
);
3317 // Queue the message
3319 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
3321 sipe_im_process_queue(sip
, session
);
3327 /* End IM Session (INVITE and MESSAGE methods) */
3329 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3331 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
3332 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3333 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3335 struct sip_im_session
*session
= find_chat_session(sip
, callid
);
3337 session
= find_im_session(sip
, from
);
3344 if (!strncmp(contenttype
, "application/x-ms-mim", 20)) {
3345 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3346 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
3347 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
3349 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
3351 if (xn_request_rm
) {
3352 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
3353 int bid
= atoi(xmlnode_get_attrib(xn_request_rm
, "bid"));
3354 gchar
*body
= g_strdup_printf(
3355 "<?xml version=\"1.0\"?>\r\n"
3356 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3357 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
3359 session
->bid
< bid
? "true" : "false");
3360 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3362 } else if (xn_set_rm
) {
3364 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
3365 g_free(session
->roster_manager
);
3366 session
->roster_manager
= g_strdup(rm
);
3368 body
= g_strdup_printf(
3369 "<?xml version=\"1.0\"?>\r\n"
3370 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3371 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
3373 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3376 xmlnode_free(xn_action
);
3379 /* looks like purple lacks typing notification for chat */
3380 if (!session
->is_multiparty
&& !session
->focus_uri
) {
3381 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
3384 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3389 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3391 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3392 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3393 struct sip_im_session
*session
;
3394 struct sip_dialog
*dialog
;
3396 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3398 session
= find_chat_session(sip
, callid
);
3400 session
= find_im_session(sip
, from
);
3407 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
3408 g_free(session
->roster_manager
);
3409 session
->roster_manager
= NULL
;
3412 if (!session
->is_multiparty
) {
3413 // TODO Let the user know the other user left the conversation?
3414 im_session_destroy(sip
, session
);
3416 dialog
= get_dialog(session
, from
);
3417 session
->dialogs
= g_slist_remove(session
->dialogs
, dialog
);
3418 free_dialog(dialog
);
3420 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
3422 if (!session
->dialogs
) {
3423 im_session_destroy(sip
, session
);
3430 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3432 gchar
*self
= g_strdup_printf("sip:%s", sip
->username
);
3433 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3434 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3435 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
3436 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
3437 struct sip_im_session
*session
;
3438 struct sip_dialog
*dialog
;
3440 session
= find_chat_session(sip
, callid
);
3441 dialog
= get_dialog(session
, from
);
3443 if (!session
|| !dialog
|| !session
->roster_manager
|| strcmp(session
->roster_manager
, self
)) {
3444 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
3446 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
3448 sipe_invite(sip
, session
, refer_to
, NULL
, referred_by
, FALSE
);
3454 g_free(referred_by
);
3458 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
3460 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
3461 struct sip_im_session
*session
;
3462 struct sip_dialog
*dialog
;
3464 if (state
== PURPLE_NOT_TYPING
)
3467 session
= find_im_session(sip
, who
);
3468 dialog
= get_dialog(session
, who
);
3470 if (session
&& dialog
) {
3471 send_sip_request(gc
, "INFO", who
, who
,
3472 "Content-Type: application/xml\r\n",
3473 SIPE_SEND_TYPING
, dialog
, NULL
);
3475 return SIPE_TYPING_SEND_TIMEOUT
;
3478 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
3480 GSList
*tmp
= sip
->transactions
;
3481 time_t currtime
= time(NULL
);
3483 struct transaction
*trans
= tmp
->data
;
3485 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
3486 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
3489 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
3491 sendout_sipmsg(sip
, trans
->msg
);
3498 static void do_reauthenticate_cb(struct sipe_account_data
*sip
, void *unused
)
3500 /* register again when security token expires */
3501 /* we have to start a new authentication as the security token
3502 * is almost expired by sending a not signed REGISTER message */
3503 purple_debug_info("sipe", "do a full reauthentication\n");
3504 sipe_auth_free(&sip
->registrar
);
3505 sipe_auth_free(&sip
->proxy
);
3506 sip
->registerstatus
= 0;
3508 sip
->reauthenticate_set
= FALSE
;
3511 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3515 gboolean found
= FALSE
;
3517 from
= parse_from(sipmsg_find_header(msg
, "From"));
3521 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
3523 contenttype
= sipmsg_find_header(msg
, "Content-Type");
3524 if (!strncmp(contenttype
, "text/plain", 10)
3525 || !strncmp(contenttype
, "text/html", 9)
3526 || !strncmp(contenttype
, "multipart/related", 21))
3528 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3529 gchar
*html
= get_html_message(contenttype
, msg
->body
);
3531 struct sip_im_session
*session
= find_chat_session(sip
, callid
);
3533 session
= find_im_session(sip
, from
);
3536 if (session
&& session
->focus_uri
) { /* a conference */
3537 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
3538 gchar
*sender
= parse_from(tmp
);
3540 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
3541 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3543 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
3544 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
3545 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3547 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3550 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3553 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
3554 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3559 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
3563 state
= xmlnode_get_child(isc
, "state");
3566 purple_debug_info("sipe", "process_incoming_message: no state found\n");
3571 statedata
= xmlnode_get_data(state
);
3573 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
3574 else serv_got_typing_stopped(sip
->gc
, from
);
3579 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3583 purple_debug_info("sipe", "got unknown mime-type");
3584 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
3589 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3591 gchar
*ms_text_format
;
3593 gchar
*newTag
= gentag();
3596 gboolean is_multiparty
= FALSE
;
3597 gboolean is_triggered
= FALSE
;
3598 gboolean was_multiparty
= TRUE
;
3599 gboolean just_joined
= FALSE
;
3600 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3601 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
3602 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3603 gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
3604 gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
3605 gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
3606 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
3607 GSList
*end_points
= NULL
;
3608 struct sip_im_session
*session
;
3609 struct sip_dialog
*dialog
;
3611 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? msg
->body
: "");
3613 /* Invitation to join conference */
3614 if (!strncmp(content_type
, "application/ms-conf-invite+xml", 30)) {
3615 process_incoming_invite_conf(sip
, msg
);
3619 /* Only accept text invitations */
3620 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
3621 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3625 // TODO There *must* be a better way to clean up the To header to add a tag...
3626 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
3627 oldHeader
= sipmsg_find_header(msg
, "To");
3628 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
3629 sipmsg_remove_header_now(msg
, "To");
3630 sipmsg_add_header_now(msg
, "To", newHeader
);
3633 if (end_points_hdr
) {
3634 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
3636 if (g_slist_length(end_points
) > 2) {
3637 is_multiparty
= TRUE
;
3640 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
3641 is_triggered
= TRUE
;
3642 is_multiparty
= TRUE
;
3645 session
= find_chat_session(sip
, callid
);
3646 /* Convert to multiparty */
3647 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
3648 g_free(session
->with
);
3649 session
->with
= NULL
;
3650 was_multiparty
= FALSE
;
3651 session
->is_multiparty
= TRUE
;
3652 session
->chat_id
= rand();
3655 if (!session
&& is_multiparty
) {
3656 session
= find_or_create_chat_session(sip
, callid
);
3660 session
= find_or_create_im_session(sip
, from
);
3663 if (!session
->callid
) {
3664 session
->callid
= g_strdup(callid
);
3667 session
->is_multiparty
= is_multiparty
;
3668 if (roster_manager
) {
3669 session
->roster_manager
= g_strdup(roster_manager
);
3672 if (is_multiparty
&& end_points
) {
3673 GSList
*entry
= end_points
;
3675 struct sipendpoint
*end_point
= entry
->data
;
3676 entry
= entry
->next
;
3678 if (!g_strcasecmp(from
, end_point
->contact
) ||
3679 !g_strcasecmp(to
, end_point
->contact
))
3682 dialog
= get_dialog(session
, end_point
->contact
);
3684 g_free(dialog
->theirepid
);
3685 dialog
->theirepid
= end_point
->epid
;
3686 end_point
->epid
= NULL
;
3688 dialog
= g_new0(struct sip_dialog
, 1);
3689 session
->dialogs
= g_slist_append(session
->dialogs
, dialog
);
3691 dialog
->callid
= g_strdup(session
->callid
);
3692 dialog
->with
= end_point
->contact
;
3693 end_point
->contact
= NULL
;
3694 dialog
->theirepid
= end_point
->epid
;
3695 end_point
->epid
= NULL
;
3699 /* send triggered INVITE */
3700 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, TRUE
);
3706 GSList
*entry
= end_points
;
3708 struct sipendpoint
*end_point
= entry
->data
;
3709 entry
= entry
->next
;
3710 g_free(end_point
->contact
);
3711 g_free(end_point
->epid
);
3714 g_slist_free(end_points
);
3718 dialog
= get_dialog(session
, from
);
3720 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
3722 dialog
= g_new0(struct sip_dialog
, 1);
3723 session
->dialogs
= g_slist_append(session
->dialogs
, dialog
);
3725 dialog
->callid
= g_strdup(session
->callid
);
3726 dialog
->with
= g_strdup(from
);
3727 sipe_parse_dialog(msg
, dialog
, FALSE
);
3729 if (!dialog
->ourtag
) {
3730 dialog
->ourtag
= newTag
;
3737 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
3741 if (is_multiparty
&& !session
->conv
) {
3742 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++sip
->chat_seq
);
3743 gchar
*self
= g_strdup_printf("sip:%s", sip
->username
);
3744 /* create prpl chat */
3745 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_name
);
3746 session
->chat_name
= g_strdup(chat_name
);
3748 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3750 PURPLE_CBFLAGS_NONE
, FALSE
);
3755 if (is_multiparty
&& !was_multiparty
) {
3756 /* add current IM counterparty to chat */
3757 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3758 ((struct sip_dialog
*)session
->dialogs
->data
)->with
, NULL
,
3759 PURPLE_CBFLAGS_NONE
, FALSE
);
3763 /* add inviting party */
3765 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3767 PURPLE_CBFLAGS_NONE
, TRUE
);
3770 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
3771 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
3772 if (ms_text_format
) {
3773 if (!strncmp(ms_text_format
, "text/plain", 10) || !strncmp(ms_text_format
, "text/html", 9)) {
3775 gchar
*html
= get_html_message(ms_text_format
, NULL
);
3777 if (is_multiparty
) {
3778 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
3779 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3781 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3784 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message received
3790 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
3791 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3792 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
3794 body
= g_strdup_printf(
3796 "o=- 0 0 IN IP4 %s\r\n"
3800 "m=message %d sip sip:%s\r\n"
3801 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml\r\n",
3802 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
3803 sip
->realport
, sip
->username
);
3804 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3808 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3812 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
3813 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3814 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
3816 body
= g_strdup_printf(
3818 "o=- 0 0 IN IP4 0.0.0.0\r\n"
3820 "c=IN IP4 0.0.0.0\r\n"
3822 "m=message %d sip sip:%s\r\n"
3823 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml\r\n",
3824 sip
->realport
, sip
->username
);
3825 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3829 static void sipe_connection_cleanup(struct sipe_account_data
*);
3830 static void create_connection(struct sipe_account_data
*, gchar
*, int);
3832 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3835 const gchar
*expires_header
;
3837 GSList
*hdr
= msg
->headers
;
3839 struct siphdrelement
*elem
;
3841 expires_header
= sipmsg_find_header(msg
, "Expires");
3842 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
3843 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
3845 switch (msg
->response
) {
3848 sip
->registerstatus
= 0;
3850 gchar
*contact_hdr
= NULL
;
3856 if (!sip
->reregister_set
) {
3857 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
3858 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
3859 g_free(action_name
);
3860 sip
->reregister_set
= TRUE
;
3863 sip
->registerstatus
= 3;
3866 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3868 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3871 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3874 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3875 fill_auth(sip
, tmp
, &sip
->registrar
);
3877 if (!sip
->reauthenticate_set
) {
3878 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
3879 guint reauth_timeout
;
3880 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
3881 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
3882 reauth_timeout
= sip
->registrar
.expires
- 300;
3884 /* NTLM: we have to reauthenticate as our security token expires
3885 after eight hours (be five minutes early) */
3886 reauth_timeout
= (8 * 3600) - 300;
3888 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
3889 g_free(action_name
);
3890 sip
->reauthenticate_set
= TRUE
;
3893 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
3895 epid
= get_epid(sip
);
3896 uuid
= generateUUIDfromEPID(epid
);
3899 // There can be multiple Contact headers (one per location where the user is logged in) so
3900 // make sure to only get the one for this uuid
3901 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
3902 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
3903 if (valid_contact
) {
3904 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
3905 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
3906 g_free(valid_contact
);
3909 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
3914 g_free(sip
->contact
);
3916 sip
->contact
= g_strdup_printf("<%s>", gruu
);
3919 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
3920 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
);
3922 sip
->msrtc_event_categories
= FALSE
;
3923 sip
->batched_support
= FALSE
;
3928 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
3929 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
3930 sip
->msrtc_event_categories
= TRUE
;
3931 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->msrtc_event_categories
);
3933 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
3934 sip
->batched_support
= TRUE
;
3935 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->batched_support
);
3938 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
3939 gchar
**caps
= g_strsplit(elem
->value
,",",0);
3942 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
3943 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
3948 hdr
= g_slist_next(hdr
);
3951 if (!sip
->subscribed
) { //do it just once, not every re-register
3952 if(!sip
->msrtc_event_categories
){ //Only for LCS2005, on OCS2007 always backs the error 504 Server time-out
3953 //sipe_options_request(sip, sip->sipdomain);
3955 entry
= sip
->allow_events
;
3958 if (tmp
&& !g_ascii_strcasecmp(tmp
, "vnd-microsoft-roaming-contacts")) {
3959 sipe_subscribe_roaming_contacts(sip
, msg
);
3961 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-roaming-ACL")) {
3962 sipe_subscribe_roaming_acl(sip
, msg
);
3964 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-roaming-self")) {
3965 sipe_subscribe_roaming_self(sip
, msg
);
3967 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-provisioning-v2")) {
3968 sipe_subscribe_roaming_provisioning_v2(sip
, msg
);
3969 } else if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-provisioning")) { // LSC2005
3970 sipe_subscribe_roaming_provisioning(sip
, msg
);
3972 if (tmp
&& !g_ascii_strcasecmp(tmp
,"presence.wpending")) {
3973 sipe_subscribe_presence_wpending(sip
, msg
);
3975 entry
= entry
->next
;
3977 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
3978 sip
->subscribed
= TRUE
;
3981 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
3982 "timeout=", ";", NULL
);
3983 if (timeout
!= NULL
) {
3984 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
3985 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
3986 sip
->keepalive_timeout
);
3990 // Should we remove the transaction here?
3991 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
3992 transactions_remove(sip
, tc
);
3997 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
3999 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
4000 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
4004 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
4007 tmp
= g_strsplit(parts
[0], ":", 0);
4008 hostname
= g_strdup(tmp
[0]);
4009 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
4013 tmp
= g_strsplit(parts
[i
], "=", 0);
4015 if (g_strcasecmp("transport", tmp
[0]) == 0) {
4016 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
4017 transport
= SIPE_TRANSPORT_TCP
;
4018 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
4019 transport
= SIPE_TRANSPORT_UDP
;
4028 /* Close old connection */
4029 sipe_connection_cleanup(sip
);
4031 /* Create new connection */
4032 sip
->transport
= transport
;
4033 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
4034 hostname
, port
, TRANSPORT_DESCRIPTOR
);
4035 create_connection(sip
, hostname
, port
);
4041 if (sip
->registerstatus
!= 2) {
4042 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
4043 if (sip
->registrar
.retries
> 3) {
4044 sip
->gc
->wants_to_die
= TRUE
;
4045 purple_connection_error(sip
->gc
, _("Wrong Password"));
4049 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4051 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
4054 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4057 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
4058 fill_auth(sip
, tmp
, &sip
->registrar
);
4059 sip
->registerstatus
= 2;
4060 if (sip
->account
->disconnecting
) {
4061 do_register_exp(sip
, 0);
4069 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
4070 if (warning
!= NULL
) {
4072 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
4074 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
4075 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
4078 warning
= g_strdup(_("You have been rejected by the server"));
4081 sip
->gc
->wants_to_die
= TRUE
;
4082 purple_connection_error(sip
->gc
, warning
);
4089 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
4090 if (warning
!= NULL
) {
4091 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
4092 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
4095 warning
= g_strdup(_("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator"));
4098 sip
->gc
->wants_to_die
= TRUE
;
4099 purple_connection_error(sip
->gc
, warning
);
4106 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
4107 if (warning
!= NULL
) {
4108 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
4109 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
4112 warning
= g_strdup(_("Service unavailable: no reason given"));
4115 sip
->gc
->wants_to_die
= TRUE
;
4116 purple_connection_error(sip
->gc
, warning
);
4125 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4128 xmlnode
*xn_categories
;
4129 xmlnode
*xn_category
;
4131 const char *activity
= NULL
;
4133 xn_categories
= xmlnode_from_str(data
, len
);
4134 uri
= xmlnode_get_attrib(xn_categories
, "uri");
4136 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
4138 xn_category
= xmlnode_get_next_twin(xn_category
) )
4140 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
4142 if (!strcmp(attrVar
, "note"))
4145 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4150 xn_node
= xmlnode_get_child(xn_category
, "note");
4151 if (!xn_node
) continue;
4152 xn_node
= xmlnode_get_child(xn_node
, "body");
4153 if (!xn_node
) continue;
4154 note
= xmlnode_get_data(xn_node
);
4155 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s),note(%s)\n",uri
,note
? note
: "");
4156 g_free(sbuddy
->annotation
);
4157 sbuddy
->annotation
= NULL
;
4158 if (note
) sbuddy
->annotation
= g_strdup(note
);
4164 else if(!strcmp(attrVar
, "state"))
4168 xn_node
= xmlnode_get_child(xn_category
, "state");
4169 if (!xn_node
) continue;
4170 xn_node
= xmlnode_get_child(xn_node
, "availability");
4171 if (!xn_node
) continue;
4173 data
= xmlnode_get_data(xn_node
);
4178 activity
= SIPE_STATUS_ID_UNKNOWN
;
4179 else if (avail
< 4500)
4180 activity
= SIPE_STATUS_ID_AVAILABLE
;
4181 else if (avail
< 6000)
4182 activity
= SIPE_STATUS_ID_BRB
;
4183 else if (avail
< 7500)
4184 activity
= SIPE_STATUS_ID_ONPHONE
;
4185 else if (avail
< 9000)
4186 activity
= SIPE_STATUS_ID_BUSY
;
4187 else if (avail
< 12000)
4188 activity
= SIPE_STATUS_ID_DND
;
4189 else if (avail
< 18000)
4190 activity
= SIPE_STATUS_ID_AWAY
;
4192 activity
= SIPE_STATUS_ID_OFFLINE
;
4196 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", activity
);
4197 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
4200 xmlnode_free(xn_categories
);
4203 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
4205 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4206 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
4207 payload
->host
= g_strdup(host
);
4208 payload
->buddies
= server
;
4209 sipe_subscribe_presence_batched_routed(sip
, payload
);
4210 sipe_subscribe_presence_batched_routed_free(payload
);
4213 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4216 xmlnode
*xn_resource
;
4217 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
4222 xn_list
= xmlnode_from_str(data
, len
);
4224 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
4226 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
4228 const char *uri
, *state
;
4229 xmlnode
*xn_instance
;
4231 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
4232 if (!xn_instance
) continue;
4234 uri
= xmlnode_get_attrib(xn_resource
, "uri");
4235 state
= xmlnode_get_attrib(xn_instance
, "state");
4236 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
4238 if (strstr(state
, "resubscribe")) {
4239 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
4240 struct sipe_buddy
*sbuddy
;
4241 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
4242 gchar
*user
= g_strdup(uri
);
4243 host
= g_strdup(poolFqdn
);
4244 server
= g_hash_table_lookup(servers
, host
);
4245 server
= g_slist_append(server
, user
);
4246 g_hash_table_insert(servers
, host
, server
);
4248 sipe_subscribe_presence_single(sip
, (void *) uri
);
4250 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4252 sbuddy
->resubscribed
= TRUE
;
4257 /* Send out any deferred poolFqdn subscriptions */
4258 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
4259 g_hash_table_destroy(servers
);
4261 xmlnode_free(xn_list
);
4264 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4268 gchar
*activity
= NULL
;
4270 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
4271 gboolean isonline
= FALSE
;
4272 xmlnode
*display_name_node
;
4274 pidf
= xmlnode_from_str(data
, len
);
4276 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data
);
4280 uri
= xmlnode_get_attrib(pidf
, "entity");
4282 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
4284 if ((status
= xmlnode_get_child(tuple
, "status"))) {
4285 basicstatus
= xmlnode_get_child(status
, "basic");
4290 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
4295 getbasic
= xmlnode_get_data(basicstatus
);
4297 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
4302 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
4303 if (strstr(getbasic
, "open")) {
4308 display_name_node
= xmlnode_get_child(pidf
, "display-name");
4309 // updating display name if alias was just URI
4310 if (display_name_node
) {
4311 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
4312 GSList
*entry
= buddies
;
4313 PurpleBuddy
*p_buddy
;
4314 char * display_name
= xmlnode_get_data(display_name_node
);
4317 const char *server_alias
;
4320 p_buddy
= entry
->data
;
4322 alias
= (char *)purple_buddy_get_alias(p_buddy
);
4323 alias
= alias
? g_strdup_printf("sip:%s", alias
) : NULL
;
4324 if (!alias
|| !g_ascii_strcasecmp(uri
, alias
)) {
4325 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
4326 purple_blist_alias_buddy(p_buddy
, display_name
);
4330 server_alias
= purple_buddy_get_server_alias(p_buddy
);
4332 ( (server_alias
&& strcmp(display_name
, server_alias
))
4333 || !server_alias
|| strlen(server_alias
) == 0 )
4335 purple_blist_server_alias_buddy(p_buddy
, display_name
);
4338 entry
= entry
->next
;
4340 g_free(display_name
);
4343 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
4344 if ((status
= xmlnode_get_child(tuple
, "status"))) {
4345 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
4346 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
4347 activity
= xmlnode_get_data(basicstatus
);
4348 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
4355 const gchar
* status_id
= NULL
;
4357 if (strstr(activity
, "busy")) {
4358 status_id
= SIPE_STATUS_ID_BUSY
;
4359 } else if (strstr(activity
, "away")) {
4360 status_id
= SIPE_STATUS_ID_AWAY
;
4365 status_id
= SIPE_STATUS_ID_AVAILABLE
;
4368 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
4369 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
4371 purple_prpl_got_user_status(sip
->account
, uri
, SIPE_STATUS_ID_OFFLINE
, NULL
);
4378 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4380 const char *availability
;
4381 const char *activity
;
4382 const char *display_name
= NULL
;
4383 const char *activity_name
= NULL
;
4388 struct sipe_buddy
*sbuddy
;
4390 xmlnode
*xn_presentity
= xmlnode_from_str(data
, len
);
4392 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
4393 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
4394 xmlnode
*xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
4395 xmlnode
*xn_email
= xmlnode_get_child(xn_presentity
, "email");
4396 const char *email
= xn_email
? xmlnode_get_attrib(xn_email
, "email") : NULL
;
4397 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
4398 xmlnode
*xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
):NULL
;
4399 const char *avail
= xn_state
? xmlnode_get_attrib(xn_state
, "avail") : NULL
;
4401 xmlnode
*xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
4402 char *note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
4403 xmlnode
*xn_devices
= xmlnode_get_child(xn_presentity
, "devices");
4404 xmlnode
*xn_device_presence
= xn_devices
? xmlnode_get_child(xn_devices
, "devicePresence") : NULL
;
4405 xmlnode
*xn_device_name
= xn_device_presence
? xmlnode_get_child(xn_device_presence
, "deviceName") : NULL
;
4406 const char *device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
4408 name
= xmlnode_get_attrib(xn_presentity
, "uri");
4409 uri
= g_strdup_printf("sip:%s", name
);
4410 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
4411 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
4413 // updating display name if alias was just URI
4414 if (xn_display_name
) {
4415 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
4416 GSList
*entry
= buddies
;
4417 PurpleBuddy
*p_buddy
;
4418 display_name
= xmlnode_get_attrib(xn_display_name
, "displayName");
4421 const char *email_str
, *server_alias
;
4423 p_buddy
= entry
->data
;
4425 if (!g_ascii_strcasecmp(name
, purple_buddy_get_alias(p_buddy
))) {
4426 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
4427 purple_blist_alias_buddy(p_buddy
, display_name
);
4430 server_alias
= purple_buddy_get_server_alias(p_buddy
);
4432 ( (server_alias
&& strcmp(display_name
, server_alias
))
4433 || !server_alias
|| strlen(server_alias
) == 0 )
4435 purple_blist_server_alias_buddy(p_buddy
, display_name
);
4439 email_str
= purple_blist_node_get_string((PurpleBlistNode
*)p_buddy
, "email");
4440 if (!email_str
|| g_ascii_strcasecmp(email_str
, email
)) {
4441 purple_blist_node_set_string((PurpleBlistNode
*)p_buddy
, "email", email
);
4445 entry
= entry
->next
;
4449 avl
= atoi(availability
);
4450 act
= atoi(activity
);
4452 if(sip
->msrtc_event_categories
){
4453 if (act
== 100 && avl
== 0)
4454 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4455 else if (act
== 100 && avl
== 300)
4456 activity_name
= SIPE_STATUS_ID_AWAY
;
4457 else if (act
== 300 && avl
== 300)
4458 activity_name
= SIPE_STATUS_ID_BRB
;
4459 else if (act
== 400 && avl
== 300)
4460 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4461 else if (act
== 500 && act
== 300)
4462 activity_name
= SIPE_STATUS_ID_ONPHONE
;
4463 else if (act
== 600 && avl
== 300)
4464 activity_name
= SIPE_STATUS_ID_BUSY
;
4465 else if (act
== 0 && avl
== 0){ //MSRTC elements are zero
4466 if(avail
){ //Check for LegacyInterop elements
4469 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4470 else if (avl
== 3500)
4471 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4472 else if (avl
== 15500)
4473 activity_name
= SIPE_STATUS_ID_AWAY
;
4474 else if (avl
== 6500)
4475 activity_name
= SIPE_STATUS_ID_BUSY
;
4476 else if (avl
== 12500)
4477 activity_name
= SIPE_STATUS_ID_BRB
;
4482 if(activity_name
== NULL
){
4484 activity_name
= SIPE_STATUS_ID_AWAY
;
4485 else if (act
<= 150)
4486 activity_name
= SIPE_STATUS_ID_LUNCH
;
4487 else if (act
<= 300)
4488 activity_name
= SIPE_STATUS_ID_BRB
;
4489 else if (act
<= 400)
4490 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4491 else if (act
<= 500)
4492 activity_name
= SIPE_STATUS_ID_ONPHONE
;
4493 else if (act
<= 600)
4494 activity_name
= SIPE_STATUS_ID_BUSY
;
4496 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4499 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4502 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4505 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
4506 sbuddy
->annotation
= NULL
;
4507 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
4509 if (sbuddy
->device_name
) { g_free(sbuddy
->device_name
); }
4510 sbuddy
->device_name
= NULL
;
4511 if (device_name
) { sbuddy
->device_name
= g_strdup(device_name
); }
4514 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
4515 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
4517 xmlnode_free(xn_presentity
);
4521 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4523 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4525 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
4527 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
4528 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
4530 const char *content
= msg
->body
;
4531 unsigned length
= msg
->bodylen
;
4532 PurpleMimeDocument
*mime
= NULL
;
4534 if (strstr(ctype
, "multipart"))
4536 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
4537 const char *content_type
;
4539 mime
= purple_mime_document_parse(doc
);
4540 parts
= purple_mime_document_get_parts(mime
);
4542 content
= purple_mime_part_get_data(parts
->data
);
4543 length
= purple_mime_part_get_length(parts
->data
);
4544 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
4545 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
4547 process_incoming_notify_rlmi_resub(sip
, content
, length
);
4549 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
4551 process_incoming_notify_msrtc(sip
, content
, length
);
4555 process_incoming_notify_rlmi(sip
, content
, length
);
4557 parts
= parts
->next
;
4563 purple_mime_document_free(mime
);
4566 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
4568 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
4570 else if(strstr(ctype
, "application/rlmi+xml"))
4572 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
4575 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
4577 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
4581 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
4585 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
4587 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4588 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
4590 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
4593 strstr(ctype
, "multipart") &&
4594 (strstr(ctype
, "application/rlmi+xml") ||
4595 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
4596 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
4597 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
4598 GList
*parts
= purple_mime_document_get_parts(mime
);
4599 GSList
*buddies
= NULL
;
4600 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4603 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
4604 purple_mime_part_get_length(parts
->data
));
4605 gchar
*uri
= g_strdup(xmlnode_get_attrib(xml
, "uri"));
4607 if (strstr(uri
, "sip:") == NULL
) {
4609 uri
= g_strdup_printf("sip:%s", tmp
);
4612 buddies
= g_slist_append(buddies
, uri
);
4615 parts
= parts
->next
;
4618 if (mime
) purple_mime_document_free(mime
);
4620 payload
->host
= who
;
4621 payload
->buddies
= buddies
;
4622 sipe_schedule_action(action_name
, timeout
,
4623 sipe_subscribe_presence_batched_routed
,
4624 sipe_subscribe_presence_batched_routed_free
,
4626 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
4629 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, NULL
, sip
, who
);
4630 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
4632 g_free(action_name
);
4636 * Dispatcher for all incoming subscription information
4637 * whether it comes from NOTIFY, BENOTIFY requests or
4638 * piggy-backed to subscription's OK responce.
4640 * @param request whether initiated from BE/NOTIFY request or OK-response message.
4641 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
4643 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
4645 gchar
*event
= sipmsg_find_header(msg
, "Event");
4646 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
4649 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event
? event
: "", msg
->body
);
4650 purple_debug_info("sipe", "process_incoming_notify: subscription_state:%s\n\n", subscription_state
);
4654 const gchar
*expires_header
;
4655 expires_header
= sipmsg_find_header(msg
, "Expires");
4656 timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
4657 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n\n", timeout
);
4658 timeout
= (timeout
- 60) > 60 ? (timeout
- 60) : timeout
; // 1 min ahead of expiration
4661 if (!subscription_state
|| strstr(subscription_state
, "active"))
4663 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
4665 sipe_process_presence(sip
, msg
);
4667 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
4669 sipe_process_roaming_contacts(sip
, msg
, NULL
);
4671 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self") )
4673 sipe_process_roaming_self(sip
, msg
);
4675 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
4677 sipe_process_roaming_acl(sip
, msg
);
4679 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
4681 sipe_process_presence_wpending(sip
, msg
);
4683 else if (event
&& !g_ascii_strcasecmp(event
, "conference"))
4685 sipe_process_conference(sip
, msg
);
4689 purple_debug_info("sipe", "Unable to process (BE)NOTIFY. Event is not supported:%s\n", event
? event
: "");
4693 //The server sends a (BE)NOTIFY with the status 'terminated'
4694 if (request
&& subscription_state
&& strstr(subscription_state
, "terminated") ) {
4695 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4696 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from
);
4700 if (timeout
&& event
) {// For LSC 2005 and OCS 2007
4701 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
4702 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
4704 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
4705 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_contacts, NULL, sip, msg);
4706 g_free(action_name);
4708 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
4709 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
4711 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
4712 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_acl, NULL, sip, msg);
4713 g_free(action_name);
4716 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
4717 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
4719 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
4720 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
4721 g_free(action_name
);
4723 else if (!g_ascii_strcasecmp(event
, "presence") &&
4724 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
4726 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
4727 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
4728 if(sip
->batched_support
) {
4729 gchar
*my_self
= g_strdup_printf("sip:%s",sip
->username
);
4730 if(!g_ascii_strcasecmp(who
, my_self
)){
4731 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_batched
, NULL
, sip
, NULL
);
4732 purple_debug_info("sipe", "Resubscription full batched list in %d\n",timeout
);
4733 g_free(who
); /* unused */
4736 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
4741 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, NULL
, sip
, who
);
4742 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
,timeout
);
4744 g_free(action_name
);
4745 /* "who" will be freed by the action we just scheduled */
4749 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
4751 sipe_process_registration_notify(sip
, msg
);
4754 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
4755 if (request
&& !benotify
)
4757 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4764 static gchar* gen_xpidf(struct sipe_account_data *sip)
4766 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4768 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
4769 "<display name=\"sip:%s\"/>\r\n"
4770 "<atom id=\"1234\">\r\n"
4771 "<address uri=\"sip:%s\">\r\n"
4772 "<status status=\"%s\"/>\r\n"
4785 static gchar* gen_pidf(struct sipe_account_data *sip)
4787 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4788 "<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"
4789 "<tuple id=\"0\">\r\n"
4791 "<basic>open</basic>\r\n"
4792 "<ep:activities>\r\n"
4793 " <ep:activity>%s</ep:activity>\r\n"
4797 "<ci:display-name>%s</ci:display-name>\r\n"
4806 static void send_presence_soap(struct sipe_account_data
*sip
, const char * note
)
4808 int availability
= 300; // online
4809 int activity
= 400; // Available
4812 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
)) {
4814 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4816 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4818 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4820 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4822 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4824 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_INVISIBLE
) ||
4825 !strcmp(sip
->status
, SIPE_STATUS_ID_OFFLINE
)) {
4826 availability
= 0; // offline
4829 activity
= 400; // available
4832 name
= g_strdup_printf("sip: sip:%s", sip
->username
);
4833 //@TODO: send user data - state; add hostname in upper case
4834 body
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
4835 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
4841 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
4843 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4844 if (msg
->response
== 200) {
4845 sip
->status_version
= 0;
4846 send_presence_status(sip
);
4852 process_send_presence_category_publish_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
4854 if (msg
->response
== 409) {
4855 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4856 // TODO need to parse the version #'s?
4857 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
4858 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
4862 purple_debug_info("sipe", "process_send_presence_category_publish_response = %s\n", msg
->body
);
4864 tmp
= get_contact(sip
);
4865 hdr
= g_strdup_printf("Contact: %s\r\n"
4866 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4868 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
4878 static void send_presence_category_publish(struct sipe_account_data
*sip
, const char * note
)
4885 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
) ||
4886 !strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4888 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_DND
)) {
4890 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4892 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4894 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4896 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4898 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_UNKNOWN
)) {
4901 // Offline or invisible
4905 uri
= g_strdup_printf("sip:%s", sip
->username
);
4906 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
4907 sip
->status_version
, code
,
4908 sip
->status_version
, code
,
4909 sip
->status_version
, note
? note
: "",
4910 sip
->status_version
, note
? note
: "",
4911 sip
->status_version
, note
? note
: ""
4913 sip
->status_version
++;
4915 tmp
= get_contact(sip
);
4916 hdr
= g_strdup_printf("Contact: %s\r\n"
4917 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4919 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
4927 static void send_presence_status(struct sipe_account_data
*sip
)
4929 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
4931 if (!status
) return;
4933 note
= purple_status_get_attr_string(status
, "message");
4935 if(sip
->msrtc_event_categories
){
4936 send_presence_category_publish(sip
, note
);
4938 send_presence_soap(sip
, note
);
4942 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
4944 gboolean found
= FALSE
;
4945 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
4946 if (msg
->response
== 0) { /* request */
4947 if (!strcmp(msg
->method
, "MESSAGE")) {
4948 process_incoming_message(sip
, msg
);
4950 } else if (!strcmp(msg
->method
, "NOTIFY")) {
4951 purple_debug_info("sipe","send->process_incoming_notify\n");
4952 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
4954 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
4955 purple_debug_info("sipe","send->process_incoming_benotify\n");
4956 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
4958 } else if (!strcmp(msg
->method
, "INVITE")) {
4959 process_incoming_invite(sip
, msg
);
4961 } else if (!strcmp(msg
->method
, "REFER")) {
4962 process_incoming_refer(sip
, msg
);
4964 } else if (!strcmp(msg
->method
, "OPTIONS")) {
4965 process_incoming_options(sip
, msg
);
4967 } else if (!strcmp(msg
->method
, "INFO")) {
4968 process_incoming_info(sip
, msg
);
4970 } else if (!strcmp(msg
->method
, "ACK")) {
4971 // ACK's don't need any response
4973 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
4974 // LCS 2005 sends us these - just respond 200 OK
4976 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4977 } else if (!strcmp(msg
->method
, "BYE")) {
4978 process_incoming_bye(sip
, msg
);
4981 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
4983 } else { /* response */
4984 struct transaction
*trans
= transactions_find(sip
, msg
);
4986 if (msg
->response
== 407) {
4987 gchar
*resend
, *auth
, *ptmp
;
4989 if (sip
->proxy
.retries
> 30) return;
4990 sip
->proxy
.retries
++;
4991 /* do proxy authentication */
4993 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
4995 fill_auth(sip
, ptmp
, &sip
->proxy
);
4996 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
4997 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
4998 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
5000 resend
= sipmsg_to_string(trans
->msg
);
5001 /* resend request */
5002 sendout_pkt(sip
->gc
, resend
);
5005 if (msg
->response
== 100 || msg
->response
== 180) {
5006 /* ignore provisional response */
5007 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
5009 sip
->proxy
.retries
= 0;
5010 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
5011 if (msg
->response
== 401)
5013 sip
->registrar
.retries
++;
5017 sip
->registrar
.retries
= 0;
5019 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
5021 if (msg
->response
== 401) {
5022 gchar
*resend
, *auth
, *ptmp
;
5024 if (sip
->registrar
.retries
> 4) return;
5025 sip
->registrar
.retries
++;
5028 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5030 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
5033 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
5037 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
5039 fill_auth(sip
, ptmp
, &sip
->registrar
);
5040 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
5041 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
5042 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
5044 //sipmsg_remove_header_now(trans->msg, "Authorization");
5045 //sipmsg_add_header(trans->msg, "Authorization", auth);
5047 resend
= sipmsg_to_string(trans
->msg
);
5048 /* resend request */
5049 sendout_pkt(sip
->gc
, resend
);
5054 if (trans
->callback
) {
5055 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
5056 /* call the callback to process response*/
5057 (trans
->callback
)(sip
, msg
, trans
);
5059 /* Not sure if this is needed or what needs to be done
5060 but transactions seem to be removed prematurely so
5061 this only removes them if the response is 200 OK */
5062 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
5063 /*Has a bug and it's unneccesary*/
5064 /*transactions_remove(sip, trans);*/
5070 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
5074 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
5078 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
5086 /* according to the RFC remove CRLF at the beginning */
5087 while (*cur
== '\r' || *cur
== '\n') {
5090 if (cur
!= conn
->inbuf
) {
5091 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
5092 conn
->inbufused
= strlen(conn
->inbuf
);
5095 /* Received a full Header? */
5096 sip
->processing_input
= TRUE
;
5097 while (sip
->processing_input
&&
5098 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
5099 time_t currtime
= time(NULL
);
5102 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
5103 msg
= sipmsg_parse_header(conn
->inbuf
);
5106 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
5107 if (restlen
>= msg
->bodylen
) {
5108 dummy
= g_malloc(msg
->bodylen
+ 1);
5109 memcpy(dummy
, cur
, msg
->bodylen
);
5110 dummy
[msg
->bodylen
] = '\0';
5112 cur
+= msg
->bodylen
;
5113 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
5114 conn
->inbufused
= strlen(conn
->inbuf
);
5116 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
5117 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
5123 purple_debug_info("sipe", "body:\n%s", msg->body);
5126 // Verify the signature before processing it
5127 if (sip
->registrar
.gssapi_context
) {
5128 struct sipmsg_breakdown msgbd
;
5129 gchar
*signature_input_str
;
5132 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
5133 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
5135 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
5137 if (rspauth
!= NULL
) {
5138 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
5139 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
5140 process_input_message(sip
, msg
);
5142 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
5143 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
5144 sip
->gc
->wants_to_die
= TRUE
;
5146 } else if (msg
->response
== 401) {
5147 purple_connection_error(sip
->gc
, _("Wrong Password"));
5148 sip
->gc
->wants_to_die
= TRUE
;
5150 g_free(signature_input_str
);
5153 sipmsg_breakdown_free(&msgbd
);
5155 process_input_message(sip
, msg
);
5162 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
5164 PurpleConnection
*gc
= data
;
5165 struct sipe_account_data
*sip
= gc
->proto_data
;
5170 static char buffer
[65536];
5171 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
5173 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
5174 msg
= sipmsg_parse_msg(buffer
);
5175 if (msg
) process_input_message(sip
, msg
);
5179 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
5181 struct sipe_account_data
*sip
= gc
->proto_data
;
5182 PurpleSslConnection
*gsc
= sip
->gsc
;
5184 purple_debug_error("sipe", "%s",debug
);
5185 purple_connection_error(gc
, msg
);
5187 /* Invalidate this connection. Next send will open a new one */
5189 connection_remove(sip
, gsc
->fd
);
5190 purple_ssl_close(gsc
);
5196 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
5198 PurpleConnection
*gc
= data
;
5199 struct sipe_account_data
*sip
;
5200 struct sip_connection
*conn
;
5202 gboolean firstread
= TRUE
;
5204 /* NOTE: This check *IS* necessary */
5205 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
5206 purple_ssl_close(gsc
);
5210 sip
= gc
->proto_data
;
5211 conn
= connection_find(sip
, gsc
->fd
);
5213 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
5214 gc
->wants_to_die
= TRUE
;
5215 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
5219 /* Read all available data from the SSL connection */
5221 /* Increase input buffer size as needed */
5222 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
5223 conn
->inbuflen
+= SIMPLE_BUF_INC
;
5224 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
5225 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
5228 /* Try to read as much as there is space left in the buffer */
5229 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
5230 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
5232 if (len
< 0 && errno
== EAGAIN
) {
5233 /* Try again later */
5235 } else if (len
< 0) {
5236 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
5238 } else if (firstread
&& (len
== 0)) {
5239 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
5243 conn
->inbufused
+= len
;
5246 /* Equivalence indicates that there is possibly more data to read */
5247 } while (len
== readlen
);
5249 conn
->inbuf
[conn
->inbufused
] = '\0';
5250 process_input(sip
, conn
);
5254 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
5256 PurpleConnection
*gc
= data
;
5257 struct sipe_account_data
*sip
= gc
->proto_data
;
5259 struct sip_connection
*conn
= connection_find(sip
, source
);
5261 purple_debug_error("sipe", "Connection not found!\n");
5265 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
5266 conn
->inbuflen
+= SIMPLE_BUF_INC
;
5267 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
5270 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
5272 if (len
< 0 && errno
== EAGAIN
)
5274 else if (len
<= 0) {
5275 purple_debug_info("sipe", "sipe_input_cb: read error\n");
5276 connection_remove(sip
, source
);
5277 if (sip
->fd
== source
) sip
->fd
= -1;
5281 conn
->inbufused
+= len
;
5282 conn
->inbuf
[conn
->inbufused
] = '\0';
5284 process_input(sip
, conn
);
5287 /* Callback for new connections on incoming TCP port */
5288 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
5290 PurpleConnection
*gc
= data
;
5291 struct sipe_account_data
*sip
= gc
->proto_data
;
5292 struct sip_connection
*conn
;
5294 int newfd
= accept(source
, NULL
, NULL
);
5296 conn
= connection_create(sip
, newfd
);
5298 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
5301 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
5303 PurpleConnection
*gc
= data
;
5304 struct sipe_account_data
*sip
;
5305 struct sip_connection
*conn
;
5307 if (!PURPLE_CONNECTION_IS_VALID(gc
))
5315 purple_connection_error(gc
, _("Could not connect"));
5319 sip
= gc
->proto_data
;
5321 sip
->last_keepalive
= time(NULL
);
5323 conn
= connection_create(sip
, source
);
5327 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
5330 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
5332 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
5333 if (sip
== NULL
) return;
5338 static guint
sipe_ht_hash_nick(const char *nick
)
5340 char *lc
= g_utf8_strdown(nick
, -1);
5341 guint bucket
= g_str_hash(lc
);
5347 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
5349 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
5352 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
5354 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5356 sip
->listen_data
= NULL
;
5358 if (listenfd
== -1) {
5359 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5365 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
5366 sip
->listenfd
= sip
->fd
;
5368 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
5370 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
5374 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
5376 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5378 sip
->query_data
= NULL
;
5380 if (!hosts
|| !hosts
->data
) {
5381 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
5385 hosts
= g_slist_remove(hosts
, hosts
->data
);
5386 g_free(sip
->serveraddr
);
5387 sip
->serveraddr
= hosts
->data
;
5388 hosts
= g_slist_remove(hosts
, hosts
->data
);
5390 hosts
= g_slist_remove(hosts
, hosts
->data
);
5391 g_free(hosts
->data
);
5392 hosts
= g_slist_remove(hosts
, hosts
->data
);
5395 /* create socket for incoming connections */
5396 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
5397 sipe_udp_host_resolved_listen_cb
, sip
);
5398 if (sip
->listen_data
== NULL
) {
5399 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5404 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
5407 PurpleConnection
*gc
= data
;
5408 struct sipe_account_data
*sip
;
5410 /* If the connection is already disconnected, we don't need to do anything else */
5411 if (!PURPLE_CONNECTION_IS_VALID(gc
))
5414 sip
= gc
->proto_data
;
5419 case PURPLE_SSL_CONNECT_FAILED
:
5420 purple_connection_error(gc
, _("Connection Failed"));
5422 case PURPLE_SSL_HANDSHAKE_FAILED
:
5423 purple_connection_error(gc
, _("SSL Handshake Failed"));
5425 case PURPLE_SSL_CERTIFICATE_INVALID
:
5426 purple_connection_error(gc
, _("SSL Certificate Invalid"));
5432 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
5434 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5435 PurpleProxyConnectData
*connect_data
;
5437 sip
->listen_data
= NULL
;
5439 sip
->listenfd
= listenfd
;
5440 if (sip
->listenfd
== -1) {
5441 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5445 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
5446 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
5447 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
5448 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
5449 sipe_newconn_cb
, sip
->gc
);
5450 purple_debug_info("sipe", "connecting to %s port %d\n",
5451 sip
->realhostname
, sip
->realport
);
5452 /* open tcp connection to the server */
5453 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
5454 sip
->realport
, login_cb
, sip
->gc
);
5456 if (connect_data
== NULL
) {
5457 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
5462 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
5464 PurpleAccount
*account
= sip
->account
;
5465 PurpleConnection
*gc
= sip
->gc
;
5467 if (purple_account_get_bool(account
, "useport", FALSE
)) {
5468 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
5469 port
= purple_account_get_int(account
, "port", 0);
5471 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
5474 sip
->realhostname
= hostname
;
5475 sip
->realport
= port
;
5477 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
5480 /* TODO: is there a good default grow size? */
5481 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
5482 sip
->txbuf
= purple_circ_buffer_new(0);
5484 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
5486 if (!purple_ssl_is_supported()) {
5487 gc
->wants_to_die
= TRUE
;
5488 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
5492 purple_debug_info("sipe", "using SSL\n");
5494 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
5495 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
5496 if (sip
->gsc
== NULL
) {
5497 purple_connection_error(gc
, _("Could not create SSL context"));
5500 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
5502 purple_debug_info("sipe", "using UDP\n");
5504 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
5505 if (sip
->query_data
== NULL
) {
5506 purple_connection_error(gc
, _("Could not resolve hostname"));
5510 purple_debug_info("sipe", "using TCP\n");
5511 /* create socket for incoming connections */
5512 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
5513 sipe_tcp_connect_listen_cb
, sip
);
5514 if (sip
->listen_data
== NULL
) {
5515 purple_connection_error(gc
, _("Could not create listen socket"));
5521 /* Service list for autodection */
5522 static const struct sipe_service_data service_autodetect
[] = {
5523 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
5524 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
5525 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
5526 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
5530 /* Service list for SSL/TLS */
5531 static const struct sipe_service_data service_tls
[] = {
5532 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
5533 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
5537 /* Service list for TCP */
5538 static const struct sipe_service_data service_tcp
[] = {
5539 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
5540 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
5544 /* Service list for UDP */
5545 static const struct sipe_service_data service_udp
[] = {
5546 { "sip", "udp", SIPE_TRANSPORT_UDP
},
5550 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
5551 static void resolve_next_service(struct sipe_account_data
*sip
,
5552 const struct sipe_service_data
*start
)
5555 sip
->service_data
= start
;
5557 sip
->service_data
++;
5558 if (sip
->service_data
->service
== NULL
) {
5560 /* Try connecting to the SIP hostname directly */
5561 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
5562 if (sip
->auto_transport
) {
5563 // If SSL is supported, default to using it; OCS servers aren't configured
5564 // by default to accept TCP
5565 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
5566 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
5567 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
5570 hostname
= g_strdup(sip
->sipdomain
);
5571 create_connection(sip
, hostname
, 0);
5576 /* Try to resolve next service */
5577 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
5578 sip
->service_data
->transport
,
5583 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
5585 struct sipe_account_data
*sip
= data
;
5587 sip
->srv_query_data
= NULL
;
5589 /* find the host to connect to */
5591 gchar
*hostname
= g_strdup(resp
->hostname
);
5592 int port
= resp
->port
;
5593 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
5597 sip
->transport
= sip
->service_data
->type
;
5599 create_connection(sip
, hostname
, port
);
5601 resolve_next_service(sip
, NULL
);
5605 static void sipe_login(PurpleAccount
*account
)
5607 PurpleConnection
*gc
;
5608 struct sipe_account_data
*sip
;
5609 gchar
**signinname_login
, **userserver
, **domain_user
;
5610 const char *transport
;
5612 const char *username
= purple_account_get_username(account
);
5613 gc
= purple_account_get_connection(account
);
5615 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
5616 gc
->wants_to_die
= TRUE
;
5617 purple_connection_error(gc
, _("SIP Exchange username contains invalid characters"));
5621 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
5622 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
5623 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
5625 sip
->account
= account
;
5626 sip
->reregister_set
= FALSE
;
5627 sip
->reauthenticate_set
= FALSE
;
5628 sip
->subscribed
= FALSE
;
5629 sip
->subscribed_buddies
= FALSE
;
5631 signinname_login
= g_strsplit(username
, ",", 2);
5633 userserver
= g_strsplit(signinname_login
[0], "@", 2);
5634 purple_connection_set_display_name(gc
, userserver
[0]);
5635 sip
->username
= g_strjoin("@", userserver
[0], userserver
[1], NULL
);
5636 sip
->sipdomain
= g_strdup(userserver
[1]);
5638 if (strpbrk(sip
->username
, " \t\v\r\n") != NULL
) {
5639 gc
->wants_to_die
= TRUE
;
5640 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
5644 domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
5645 sip
->authdomain
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[0]) : NULL
;
5646 sip
->authuser
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[1]) : (signinname_login
? g_strdup(signinname_login
[1]) : NULL
);
5648 sip
->password
= g_strdup(purple_connection_get_password(gc
));
5650 g_strfreev(userserver
);
5651 g_strfreev(domain_user
);
5652 g_strfreev(signinname_login
);
5654 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
5656 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
5658 /* TODO: Set the status correctly. */
5659 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
5661 transport
= purple_account_get_string(account
, "transport", "auto");
5662 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
5663 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
5666 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
5667 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
5668 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
5669 } else if (strcmp(transport
, "auto") == 0) {
5670 sip
->auto_transport
= TRUE
;
5671 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
5672 } else if (strcmp(transport
, "tls") == 0) {
5673 resolve_next_service(sip
, service_tls
);
5674 } else if (strcmp(transport
, "tcp") == 0) {
5675 resolve_next_service(sip
, service_tcp
);
5677 resolve_next_service(sip
, service_udp
);
5681 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
5683 connection_free_all(sip
);
5688 if (sip
->query_data
!= NULL
)
5689 purple_dnsquery_destroy(sip
->query_data
);
5690 sip
->query_data
= NULL
;
5692 if (sip
->srv_query_data
!= NULL
)
5693 purple_srv_cancel(sip
->srv_query_data
);
5694 sip
->srv_query_data
= NULL
;
5696 if (sip
->listen_data
!= NULL
)
5697 purple_network_listen_cancel(sip
->listen_data
);
5698 sip
->listen_data
= NULL
;
5700 if (sip
->gsc
!= NULL
)
5701 purple_ssl_close(sip
->gsc
);
5704 sipe_auth_free(&sip
->registrar
);
5705 sipe_auth_free(&sip
->proxy
);
5708 purple_circ_buffer_destroy(sip
->txbuf
);
5711 g_free(sip
->realhostname
);
5712 sip
->realhostname
= NULL
;
5715 purple_input_remove(sip
->listenpa
);
5717 if (sip
->tx_handler
)
5718 purple_input_remove(sip
->tx_handler
);
5719 sip
->tx_handler
= 0;
5720 if (sip
->resendtimeout
)
5721 purple_timeout_remove(sip
->resendtimeout
);
5722 sip
->resendtimeout
= 0;
5723 if (sip
->timeouts
) {
5724 GSList
*entry
= sip
->timeouts
;
5726 struct scheduled_action
*sched_action
= entry
->data
;
5727 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
5728 purple_timeout_remove(sched_action
->timeout_handler
);
5729 (*sched_action
->destroy
)(sched_action
->payload
);
5730 g_free(sched_action
->name
);
5731 g_free(sched_action
);
5732 entry
= entry
->next
;
5735 g_slist_free(sip
->timeouts
);
5737 if (sip
->allow_events
) {
5738 GSList
*entry
= sip
->allow_events
;
5740 g_free(entry
->data
);
5741 entry
= entry
->next
;
5744 g_slist_free(sip
->allow_events
);
5746 if (sip
->containers
) {
5747 GSList
*entry
= sip
->containers
;
5749 free_container((struct sipe_container
*)entry
->data
);
5750 entry
= entry
->next
;
5753 g_slist_free(sip
->containers
);
5756 g_free(sip
->contact
);
5757 sip
->contact
= NULL
;
5759 g_free(sip
->regcallid
);
5760 sip
->regcallid
= NULL
;
5762 if (sip
->serveraddr
)
5763 g_free(sip
->serveraddr
);
5764 sip
->serveraddr
= NULL
;
5767 sip
->processing_input
= FALSE
;
5771 * A callback for g_hash_table_foreach_remove
5773 static gboolean
sipe_buddy_remove(gpointer key
, gpointer buddy
, gpointer user_data
)
5775 sipe_free_buddy((struct sipe_buddy
*) buddy
);
5777 /* We must return TRUE as the key/value have already been deleted */
5781 static void sipe_close(PurpleConnection
*gc
)
5783 struct sipe_account_data
*sip
= gc
->proto_data
;
5786 /* leave all conversations */
5787 im_session_close_all(sip
);
5790 do_register_exp(sip
, 0);
5792 sipe_connection_cleanup(sip
);
5793 g_free(sip
->sipdomain
);
5794 g_free(sip
->username
);
5795 g_free(sip
->password
);
5796 g_free(sip
->authdomain
);
5797 g_free(sip
->authuser
);
5798 g_free(sip
->status
);
5800 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
5801 g_hash_table_destroy(sip
->buddies
);
5803 g_free(gc
->proto_data
);
5804 gc
->proto_data
= NULL
;
5807 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
5809 PurpleAccount
*acct
= purple_connection_get_account(gc
);
5810 char *id
= g_strdup_printf("sip:%s", (char *)g_list_nth_data(row
, 0));
5811 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
5813 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
5814 purple_conversation_present(conv
);
5818 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
5821 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
5822 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
5825 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,struct transaction
*tc
)
5827 PurpleNotifySearchResults
*results
;
5828 PurpleNotifySearchColumn
*column
;
5829 xmlnode
*searchResults
;
5831 int match_count
= 0;
5832 gboolean more
= FALSE
;
5835 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
5837 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5838 if (!searchResults
) {
5839 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
5843 results
= purple_notify_searchresults_new();
5845 if (results
== NULL
) {
5846 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
5847 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
5849 xmlnode_free(searchResults
);
5853 column
= purple_notify_searchresults_column_new(_("User Name"));
5854 purple_notify_searchresults_column_add(results
, column
);
5856 column
= purple_notify_searchresults_column_new(_("Name"));
5857 purple_notify_searchresults_column_add(results
, column
);
5859 column
= purple_notify_searchresults_column_new(_("Company"));
5860 purple_notify_searchresults_column_add(results
, column
);
5862 column
= purple_notify_searchresults_column_new(_("Country"));
5863 purple_notify_searchresults_column_add(results
, column
);
5865 column
= purple_notify_searchresults_column_new(_("Email"));
5866 purple_notify_searchresults_column_add(results
, column
);
5868 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
5871 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
5872 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
5873 g_strfreev(uri_parts
);
5875 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
5876 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
5877 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
5878 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
5880 purple_notify_searchresults_row_add(results
, row
);
5884 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
5885 char *data
= xmlnode_get_data_unescaped(mrow
);
5886 more
= (g_strcasecmp(data
, "true") == 0);
5890 secondary
= g_strdup_printf(
5891 dngettext(GETTEXT_PACKAGE
,
5892 "Found %d contact%s:",
5893 "Found %d contacts%s:", match_count
),
5894 match_count
, more
? _(" (more matched your query)") : "");
5896 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
5897 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
5898 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
5901 xmlnode_free(searchResults
);
5905 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
5907 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
5908 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
5912 PurpleRequestField
*field
= entries
->data
;
5913 const char *id
= purple_request_field_get_id(field
);
5914 const char *value
= purple_request_field_string_get_value(field
);
5916 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
5918 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
5919 } while ((entries
= g_list_next(entries
)) != NULL
);
5923 gchar
*query
= g_strjoinv(NULL
, attrs
);
5924 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
5925 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
5926 send_soap_request_with_cb(gc
->proto_data
, body
,
5927 (TransCallback
) process_search_contact_response
, NULL
);
5935 static void sipe_show_find_contact(PurplePluginAction
*action
)
5937 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
5938 PurpleRequestFields
*fields
;
5939 PurpleRequestFieldGroup
*group
;
5940 PurpleRequestField
*field
;
5942 fields
= purple_request_fields_new();
5943 group
= purple_request_field_group_new(NULL
);
5944 purple_request_fields_add_group(fields
, group
);
5946 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
5947 purple_request_field_group_add_field(group
, field
);
5948 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
5949 purple_request_field_group_add_field(group
, field
);
5950 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
5951 purple_request_field_group_add_field(group
, field
);
5952 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
5953 purple_request_field_group_add_field(group
, field
);
5955 purple_request_fields(gc
,
5957 _("Search for a Contact"),
5958 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
5960 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
5962 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
5965 GList
*sipe_actions(PurplePlugin
*plugin
, gpointer context
)
5968 PurplePluginAction
*act
;
5970 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
5971 menu
= g_list_prepend(menu
, act
);
5973 menu
= g_list_reverse(menu
);
5978 static void dummy_permit_deny(PurpleConnection
*gc
)
5982 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
5988 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
5994 static char *sipe_status_text(PurpleBuddy
*buddy
)
5996 struct sipe_account_data
*sip
;
5997 struct sipe_buddy
*sbuddy
;
6000 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
6001 if (sip
) //happens on pidgin exit
6003 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
6004 if (sbuddy
&& sbuddy
->annotation
)
6006 text
= g_strdup(sbuddy
->annotation
);
6013 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
6015 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
6016 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
6017 struct sipe_account_data
*sip
;
6018 struct sipe_buddy
*sbuddy
;
6019 char *annotation
= NULL
;
6021 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
6022 if (sip
) //happens on pidgin exit
6024 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
6027 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
6032 if (purple_presence_is_online(presence
))
6034 purple_notify_user_info_add_pair(user_info
, _("Status"), purple_status_get_name(status
));
6039 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
6046 sipe_get_account_text_table(PurpleAccount
*account
)
6049 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
6050 g_hash_table_insert(table
, "login_label", (gpointer
)_("Sign-In Name..."));
6054 static PurpleBuddy
*
6055 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
6058 const gchar
*server_alias
, *email
;
6059 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
6061 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
6063 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
6065 server_alias
= g_strdup(purple_buddy_get_server_alias(buddy
));
6067 purple_blist_server_alias_buddy(clone
, server_alias
);
6070 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
6072 purple_blist_node_set_string((PurpleBlistNode
*)clone
, "email", email
);
6075 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
6077 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
6082 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
6084 PurpleBuddy
*buddy
, *b
;
6085 PurpleConnection
*gc
;
6086 PurpleGroup
* group
= purple_find_group(group_name
);
6088 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
6090 buddy
= (PurpleBuddy
*)node
;
6092 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
6093 gc
= purple_account_get_connection(buddy
->account
);
6095 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
6097 b
= purple_blist_add_buddy_clone(group
, buddy
);
6100 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
6104 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
6106 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6107 gchar
*self
= g_strdup_printf("sip:%s", sip
->username
);
6108 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++sip
->chat_seq
);
6109 struct sip_im_session
*session
;
6111 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: buddy->name=%s\n", buddy
->name
);
6113 session
= create_chat_session(sip
);
6114 session
->roster_manager
= g_strdup(self
);
6116 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, g_strdup(chat_name
));
6117 session
->chat_name
= g_strdup(chat_name
);
6118 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
6119 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, FALSE
);
6126 sipe_is_election_finished(struct sipe_account_data
*sip
,
6127 struct sip_im_session
*session
)
6129 struct sip_dialog
*dialog
;
6131 gboolean res
= TRUE
;
6133 entry
= session
->dialogs
;
6135 dialog
= entry
->data
;
6136 if (dialog
->election_vote
== 0) {
6140 entry
= entry
->next
;
6144 session
->is_voting_in_progress
= FALSE
;
6150 sipe_election_start(struct sipe_account_data
*sip
,
6151 struct sip_im_session
*session
)
6153 struct sip_dialog
*dialog
;
6155 int election_timeout
;
6157 if (session
->is_voting_in_progress
) {
6158 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
6161 session
->is_voting_in_progress
= TRUE
;
6163 session
->bid
= rand();
6165 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
6167 /* reset election_vote for each chat participant */
6168 entry
= session
->dialogs
;
6170 dialog
= entry
->data
;
6171 dialog
->election_vote
= 0;
6172 entry
= entry
->next
;
6175 entry
= session
->dialogs
;
6177 dialog
= entry
->data
;
6178 /* send RequestRM to each chat participant*/
6179 sipe_send_election_request_rm(sip
, session
, dialog
->with
, session
->bid
);
6180 entry
= entry
->next
;
6183 election_timeout
= 15; /* sec */
6184 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
6188 * @param who a URI to whom to invite to chat
6191 sipe_invite_to_chat(struct sipe_account_data
*sip
,
6192 struct sip_im_session
*session
,
6195 gchar
*self
= g_strdup_printf("sip:%s", sip
->username
);
6197 if (session
->roster_manager
) {
6198 if (!strcmp(session
->roster_manager
, self
)) {
6199 sipe_invite(sip
, session
, who
, NULL
, NULL
, FALSE
);
6201 sipe_refer(sip
, session
, who
);
6204 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: no RM available\n");
6206 session
->pending_invite_queue
= slist_insert_unique_sorted(
6207 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
6209 sipe_election_start(sip
, session
);
6216 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
6217 struct sip_im_session
*session
)
6220 GSList
*entry
= session
->pending_invite_queue
;
6223 invitee
= entry
->data
;
6224 sipe_invite_to_chat(sip
, session
, invitee
);
6225 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
6231 sipe_election_result(struct sipe_account_data
*sip
,
6234 struct sip_im_session
*session
= (struct sip_im_session
*)sess
;
6235 struct sip_dialog
*dialog
;
6238 gboolean has_won
= TRUE
;
6240 if (session
->roster_manager
) {
6241 purple_debug_info("sipe",
6242 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
6246 session
->is_voting_in_progress
= FALSE
;
6248 entry
= session
->dialogs
;
6250 dialog
= entry
->data
;
6251 if (dialog
->election_vote
< 0) {
6253 rival
= dialog
->with
;
6256 entry
= entry
->next
;
6260 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
6262 session
->roster_manager
= g_strdup_printf("sip:%s", sip
->username
);
6264 entry
= session
->dialogs
;
6266 dialog
= entry
->data
;
6267 /* send SetRM to each chat participant*/
6268 sipe_send_election_set_rm(sip
, session
, dialog
->with
);
6269 entry
= entry
->next
;
6272 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
6276 sipe_process_pending_invite_queue(sip
, session
);
6280 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, const char *chat_name
)
6282 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6283 struct sip_im_session
*session
;
6285 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: buddy->name=%s\n", buddy
->name
);
6286 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: chat_name=%s\n", chat_name
);
6288 session
= find_chat_session_by_name(sip
, chat_name
);
6290 sipe_invite_to_chat(sip
, session
, buddy
->name
);
6294 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
6297 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
6299 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
6302 char *mailto
= g_strdup_printf("mailto:%s", email
);
6303 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
6307 char *const parmList
[] = {mailto
, NULL
};
6308 if ((pid
= fork()) == -1)
6310 purple_debug_info("sipe", "fork() error\n");
6314 execvp("xdg-email", parmList
);
6315 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
6323 //@TODO resolve env variable %WINDIR% first
6324 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
6327 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
6336 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
6341 * A menu which appear when right-clicking on buddy in contact list.
6344 sipe_buddy_menu(PurpleBuddy
*buddy
)
6346 PurpleBlistNode
*g_node
;
6347 PurpleGroup
*group
, *gr_parent
;
6348 PurpleMenuAction
*act
;
6350 GList
*menu_groups
= NULL
;
6351 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6352 struct sip_im_session
*session
;
6354 gchar
*self
= g_strdup_printf("sip:%s", sip
->username
);
6356 act
= purple_menu_action_new(_("New Chat"),
6357 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
6359 menu
= g_list_prepend(menu
, act
);
6361 entry
= sip
->im_sessions
;
6363 session
= entry
->data
;
6364 if (strcmp(self
, buddy
->name
) && session
->chat_name
&& !get_dialog(session
, buddy
->name
)) {
6365 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_name
);
6366 act
= purple_menu_action_new(label
,
6367 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
6368 g_strdup(session
->chat_name
), NULL
);
6370 menu
= g_list_prepend(menu
, act
);
6372 entry
= entry
->next
;
6375 act
= purple_menu_action_new(_("Send Email..."),
6376 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
6378 menu
= g_list_prepend(menu
, act
);
6380 gr_parent
= purple_buddy_get_group(buddy
);
6381 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
6382 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
6385 group
= (PurpleGroup
*)g_node
;
6386 if (group
== gr_parent
)
6389 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
6392 act
= purple_menu_action_new(purple_group_get_name(group
),
6393 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
6395 menu_groups
= g_list_prepend(menu_groups
, act
);
6397 menu_groups
= g_list_reverse(menu_groups
);
6399 act
= purple_menu_action_new(_("Copy to"),
6402 menu
= g_list_prepend(menu
, act
);
6403 menu
= g_list_reverse(menu
);
6410 sipe_blist_node_menu(PurpleBlistNode
*node
)
6412 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
6413 return sipe_buddy_menu((PurpleBuddy
*) node
);
6420 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
6422 gboolean ret
= TRUE
;
6423 char *username
= (char *)trans
->payload
;
6425 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
6426 PurpleBuddy
*pbuddy
;
6427 struct sipe_buddy
*sbuddy
;
6429 char *server_alias
= NULL
;
6431 const char *device_name
= NULL
;
6433 purple_debug_info("sipe", "Fetching %s's user info for %s\n", username
, sip
->username
);
6435 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, username
);
6436 alias
= purple_buddy_get_local_alias(pbuddy
);
6440 //will query buddy UA's capabilities and send answer to log
6441 sipe_options_request(sip
, username
);
6443 sbuddy
= g_hash_table_lookup(sip
->buddies
, username
);
6446 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
6450 if (msg
->response
!= 200) {
6451 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
6453 xmlnode
*searchResults
;
6456 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
6457 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
6458 if (!searchResults
) {
6459 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
6460 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
6461 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
6462 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
6463 purple_notify_user_info_add_pair(info
, _("Job Title"), g_strdup(xmlnode_get_attrib(mrow
, "title")));
6464 purple_notify_user_info_add_pair(info
, _("Office"), g_strdup(xmlnode_get_attrib(mrow
, "office")));
6465 purple_notify_user_info_add_pair(info
, _("Business Phone"), g_strdup(xmlnode_get_attrib(mrow
, "phone")));
6466 purple_notify_user_info_add_pair(info
, _("Company"), g_strdup(xmlnode_get_attrib(mrow
, "company")));
6467 purple_notify_user_info_add_pair(info
, _("City"), g_strdup(xmlnode_get_attrib(mrow
, "city")));
6468 purple_notify_user_info_add_pair(info
, _("State"), g_strdup(xmlnode_get_attrib(mrow
, "state")));
6469 purple_notify_user_info_add_pair(info
, _("Country"), g_strdup(xmlnode_get_attrib(mrow
, "country")));
6470 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
6471 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
6472 if (!email
|| strcmp("", email
)) {
6473 if (!purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email")) {
6474 purple_blist_node_set_string((PurpleBlistNode
*)pbuddy
, "email", email
);
6478 xmlnode_free(searchResults
);
6481 purple_notify_user_info_add_section_break(info
);
6483 if (!server_alias
|| !strcmp("", server_alias
)) {
6484 g_free(server_alias
);
6485 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
6487 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
6491 // same as server alias, do not present
6492 alias
= (alias
&& server_alias
&& !strcmp(alias
, server_alias
)) ? NULL
: alias
;
6495 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
6498 if (!email
|| !strcmp("", email
)) {
6500 email
= g_strdup(purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email"));
6502 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
6508 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
6511 /* show a buddy's user info in a nice dialog box */
6512 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
6513 username
, /* buddy's username */
6515 NULL
, /* callback called when dialog closed */
6516 NULL
); /* userdata for callback */
6522 * AD search first, LDAP based
6524 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
6526 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
6527 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
6529 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
6530 send_soap_request_with_cb((struct sipe_account_data
*)gc
->proto_data
, body
,
6531 (TransCallback
) process_get_info_response
, (gpointer
)g_strdup(username
));
6536 static PurplePlugin
*my_protocol
= NULL
;
6538 static PurplePluginProtocolInfo prpl_info
=
6541 NULL
, /* user_splits */
6542 NULL
, /* protocol_options */
6543 NO_BUDDY_ICONS
, /* icon_spec */
6544 sipe_list_icon
, /* list_icon */
6545 NULL
, /* list_emblems */
6546 sipe_status_text
, /* status_text */
6547 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
6548 sipe_status_types
, /* away_states */
6549 sipe_blist_node_menu
, /* blist_node_menu */
6550 NULL
, /* chat_info */
6551 NULL
, /* chat_info_defaults */
6552 sipe_login
, /* login */
6553 sipe_close
, /* close */
6554 sipe_im_send
, /* send_im */
6555 NULL
, /* set_info */ // TODO maybe
6556 sipe_send_typing
, /* send_typing */
6557 sipe_get_info
, /* get_info */
6558 sipe_set_status
, /* set_status */
6559 NULL
, /* set_idle */
6560 NULL
, /* change_passwd */
6561 sipe_add_buddy
, /* add_buddy */
6562 NULL
, /* add_buddies */
6563 sipe_remove_buddy
, /* remove_buddy */
6564 NULL
, /* remove_buddies */
6565 sipe_add_permit
, /* add_permit */
6566 sipe_add_deny
, /* add_deny */
6567 sipe_add_deny
, /* rem_permit */
6568 sipe_add_permit
, /* rem_deny */
6569 dummy_permit_deny
, /* set_permit_deny */
6570 NULL
, /* join_chat */
6571 NULL
, /* reject_chat */
6572 NULL
, /* get_chat_name */
6573 NULL
, /* chat_invite */
6574 sipe_chat_leave
, /* chat_leave */
6575 NULL
, /* chat_whisper */
6576 sipe_chat_send
, /* chat_send */
6577 sipe_keep_alive
, /* keepalive */
6578 NULL
, /* register_user */
6579 NULL
, /* get_cb_info */ // deprecated
6580 NULL
, /* get_cb_away */ // deprecated
6581 sipe_alias_buddy
, /* alias_buddy */
6582 sipe_group_buddy
, /* group_buddy */
6583 sipe_rename_group
, /* rename_group */
6584 NULL
, /* buddy_free */
6585 sipe_convo_closed
, /* convo_closed */
6586 purple_normalize_nocase
, /* normalize */
6587 NULL
, /* set_buddy_icon */
6588 sipe_remove_group
, /* remove_group */
6589 NULL
, /* get_cb_real_name */ // TODO?
6590 NULL
, /* set_chat_topic */
6591 NULL
, /* find_blist_chat */
6592 NULL
, /* roomlist_get_list */
6593 NULL
, /* roomlist_cancel */
6594 NULL
, /* roomlist_expand_category */
6595 NULL
, /* can_receive_file */
6596 NULL
, /* send_file */
6597 NULL
, /* new_xfer */
6598 NULL
, /* offline_message */
6599 NULL
, /* whiteboard_prpl_ops */
6600 sipe_send_raw
, /* send_raw */
6601 NULL
, /* roomlist_room_serialize */
6602 NULL
, /* unregister_user */
6603 NULL
, /* send_attention */
6604 NULL
, /* get_attention_types */
6606 sizeof(PurplePluginProtocolInfo
), /* struct_size */
6607 sipe_get_account_text_table
, /* get_account_text_table */
6611 static PurplePluginInfo info
= {
6612 PURPLE_PLUGIN_MAGIC
,
6613 PURPLE_MAJOR_VERSION
,
6614 PURPLE_MINOR_VERSION
,
6615 PURPLE_PLUGIN_PROTOCOL
, /**< type */
6616 NULL
, /**< ui_requirement */
6618 NULL
, /**< dependencies */
6619 PURPLE_PRIORITY_DEFAULT
, /**< priority */
6620 "prpl-sipe", /**< id */
6621 "Microsoft LCS/OCS", /**< name */
6622 VERSION
, /**< version */
6623 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
6624 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
6625 "Anibal Avelar <avelar@gmail.com>, " /**< author */
6626 "Gabriel Burt <gburt@novell.com>", /**< author */
6627 PURPLE_WEBSITE
, /**< homepage */
6628 sipe_plugin_load
, /**< load */
6629 sipe_plugin_unload
, /**< unload */
6630 sipe_plugin_destroy
, /**< destroy */
6631 NULL
, /**< ui_info */
6632 &prpl_info
, /**< extra_info */
6641 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
6645 entry
= prpl_info
.protocol_options
;
6647 purple_account_option_destroy(entry
->data
);
6648 entry
= g_list_delete_link(entry
, entry
);
6650 prpl_info
.protocol_options
= NULL
;
6652 entry
= prpl_info
.user_splits
;
6654 purple_account_user_split_destroy(entry
->data
);
6655 entry
= g_list_delete_link(entry
, entry
);
6657 prpl_info
.user_splits
= NULL
;
6660 static void init_plugin(PurplePlugin
*plugin
)
6662 PurpleAccountUserSplit
*split
;
6663 PurpleAccountOption
*option
;
6668 purple_debug_info(PACKAGE
, "bindtextdomain = %s\n", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
6669 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s\n",
6670 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
6671 textdomain(GETTEXT_PACKAGE
);
6674 purple_plugin_register(plugin
);
6676 split
= purple_account_user_split_new(_("Login \n domain\\user or\n someone@linux.com "), NULL
, ',');
6677 purple_account_user_split_set_reverse(split
, FALSE
);
6678 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
6680 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
6681 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6682 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
6683 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6685 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
6686 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6687 // Translators: noun (networking port)
6688 option
= purple_account_option_int_new(_("Port"), "port", 5061);
6689 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6691 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
6692 purple_account_option_add_list_item(option
, _("Auto"), "auto");
6693 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
6694 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
6695 purple_account_option_add_list_item(option
, _("UDP"), "udp");
6696 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6698 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
6699 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
6701 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
6702 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6705 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
6706 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6708 /* Suitable for sspi/NTLM, sspi/Kerberos and krb5 security mechanisms
6709 * No login/password is taken into account if this option present,
6710 * instead used default credentials stored in OS.
6712 option
= purple_account_option_bool_new(_("Use Single Sign-On"), "sso", TRUE
);
6713 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6715 my_protocol
= plugin
;
6718 /* I had to redefined the function for it load, but works */
6719 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
6720 plugin
->info
= &(info
);
6721 init_plugin((plugin
));
6722 sipe_plugin_load((plugin
));
6723 return purple_plugin_register(plugin
);