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) */
2480 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
2482 get_end_points (struct sipe_account_data
*sip
,
2483 struct sip_im_session
*session
)
2487 if (session
== NULL
) {
2491 res
= g_strdup_printf("<sip:%s>", sip
->username
);
2493 SIPE_DIALOG_FOREACH
{
2495 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
2498 if (dialog
->theirepid
) {
2500 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
2503 } SIPE_DIALOG_FOREACH_END
;
2508 static struct sip_im_session
*
2509 find_chat_session_by_id (struct sipe_account_data
*sip
,
2512 struct sip_im_session
*session
;
2518 entry
= sip
->im_sessions
;
2520 session
= entry
->data
;
2521 if (id
== session
->chat_id
) {
2524 entry
= entry
->next
;
2529 static struct sip_im_session
*
2530 find_chat_session_by_name (struct sipe_account_data
*sip
,
2531 const char *chat_name
)
2533 struct sip_im_session
*session
;
2535 if (sip
== NULL
|| chat_name
== NULL
) {
2539 entry
= sip
->im_sessions
;
2541 session
= entry
->data
;
2542 if (session
->chat_name
&& !g_strcasecmp(chat_name
, session
->chat_name
)) {
2545 entry
= entry
->next
;
2550 static struct sip_im_session
*
2551 find_chat_session (struct sipe_account_data
*sip
,
2554 struct sip_im_session
*session
;
2556 if (sip
== NULL
|| callid
== NULL
) {
2560 entry
= sip
->im_sessions
;
2562 session
= entry
->data
;
2563 if (session
->callid
&& !g_strcasecmp(callid
, session
->callid
)) {
2566 entry
= entry
->next
;
2571 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
2573 struct sip_im_session
*session
;
2575 if (sip
== NULL
|| who
== NULL
) {
2579 entry
= sip
->im_sessions
;
2581 session
= entry
->data
;
2582 if (session
->with
&& !strcmp(who
, session
->with
)) {
2585 entry
= entry
->next
;
2590 struct sip_im_session
*
2591 create_chat_session (struct sipe_account_data
*sip
)
2593 struct sip_im_session
*session
= g_new0(struct sip_im_session
, 1);
2594 session
->callid
= gencallid();
2595 session
->is_multiparty
= TRUE
;
2596 session
->chat_id
= rand();
2597 session
->unconfirmed_messages
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
2598 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
2602 static struct sip_im_session
* find_or_create_chat_session (struct sipe_account_data
*sip
, const char *callid
)
2604 struct sip_im_session
*session
= find_chat_session(sip
, callid
);
2606 session
= create_chat_session(sip
);
2607 session
->callid
= g_strdup(callid
);
2612 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
2614 struct sip_im_session
*session
= find_im_session(sip
, who
);
2616 session
= g_new0(struct sip_im_session
, 1);
2617 session
->is_multiparty
= FALSE
;
2618 session
->with
= g_strdup(who
);
2619 session
->unconfirmed_messages
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
2620 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
2625 void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2629 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
2631 sipe_dialog_remove_all(session
);
2633 entry
= session
->outgoing_message_queue
;
2635 g_free(entry
->data
);
2636 entry
= g_slist_remove(entry
, entry
->data
);
2639 entry
= session
->pending_invite_queue
;
2641 g_free(entry
->data
);
2642 entry
= g_slist_remove(entry
, entry
->data
);
2645 g_hash_table_destroy(session
->unconfirmed_messages
);
2647 g_free(session
->with
);
2648 g_free(session
->chat_name
);
2649 g_free(session
->callid
);
2650 g_free(session
->roster_manager
);
2651 g_free(session
->focus_uri
);
2652 g_free(session
->im_mcu_uri
);
2657 process_options_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2659 gboolean ret
= TRUE
;
2661 if (msg
->response
!= 200) {
2662 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
2666 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
2672 * Asks UA/proxy about its capabilities.
2674 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
2676 gchar
*to
= strstr(who
, "sip:") ? g_strdup(who
) : g_strdup_printf("sip:%s", who
);
2677 gchar
*contact
= get_contact(sip
);
2679 request
= g_strdup_printf(
2680 "Accept: application/sdp\r\n"
2681 "Contact: %s\r\n", contact
);
2685 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
2691 static void sipe_present_message_undelivered_err(gchar
*with
, struct sipe_account_data
*sip
, gchar
*message
)
2693 char *msg
, *msg_tmp
;
2694 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
2695 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2697 msg_tmp
= g_strdup_printf( _("The following message could not be delivered to all recipients, "\
2698 "possibly because one or more persons are offline:\n%s") ,
2700 purple_conv_present_error(with
, sip
->account
, msg_tmp
);
2705 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
);
2708 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2710 gboolean ret
= TRUE
;
2711 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
2712 struct sip_im_session
* session
= find_im_session(sip
, with
);
2713 struct sip_dialog
*dialog
;
2719 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2724 dialog
= sipe_dialog_find(session
, with
);
2726 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2731 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2732 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
2734 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2736 if (msg
->response
!= 200) {
2737 purple_debug_info("sipe", "process_message_response: MESSAGE response not 200\n");
2739 sipe_present_message_undelivered_err(with
, sip
, message
);
2740 im_session_destroy(sip
, session
);
2743 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2744 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
2745 key
, g_hash_table_size(session
->unconfirmed_messages
));
2751 if (ret
) sipe_im_process_queue(sip
, session
);
2756 sipe_is_election_finished(struct sipe_account_data
*sip
,
2757 struct sip_im_session
*session
);
2760 sipe_election_result(struct sipe_account_data
*sip
,
2764 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2766 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2767 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2768 struct sip_dialog
*dialog
;
2769 struct sip_im_session
*session
;
2771 session
= find_chat_session(sip
, callid
);
2773 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
2777 if (msg
->response
== 200 && !strncmp(contenttype
, "application/x-ms-mim", 20)) {
2778 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2779 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
2780 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
2782 if (xn_request_rm_response
) {
2783 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
2784 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
2786 dialog
= sipe_dialog_find(session
, with
);
2788 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
2792 if (allow
&& !g_strcasecmp(allow
, "true")) {
2793 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
2794 dialog
->election_vote
= 1;
2795 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
2796 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
2797 dialog
->election_vote
= -1;
2800 if (sipe_is_election_finished(sip
, session
)) {
2801 sipe_election_result(sip
, session
);
2804 } else if (xn_set_rm_response
) {
2807 xmlnode_free(xn_action
);
2814 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
)
2823 sipe_parse_html(msg
, &msgformat
, &msgtext
);
2824 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
2826 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2829 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2832 msgr
= g_strdup("");
2835 tmp
= get_contact(sip
);
2836 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
2837 //hdr = g_strdup("Content-Type: text/rtf\r\n");
2838 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
2839 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n", tmp
, msgr
);
2843 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
2850 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
2852 GSList
*entry2
= session
->outgoing_message_queue
;
2854 char *queued_msg
= entry2
->data
;
2856 if (session
->is_multiparty
) {
2857 gchar
*who
= g_strdup_printf("sip:%s", sip
->username
);
2858 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
2859 PURPLE_MESSAGE_SEND
, queued_msg
, time(NULL
));
2863 SIPE_DIALOG_FOREACH
{
2866 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
2868 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
2869 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
2870 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
2871 key
, g_hash_table_size(session
->unconfirmed_messages
));
2874 sipe_send_message(sip
, dialog
, queued_msg
);
2875 } SIPE_DIALOG_FOREACH_END
;
2877 entry2
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2883 sipe_refer_notify(struct sipe_account_data
*sip
,
2884 struct sip_im_session
*session
,
2891 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
2893 hdr
= g_strdup_printf(
2895 "Subscription-State: %s\r\n"
2896 "Content-Type: message/sipfrag\r\n",
2897 status
>= 200 ? "terminated" : "active");
2899 body
= g_strdup_printf(
2900 "SIP/2.0 %d %s\r\n",
2903 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "NOTIFY",
2904 who
, who
, hdr
, body
, dialog
, NULL
);
2911 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2913 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2914 struct sip_im_session
*session
;
2915 struct sip_dialog
*dialog
;
2919 struct sipmsg
*request_msg
= trans
->msg
;
2921 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2924 session
= find_chat_session(sip
, callid
);
2926 session
= find_im_session(sip
, with
);
2929 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2934 dialog
= sipe_dialog_find(session
, with
);
2936 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2941 sipe_dialog_parse(dialog
, msg
, TRUE
);
2943 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2944 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
2946 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2948 if (msg
->response
!= 200) {
2949 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2951 sipe_present_message_undelivered_err(with
, sip
, message
);
2952 im_session_destroy(sip
, session
);
2959 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
2960 dialog
->outgoing_invite
= NULL
;
2961 dialog
->is_established
= TRUE
;
2963 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
2965 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
2966 g_free(referred_by
);
2969 /* add user to chat if it is a multiparty session */
2970 if (session
->is_multiparty
) {
2971 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
2973 PURPLE_CBFLAGS_NONE
, TRUE
);
2976 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
2977 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
2978 if (session
->outgoing_message_queue
) {
2979 char *queued_msg
= session
->outgoing_message_queue
->data
;
2980 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2985 sipe_im_process_queue(sip
, session
);
2987 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2988 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
2989 key
, g_hash_table_size(session
->unconfirmed_messages
));
2998 sipe_invite(struct sipe_account_data
*sip
,
2999 struct sip_im_session
*session
,
3001 const gchar
*msg_body
,
3002 const gchar
*referred_by
,
3003 const gboolean is_triggered
)
3010 char *ms_text_format
= g_strdup("");
3011 gchar
*roster_manager
;
3013 gchar
*referred_by_str
;
3014 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3016 if (dialog
&& dialog
->is_established
) {
3017 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
3022 dialog
= sipe_dialog_add(session
);
3023 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
3024 dialog
->with
= g_strdup(who
);
3027 if (!(dialog
->ourtag
)) {
3028 dialog
->ourtag
= gentag();
3032 if (strstr(who
, "sip:")) {
3035 to
= g_strdup_printf("sip:%s", who
);
3046 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
3047 purple_debug_info("sipe", "sipe_invite: msgformat=%s", msgformat
);
3049 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3053 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
3057 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
3058 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
3063 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
3064 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
3065 purple_debug_info("sipe", "sipe_im_send: added message %s to unconfirmed_messages(count=%d)\n",
3066 key
, g_hash_table_size(session
->unconfirmed_messages
));
3070 contact
= get_contact(sip
);
3071 end_points
= get_end_points(sip
, session
);
3072 self
= g_strdup_printf("sip:%s", sip
->username
);
3073 roster_manager
= g_strdup_printf(
3074 "Roster-Manager: %s\r\n"
3075 "EndPoints: %s\r\n",
3078 referred_by_str
= referred_by
?
3080 "Referred-By: %s\r\n",
3083 hdr
= g_strdup_printf(
3089 "Content-Type: application/sdp\r\n",
3090 (session
->roster_manager
&& !strcmp(session
->roster_manager
, self
)) ? roster_manager
: "",
3092 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
3093 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
3096 g_free(ms_text_format
);
3099 body
= g_strdup_printf(
3101 "o=- 0 0 IN IP4 %s\r\n"
3105 "m=message %d sip null\r\n"
3106 "a=accept-types:text/plain text/html image/gif "
3107 "multipart/alternative application/im-iscomposing+xml\r\n",
3108 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
3110 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
3111 to
, to
, hdr
, body
, dialog
, process_invite_response
);
3114 g_free(roster_manager
);
3116 g_free(referred_by_str
);
3123 sipe_refer(struct sipe_account_data
*sip
,
3124 struct sip_im_session
*session
,
3129 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
3130 session
->roster_manager
);
3132 contact
= get_contact(sip
);
3133 hdr
= g_strdup_printf(
3135 "Refer-to: <%s>\r\n"
3136 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
3137 "Require: com.microsoft.rtc-multiparty\r\n",
3141 dialog
->ourtag
? ";tag=" : "",
3142 dialog
->ourtag
? dialog
->ourtag
: "",
3145 send_sip_request(sip
->gc
, "REFER",
3146 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
3153 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
3154 struct sip_dialog
*dialog
,
3157 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
3159 gchar
*body
= g_strdup_printf(
3160 "<?xml version=\"1.0\"?>\r\n"
3161 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3162 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
3163 sip
->username
, bid
);
3165 send_sip_request(sip
->gc
, "INFO",
3166 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
3172 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
3173 struct sip_dialog
*dialog
)
3175 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
3177 gchar
*body
= g_strdup_printf(
3178 "<?xml version=\"1.0\"?>\r\n"
3179 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3180 "<SetRM uri=\"sip:%s\"/></action>\r\n",
3183 send_sip_request(sip
->gc
, "INFO",
3184 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
3190 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
3193 SIPE_DIALOG_FOREACH
{
3194 /* @TODO slow down BYE message sending rate */
3195 /* @see single subscription code */
3196 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
3197 } SIPE_DIALOG_FOREACH_END
;
3199 im_session_destroy(sip
, session
);
3204 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
3206 struct sipe_account_data
*sip
= gc
->proto_data
;
3208 purple_debug_info("sipe", "conversation with %s closed\n", who
);
3209 im_session_close(sip
, find_im_session(sip
, who
));
3213 sipe_chat_leave (PurpleConnection
*gc
, int id
)
3215 struct sipe_account_data
*sip
= gc
->proto_data
;
3216 struct sip_im_session
* session
= find_chat_session_by_id(sip
, id
);
3217 im_session_close(sip
, session
);
3221 im_session_close_all (struct sipe_account_data
*sip
)
3223 GSList
*entry
= sip
->im_sessions
;
3225 im_session_close (sip
, entry
->data
);
3226 entry
= sip
->im_sessions
;
3230 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
3232 struct sipe_account_data
*sip
= gc
->proto_data
;
3233 struct sip_im_session
*session
;
3234 struct sip_dialog
*dialog
;
3236 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
3238 session
= find_or_create_im_session(sip
, who
);
3239 dialog
= sipe_dialog_find(session
, who
);
3241 // Queue the message
3242 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
3244 if (dialog
&& dialog
->callid
) {
3245 sipe_im_process_queue(sip
, session
);
3246 } else if (!dialog
|| !dialog
->outgoing_invite
) {
3247 // Need to send the INVITE to get the outgoing dialog setup
3248 sipe_invite(sip
, session
, who
, what
, NULL
, FALSE
);
3254 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
, PurpleMessageFlags flags
)
3256 struct sipe_account_data
*sip
= gc
->proto_data
;
3257 struct sip_im_session
*session
;
3259 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
3261 session
= find_chat_session_by_id(sip
, id
);
3263 // Queue the message
3265 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
3267 sipe_im_process_queue(sip
, session
);
3273 /* End IM Session (INVITE and MESSAGE methods) */
3275 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3277 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
3278 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3279 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3281 struct sip_im_session
*session
= find_chat_session(sip
, callid
);
3283 session
= find_im_session(sip
, from
);
3290 if (!strncmp(contenttype
, "application/x-ms-mim", 20)) {
3291 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3292 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
3293 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
3295 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
3297 if (xn_request_rm
) {
3298 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
3299 int bid
= atoi(xmlnode_get_attrib(xn_request_rm
, "bid"));
3300 gchar
*body
= g_strdup_printf(
3301 "<?xml version=\"1.0\"?>\r\n"
3302 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3303 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
3305 session
->bid
< bid
? "true" : "false");
3306 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3308 } else if (xn_set_rm
) {
3310 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
3311 g_free(session
->roster_manager
);
3312 session
->roster_manager
= g_strdup(rm
);
3314 body
= g_strdup_printf(
3315 "<?xml version=\"1.0\"?>\r\n"
3316 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3317 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
3319 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3322 xmlnode_free(xn_action
);
3325 /* looks like purple lacks typing notification for chat */
3326 if (!session
->is_multiparty
) {
3327 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
3330 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3335 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3337 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3338 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3339 struct sip_im_session
*session
;
3341 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3343 session
= find_chat_session(sip
, callid
);
3345 session
= find_im_session(sip
, from
);
3352 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
3353 g_free(session
->roster_manager
);
3354 session
->roster_manager
= NULL
;
3357 if (!session
->is_multiparty
) {
3358 // TODO Let the user know the other user left the conversation?
3359 im_session_destroy(sip
, session
);
3361 sipe_dialog_remove(session
, from
);
3363 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
3365 if (!sipe_dialog_any(session
)) {
3366 im_session_destroy(sip
, session
);
3373 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3375 gchar
*self
= g_strdup_printf("sip:%s", sip
->username
);
3376 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3377 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3378 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
3379 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
3380 struct sip_im_session
*session
;
3381 struct sip_dialog
*dialog
;
3383 session
= find_chat_session(sip
, callid
);
3384 dialog
= sipe_dialog_find(session
, from
);
3386 if (!session
|| !dialog
|| !session
->roster_manager
|| strcmp(session
->roster_manager
, self
)) {
3387 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
3389 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
3391 sipe_invite(sip
, session
, refer_to
, NULL
, referred_by
, FALSE
);
3397 g_free(referred_by
);
3401 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
3403 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
3404 struct sip_im_session
*session
;
3405 struct sip_dialog
*dialog
;
3407 if (state
== PURPLE_NOT_TYPING
)
3410 session
= find_im_session(sip
, who
);
3411 dialog
= sipe_dialog_find(session
, who
);
3413 if (session
&& dialog
) {
3414 send_sip_request(gc
, "INFO", who
, who
,
3415 "Content-Type: application/xml\r\n",
3416 SIPE_SEND_TYPING
, dialog
, NULL
);
3418 return SIPE_TYPING_SEND_TIMEOUT
;
3421 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
3423 GSList
*tmp
= sip
->transactions
;
3424 time_t currtime
= time(NULL
);
3426 struct transaction
*trans
= tmp
->data
;
3428 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
3429 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
3432 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
3434 sendout_sipmsg(sip
, trans
->msg
);
3441 static void do_reauthenticate_cb(struct sipe_account_data
*sip
, void *unused
)
3443 /* register again when security token expires */
3444 /* we have to start a new authentication as the security token
3445 * is almost expired by sending a not signed REGISTER message */
3446 purple_debug_info("sipe", "do a full reauthentication\n");
3447 sipe_auth_free(&sip
->registrar
);
3448 sipe_auth_free(&sip
->proxy
);
3449 sip
->registerstatus
= 0;
3451 sip
->reauthenticate_set
= FALSE
;
3454 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3458 gboolean found
= FALSE
;
3460 from
= parse_from(sipmsg_find_header(msg
, "From"));
3464 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
3466 contenttype
= sipmsg_find_header(msg
, "Content-Type");
3467 if (!strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
3469 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3470 gchar
*html
= get_html_message(contenttype
, msg
->body
);
3472 struct sip_im_session
*session
= find_chat_session(sip
, callid
);
3474 session
= find_im_session(sip
, from
);
3477 if (session
&& session
->is_multiparty
) {
3478 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
3479 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3481 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3484 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3487 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
3488 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3493 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
3497 state
= xmlnode_get_child(isc
, "state");
3500 purple_debug_info("sipe", "process_incoming_message: no state found\n");
3505 statedata
= xmlnode_get_data(state
);
3507 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
3508 else serv_got_typing_stopped(sip
->gc
, from
);
3513 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3517 purple_debug_info("sipe", "got unknown mime-type");
3518 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
3523 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3525 gchar
*ms_text_format
;
3527 gchar
*newTag
= gentag();
3530 gboolean is_multiparty
= FALSE
;
3531 gboolean is_triggered
= FALSE
;
3532 gboolean was_multiparty
= TRUE
;
3533 gboolean just_joined
= FALSE
;
3534 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3535 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
3536 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3537 gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
3538 gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
3539 gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
3540 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
3541 GSList
*end_points
= NULL
;
3542 struct sip_im_session
*session
;
3544 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? msg
->body
: "");
3546 /* Invitation to join conference */
3547 if (!strncmp(content_type
, "application/ms-conf-invite+xml", 30)) {
3548 process_incoming_invite_conf(sip
, msg
);
3552 /* Only accept text invitations */
3553 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
3554 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3558 // TODO There *must* be a better way to clean up the To header to add a tag...
3559 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
3560 oldHeader
= sipmsg_find_header(msg
, "To");
3561 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
3562 sipmsg_remove_header_now(msg
, "To");
3563 sipmsg_add_header_now(msg
, "To", newHeader
);
3566 if (end_points_hdr
) {
3567 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
3569 if (g_slist_length(end_points
) > 2) {
3570 is_multiparty
= TRUE
;
3573 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
3574 is_triggered
= TRUE
;
3575 is_multiparty
= TRUE
;
3578 session
= find_chat_session(sip
, callid
);
3579 /* Convert to multiparty */
3580 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
3581 g_free(session
->with
);
3582 session
->with
= NULL
;
3583 was_multiparty
= FALSE
;
3584 session
->is_multiparty
= TRUE
;
3585 session
->chat_id
= rand();
3588 if (!session
&& is_multiparty
) {
3589 session
= find_or_create_chat_session(sip
, callid
);
3593 session
= find_or_create_im_session(sip
, from
);
3596 if (!session
->callid
) {
3597 session
->callid
= g_strdup(callid
);
3600 session
->is_multiparty
= is_multiparty
;
3601 if (roster_manager
) {
3602 session
->roster_manager
= g_strdup(roster_manager
);
3605 if (is_multiparty
&& end_points
) {
3606 GSList
*entry
= end_points
;
3608 struct sip_dialog
*dialog
;
3609 struct sipendpoint
*end_point
= entry
->data
;
3610 entry
= entry
->next
;
3612 if (!g_strcasecmp(from
, end_point
->contact
) ||
3613 !g_strcasecmp(to
, end_point
->contact
))
3616 dialog
= sipe_dialog_find(session
, end_point
->contact
);
3618 g_free(dialog
->theirepid
);
3619 dialog
->theirepid
= end_point
->epid
;
3620 end_point
->epid
= NULL
;
3622 dialog
= sipe_dialog_add(session
);
3624 dialog
->callid
= g_strdup(session
->callid
);
3625 dialog
->with
= end_point
->contact
;
3626 end_point
->contact
= NULL
;
3627 dialog
->theirepid
= end_point
->epid
;
3628 end_point
->epid
= NULL
;
3632 /* send triggered INVITE */
3633 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, TRUE
);
3639 GSList
*entry
= end_points
;
3641 struct sipendpoint
*end_point
= entry
->data
;
3642 entry
= entry
->next
;
3643 g_free(end_point
->contact
);
3644 g_free(end_point
->epid
);
3647 g_slist_free(end_points
);
3651 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
3653 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
3655 dialog
= sipe_dialog_add(session
);
3657 dialog
->callid
= g_strdup(session
->callid
);
3658 dialog
->with
= g_strdup(from
);
3659 sipe_dialog_parse(dialog
, msg
, FALSE
);
3661 if (!dialog
->ourtag
) {
3662 dialog
->ourtag
= newTag
;
3669 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
3673 if (is_multiparty
&& !session
->conv
) {
3674 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++sip
->chat_seq
);
3675 gchar
*self
= g_strdup_printf("sip:%s", sip
->username
);
3676 /* create prpl chat */
3677 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_name
);
3678 session
->chat_name
= g_strdup(chat_name
);
3680 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3682 PURPLE_CBFLAGS_NONE
, FALSE
);
3687 if (is_multiparty
&& !was_multiparty
) {
3688 /* add current IM counterparty to chat */
3689 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3690 sipe_dialog_first(session
)->with
, NULL
,
3691 PURPLE_CBFLAGS_NONE
, FALSE
);
3694 /* add inviting party */
3696 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3698 PURPLE_CBFLAGS_NONE
, TRUE
);
3701 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
3702 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
3703 if (ms_text_format
) {
3704 if (!strncmp(ms_text_format
, "text/plain", 10) || !strncmp(ms_text_format
, "text/html", 9)) {
3706 gchar
*html
= get_html_message(ms_text_format
, NULL
);
3708 if (is_multiparty
) {
3709 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
3710 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3712 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3715 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message received
3721 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
3722 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3723 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
3725 body
= g_strdup_printf(
3727 "o=- 0 0 IN IP4 %s\r\n"
3731 "m=message %d sip sip:%s\r\n"
3732 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
3733 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
3734 sip
->realport
, sip
->username
);
3735 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3739 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3743 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
3744 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3745 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
3747 body
= g_strdup_printf(
3749 "o=- 0 0 IN IP4 0.0.0.0\r\n"
3751 "c=IN IP4 0.0.0.0\r\n"
3753 "m=message %d sip sip:%s\r\n"
3754 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
3755 sip
->realport
, sip
->username
);
3756 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3760 static void sipe_connection_cleanup(struct sipe_account_data
*);
3761 static void create_connection(struct sipe_account_data
*, gchar
*, int);
3763 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3766 const gchar
*expires_header
;
3768 GSList
*hdr
= msg
->headers
;
3770 struct siphdrelement
*elem
;
3772 expires_header
= sipmsg_find_header(msg
, "Expires");
3773 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
3774 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
3776 switch (msg
->response
) {
3779 sip
->registerstatus
= 0;
3781 gchar
*contact_hdr
= NULL
;
3787 if (!sip
->reregister_set
) {
3788 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
3789 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
3790 g_free(action_name
);
3791 sip
->reregister_set
= TRUE
;
3794 sip
->registerstatus
= 3;
3797 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3799 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3802 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3805 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3806 fill_auth(sip
, tmp
, &sip
->registrar
);
3808 if (!sip
->reauthenticate_set
) {
3809 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
3810 guint reauth_timeout
;
3811 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
3812 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
3813 reauth_timeout
= sip
->registrar
.expires
- 300;
3815 /* NTLM: we have to reauthenticate as our security token expires
3816 after eight hours (be five minutes early) */
3817 reauth_timeout
= (8 * 3600) - 300;
3819 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
3820 g_free(action_name
);
3821 sip
->reauthenticate_set
= TRUE
;
3824 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
3826 epid
= get_epid(sip
);
3827 uuid
= generateUUIDfromEPID(epid
);
3830 // There can be multiple Contact headers (one per location where the user is logged in) so
3831 // make sure to only get the one for this uuid
3832 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
3833 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
3834 if (valid_contact
) {
3835 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
3836 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
3837 g_free(valid_contact
);
3840 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
3845 g_free(sip
->contact
);
3847 sip
->contact
= g_strdup_printf("<%s>", gruu
);
3850 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
3851 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
);
3853 sip
->msrtc_event_categories
= FALSE
;
3854 sip
->batched_support
= FALSE
;
3859 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
3860 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
3861 sip
->msrtc_event_categories
= TRUE
;
3862 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->msrtc_event_categories
);
3864 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
3865 sip
->batched_support
= TRUE
;
3866 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->batched_support
);
3869 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
3870 gchar
**caps
= g_strsplit(elem
->value
,",",0);
3873 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
3874 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
3879 hdr
= g_slist_next(hdr
);
3882 if (!sip
->subscribed
) { //do it just once, not every re-register
3883 if(!sip
->msrtc_event_categories
){ //Only for LCS2005, on OCS2007 always backs the error 504 Server time-out
3884 //sipe_options_request(sip, sip->sipdomain);
3886 entry
= sip
->allow_events
;
3889 if (tmp
&& !g_ascii_strcasecmp(tmp
, "vnd-microsoft-roaming-contacts")) {
3890 sipe_subscribe_roaming_contacts(sip
, msg
);
3892 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-roaming-ACL")) {
3893 sipe_subscribe_roaming_acl(sip
, msg
);
3895 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-roaming-self")) {
3896 sipe_subscribe_roaming_self(sip
, msg
);
3898 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-provisioning-v2")) {
3899 sipe_subscribe_roaming_provisioning_v2(sip
, msg
);
3900 } else if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-provisioning")) { // LSC2005
3901 sipe_subscribe_roaming_provisioning(sip
, msg
);
3903 if (tmp
&& !g_ascii_strcasecmp(tmp
,"presence.wpending")) {
3904 sipe_subscribe_presence_wpending(sip
, msg
);
3906 entry
= entry
->next
;
3908 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
3909 sip
->subscribed
= TRUE
;
3912 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
3913 "timeout=", ";", NULL
);
3914 if (timeout
!= NULL
) {
3915 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
3916 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
3917 sip
->keepalive_timeout
);
3921 // Should we remove the transaction here?
3922 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
3923 transactions_remove(sip
, tc
);
3928 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
3930 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
3931 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
3935 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
3938 tmp
= g_strsplit(parts
[0], ":", 0);
3939 hostname
= g_strdup(tmp
[0]);
3940 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
3944 tmp
= g_strsplit(parts
[i
], "=", 0);
3946 if (g_strcasecmp("transport", tmp
[0]) == 0) {
3947 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
3948 transport
= SIPE_TRANSPORT_TCP
;
3949 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
3950 transport
= SIPE_TRANSPORT_UDP
;
3959 /* Close old connection */
3960 sipe_connection_cleanup(sip
);
3962 /* Create new connection */
3963 sip
->transport
= transport
;
3964 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
3965 hostname
, port
, TRANSPORT_DESCRIPTOR
);
3966 create_connection(sip
, hostname
, port
);
3972 if (sip
->registerstatus
!= 2) {
3973 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
3974 if (sip
->registrar
.retries
> 3) {
3975 sip
->gc
->wants_to_die
= TRUE
;
3976 purple_connection_error(sip
->gc
, _("Wrong Password"));
3980 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3982 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3985 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3988 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3989 fill_auth(sip
, tmp
, &sip
->registrar
);
3990 sip
->registerstatus
= 2;
3991 if (sip
->account
->disconnecting
) {
3992 do_register_exp(sip
, 0);
4000 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
4001 if (warning
!= NULL
) {
4003 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
4005 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
4006 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
4009 warning
= g_strdup(_("You have been rejected by the server"));
4012 sip
->gc
->wants_to_die
= TRUE
;
4013 purple_connection_error(sip
->gc
, warning
);
4020 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
4021 if (warning
!= NULL
) {
4022 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
4023 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
4026 warning
= g_strdup(_("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator"));
4029 sip
->gc
->wants_to_die
= TRUE
;
4030 purple_connection_error(sip
->gc
, warning
);
4037 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
4038 if (warning
!= NULL
) {
4039 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
4040 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
4043 warning
= g_strdup(_("Service unavailable: no reason given"));
4046 sip
->gc
->wants_to_die
= TRUE
;
4047 purple_connection_error(sip
->gc
, warning
);
4056 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4059 xmlnode
*xn_categories
;
4060 xmlnode
*xn_category
;
4062 const char *activity
= NULL
;
4064 xn_categories
= xmlnode_from_str(data
, len
);
4065 uri
= xmlnode_get_attrib(xn_categories
, "uri");
4067 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
4069 xn_category
= xmlnode_get_next_twin(xn_category
) )
4071 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
4073 if (!strcmp(attrVar
, "note"))
4076 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4081 xn_node
= xmlnode_get_child(xn_category
, "note");
4082 if (!xn_node
) continue;
4083 xn_node
= xmlnode_get_child(xn_node
, "body");
4084 if (!xn_node
) continue;
4085 note
= xmlnode_get_data(xn_node
);
4086 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s),note(%s)\n",uri
,note
? note
: "");
4087 g_free(sbuddy
->annotation
);
4088 sbuddy
->annotation
= NULL
;
4089 if (note
) sbuddy
->annotation
= g_strdup(note
);
4095 else if(!strcmp(attrVar
, "state"))
4099 xn_node
= xmlnode_get_child(xn_category
, "state");
4100 if (!xn_node
) continue;
4101 xn_node
= xmlnode_get_child(xn_node
, "availability");
4102 if (!xn_node
) continue;
4104 data
= xmlnode_get_data(xn_node
);
4109 activity
= SIPE_STATUS_ID_UNKNOWN
;
4110 else if (avail
< 4500)
4111 activity
= SIPE_STATUS_ID_AVAILABLE
;
4112 else if (avail
< 6000)
4113 activity
= SIPE_STATUS_ID_BRB
;
4114 else if (avail
< 7500)
4115 activity
= SIPE_STATUS_ID_ONPHONE
;
4116 else if (avail
< 9000)
4117 activity
= SIPE_STATUS_ID_BUSY
;
4118 else if (avail
< 12000)
4119 activity
= SIPE_STATUS_ID_DND
;
4120 else if (avail
< 18000)
4121 activity
= SIPE_STATUS_ID_AWAY
;
4123 activity
= SIPE_STATUS_ID_OFFLINE
;
4127 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", activity
);
4128 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
4131 xmlnode_free(xn_categories
);
4134 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
4136 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4137 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
4138 payload
->host
= g_strdup(host
);
4139 payload
->buddies
= server
;
4140 sipe_subscribe_presence_batched_routed(sip
, payload
);
4141 sipe_subscribe_presence_batched_routed_free(payload
);
4144 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4147 xmlnode
*xn_resource
;
4148 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
4153 xn_list
= xmlnode_from_str(data
, len
);
4155 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
4157 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
4159 const char *uri
, *state
;
4160 xmlnode
*xn_instance
;
4162 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
4163 if (!xn_instance
) continue;
4165 uri
= xmlnode_get_attrib(xn_resource
, "uri");
4166 state
= xmlnode_get_attrib(xn_instance
, "state");
4167 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
4169 if (strstr(state
, "resubscribe")) {
4170 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
4171 struct sipe_buddy
*sbuddy
;
4172 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
4173 gchar
*user
= g_strdup(uri
);
4174 host
= g_strdup(poolFqdn
);
4175 server
= g_hash_table_lookup(servers
, host
);
4176 server
= g_slist_append(server
, user
);
4177 g_hash_table_insert(servers
, host
, server
);
4179 sipe_subscribe_presence_single(sip
, (void *) uri
);
4181 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4183 sbuddy
->resubscribed
= TRUE
;
4188 /* Send out any deferred poolFqdn subscriptions */
4189 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
4190 g_hash_table_destroy(servers
);
4192 xmlnode_free(xn_list
);
4195 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4199 gchar
*activity
= NULL
;
4201 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
4202 gboolean isonline
= FALSE
;
4203 xmlnode
*display_name_node
;
4205 pidf
= xmlnode_from_str(data
, len
);
4207 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data
);
4211 uri
= xmlnode_get_attrib(pidf
, "entity");
4213 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
4215 if ((status
= xmlnode_get_child(tuple
, "status"))) {
4216 basicstatus
= xmlnode_get_child(status
, "basic");
4221 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
4226 getbasic
= xmlnode_get_data(basicstatus
);
4228 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
4233 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
4234 if (strstr(getbasic
, "open")) {
4239 display_name_node
= xmlnode_get_child(pidf
, "display-name");
4240 // updating display name if alias was just URI
4241 if (display_name_node
) {
4242 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
4243 GSList
*entry
= buddies
;
4244 PurpleBuddy
*p_buddy
;
4245 char * display_name
= xmlnode_get_data(display_name_node
);
4248 const char *server_alias
;
4251 p_buddy
= entry
->data
;
4253 alias
= (char *)purple_buddy_get_alias(p_buddy
);
4254 alias
= alias
? g_strdup_printf("sip:%s", alias
) : NULL
;
4255 if (!alias
|| !g_ascii_strcasecmp(uri
, alias
)) {
4256 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
4257 purple_blist_alias_buddy(p_buddy
, display_name
);
4261 server_alias
= purple_buddy_get_server_alias(p_buddy
);
4263 ( (server_alias
&& strcmp(display_name
, server_alias
))
4264 || !server_alias
|| strlen(server_alias
) == 0 )
4266 purple_blist_server_alias_buddy(p_buddy
, display_name
);
4269 entry
= entry
->next
;
4271 g_free(display_name
);
4274 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
4275 if ((status
= xmlnode_get_child(tuple
, "status"))) {
4276 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
4277 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
4278 activity
= xmlnode_get_data(basicstatus
);
4279 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
4286 const gchar
* status_id
= NULL
;
4288 if (strstr(activity
, "busy")) {
4289 status_id
= SIPE_STATUS_ID_BUSY
;
4290 } else if (strstr(activity
, "away")) {
4291 status_id
= SIPE_STATUS_ID_AWAY
;
4296 status_id
= SIPE_STATUS_ID_AVAILABLE
;
4299 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
4300 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
4302 purple_prpl_got_user_status(sip
->account
, uri
, SIPE_STATUS_ID_OFFLINE
, NULL
);
4309 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4311 const char *availability
;
4312 const char *activity
;
4313 const char *display_name
= NULL
;
4314 const char *activity_name
= NULL
;
4319 struct sipe_buddy
*sbuddy
;
4321 xmlnode
*xn_presentity
= xmlnode_from_str(data
, len
);
4323 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
4324 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
4325 xmlnode
*xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
4326 xmlnode
*xn_email
= xmlnode_get_child(xn_presentity
, "email");
4327 const char *email
= xn_email
? xmlnode_get_attrib(xn_email
, "email") : NULL
;
4328 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
4329 xmlnode
*xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
):NULL
;
4330 const char *avail
= xn_state
? xmlnode_get_attrib(xn_state
, "avail") : NULL
;
4332 xmlnode
*xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
4333 char *note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
4334 xmlnode
*xn_devices
= xmlnode_get_child(xn_presentity
, "devices");
4335 xmlnode
*xn_device_presence
= xn_devices
? xmlnode_get_child(xn_devices
, "devicePresence") : NULL
;
4336 xmlnode
*xn_device_name
= xn_device_presence
? xmlnode_get_child(xn_device_presence
, "deviceName") : NULL
;
4337 const char *device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
4339 name
= xmlnode_get_attrib(xn_presentity
, "uri");
4340 uri
= g_strdup_printf("sip:%s", name
);
4341 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
4342 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
4344 // updating display name if alias was just URI
4345 if (xn_display_name
) {
4346 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
4347 GSList
*entry
= buddies
;
4348 PurpleBuddy
*p_buddy
;
4349 display_name
= xmlnode_get_attrib(xn_display_name
, "displayName");
4352 const char *email_str
, *server_alias
;
4354 p_buddy
= entry
->data
;
4356 if (!g_ascii_strcasecmp(name
, purple_buddy_get_alias(p_buddy
))) {
4357 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
4358 purple_blist_alias_buddy(p_buddy
, display_name
);
4361 server_alias
= purple_buddy_get_server_alias(p_buddy
);
4363 ( (server_alias
&& strcmp(display_name
, server_alias
))
4364 || !server_alias
|| strlen(server_alias
) == 0 )
4366 purple_blist_server_alias_buddy(p_buddy
, display_name
);
4370 email_str
= purple_blist_node_get_string((PurpleBlistNode
*)p_buddy
, "email");
4371 if (!email_str
|| g_ascii_strcasecmp(email_str
, email
)) {
4372 purple_blist_node_set_string((PurpleBlistNode
*)p_buddy
, "email", email
);
4376 entry
= entry
->next
;
4380 avl
= atoi(availability
);
4381 act
= atoi(activity
);
4383 if(sip
->msrtc_event_categories
){
4384 if (act
== 100 && avl
== 0)
4385 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4386 else if (act
== 100 && avl
== 300)
4387 activity_name
= SIPE_STATUS_ID_AWAY
;
4388 else if (act
== 300 && avl
== 300)
4389 activity_name
= SIPE_STATUS_ID_BRB
;
4390 else if (act
== 400 && avl
== 300)
4391 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4392 else if (act
== 500 && act
== 300)
4393 activity_name
= SIPE_STATUS_ID_ONPHONE
;
4394 else if (act
== 600 && avl
== 300)
4395 activity_name
= SIPE_STATUS_ID_BUSY
;
4396 else if (act
== 0 && avl
== 0){ //MSRTC elements are zero
4397 if(avail
){ //Check for LegacyInterop elements
4400 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4401 else if (avl
== 3500)
4402 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4403 else if (avl
== 15500)
4404 activity_name
= SIPE_STATUS_ID_AWAY
;
4405 else if (avl
== 6500)
4406 activity_name
= SIPE_STATUS_ID_BUSY
;
4407 else if (avl
== 12500)
4408 activity_name
= SIPE_STATUS_ID_BRB
;
4413 if(activity_name
== NULL
){
4415 activity_name
= SIPE_STATUS_ID_AWAY
;
4416 else if (act
<= 150)
4417 activity_name
= SIPE_STATUS_ID_LUNCH
;
4418 else if (act
<= 300)
4419 activity_name
= SIPE_STATUS_ID_BRB
;
4420 else if (act
<= 400)
4421 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4422 else if (act
<= 500)
4423 activity_name
= SIPE_STATUS_ID_ONPHONE
;
4424 else if (act
<= 600)
4425 activity_name
= SIPE_STATUS_ID_BUSY
;
4427 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4430 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4433 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4436 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
4437 sbuddy
->annotation
= NULL
;
4438 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
4440 if (sbuddy
->device_name
) { g_free(sbuddy
->device_name
); }
4441 sbuddy
->device_name
= NULL
;
4442 if (device_name
) { sbuddy
->device_name
= g_strdup(device_name
); }
4445 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
4446 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
4448 xmlnode_free(xn_presentity
);
4452 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4454 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4456 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
4458 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
4459 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
4461 const char *content
= msg
->body
;
4462 unsigned length
= msg
->bodylen
;
4463 PurpleMimeDocument
*mime
= NULL
;
4465 if (strstr(ctype
, "multipart"))
4467 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
4468 const char *content_type
;
4470 mime
= purple_mime_document_parse(doc
);
4471 parts
= purple_mime_document_get_parts(mime
);
4473 content
= purple_mime_part_get_data(parts
->data
);
4474 length
= purple_mime_part_get_length(parts
->data
);
4475 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
4476 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
4478 process_incoming_notify_rlmi_resub(sip
, content
, length
);
4480 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
4482 process_incoming_notify_msrtc(sip
, content
, length
);
4486 process_incoming_notify_rlmi(sip
, content
, length
);
4488 parts
= parts
->next
;
4494 purple_mime_document_free(mime
);
4497 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
4499 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
4501 else if(strstr(ctype
, "application/rlmi+xml"))
4503 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
4506 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
4508 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
4512 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
4516 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
4518 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4519 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
4521 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
4524 strstr(ctype
, "multipart") &&
4525 (strstr(ctype
, "application/rlmi+xml") ||
4526 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
4527 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
4528 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
4529 GList
*parts
= purple_mime_document_get_parts(mime
);
4530 GSList
*buddies
= NULL
;
4531 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4534 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
4535 purple_mime_part_get_length(parts
->data
));
4536 gchar
*uri
= g_strdup(xmlnode_get_attrib(xml
, "uri"));
4538 if (strstr(uri
, "sip:") == NULL
) {
4540 uri
= g_strdup_printf("sip:%s", tmp
);
4543 buddies
= g_slist_append(buddies
, uri
);
4546 parts
= parts
->next
;
4549 if (mime
) purple_mime_document_free(mime
);
4551 payload
->host
= who
;
4552 payload
->buddies
= buddies
;
4553 sipe_schedule_action(action_name
, timeout
,
4554 sipe_subscribe_presence_batched_routed
,
4555 sipe_subscribe_presence_batched_routed_free
,
4557 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
4560 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, NULL
, sip
, who
);
4561 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
4563 g_free(action_name
);
4567 * Dispatcher for all incoming subscription information
4568 * whether it comes from NOTIFY, BENOTIFY requests or
4569 * piggy-backed to subscription's OK responce.
4571 * @param request whether initiated from BE/NOTIFY request or OK-response message.
4572 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
4574 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
4576 gchar
*event
= sipmsg_find_header(msg
, "Event");
4577 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
4580 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event
? event
: "", msg
->body
);
4581 purple_debug_info("sipe", "process_incoming_notify: subscription_state:%s\n\n", subscription_state
);
4585 const gchar
*expires_header
;
4586 expires_header
= sipmsg_find_header(msg
, "Expires");
4587 timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
4588 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n\n", timeout
);
4589 timeout
= (timeout
- 60) > 60 ? (timeout
- 60) : timeout
; // 1 min ahead of expiration
4592 if (!subscription_state
|| strstr(subscription_state
, "active"))
4594 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
4596 sipe_process_presence(sip
, msg
);
4598 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
4600 sipe_process_roaming_contacts(sip
, msg
, NULL
);
4602 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self") )
4604 sipe_process_roaming_self(sip
, msg
);
4606 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
4608 sipe_process_roaming_acl(sip
, msg
);
4610 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
4612 sipe_process_presence_wpending(sip
, msg
);
4614 else if (event
&& !g_ascii_strcasecmp(event
, "conference"))
4616 sipe_process_conference(sip
, msg
);
4620 purple_debug_info("sipe", "Unable to process (BE)NOTIFY. Event is not supported:%s\n", event
? event
: "");
4624 //The server sends a (BE)NOTIFY with the status 'terminated'
4625 if (request
&& subscription_state
&& strstr(subscription_state
, "terminated") ) {
4626 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4627 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from
);
4631 if (timeout
&& event
) {// For LSC 2005 and OCS 2007
4632 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
4633 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
4635 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
4636 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_contacts, NULL, sip, msg);
4637 g_free(action_name);
4639 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
4640 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
4642 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
4643 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_acl, NULL, sip, msg);
4644 g_free(action_name);
4647 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
4648 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
4650 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
4651 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
4652 g_free(action_name
);
4654 else if (!g_ascii_strcasecmp(event
, "presence") &&
4655 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
4657 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
4658 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
4659 if(sip
->batched_support
) {
4660 gchar
*my_self
= g_strdup_printf("sip:%s",sip
->username
);
4661 if(!g_ascii_strcasecmp(who
, my_self
)){
4662 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_batched
, NULL
, sip
, NULL
);
4663 purple_debug_info("sipe", "Resubscription full batched list in %d\n",timeout
);
4664 g_free(who
); /* unused */
4667 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
4672 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, NULL
, sip
, who
);
4673 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
,timeout
);
4675 g_free(action_name
);
4676 /* "who" will be freed by the action we just scheduled */
4680 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
4682 sipe_process_registration_notify(sip
, msg
);
4685 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
4686 if (request
&& !benotify
)
4688 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4695 static gchar* gen_xpidf(struct sipe_account_data *sip)
4697 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4699 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
4700 "<display name=\"sip:%s\"/>\r\n"
4701 "<atom id=\"1234\">\r\n"
4702 "<address uri=\"sip:%s\">\r\n"
4703 "<status status=\"%s\"/>\r\n"
4716 static gchar* gen_pidf(struct sipe_account_data *sip)
4718 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4719 "<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"
4720 "<tuple id=\"0\">\r\n"
4722 "<basic>open</basic>\r\n"
4723 "<ep:activities>\r\n"
4724 " <ep:activity>%s</ep:activity>\r\n"
4728 "<ci:display-name>%s</ci:display-name>\r\n"
4737 static void send_presence_soap(struct sipe_account_data
*sip
, const char * note
)
4739 int availability
= 300; // online
4740 int activity
= 400; // Available
4743 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
)) {
4745 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4747 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4749 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4751 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4753 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4755 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_INVISIBLE
) ||
4756 !strcmp(sip
->status
, SIPE_STATUS_ID_OFFLINE
)) {
4757 availability
= 0; // offline
4760 activity
= 400; // available
4763 name
= g_strdup_printf("sip: sip:%s", sip
->username
);
4764 //@TODO: send user data - state; add hostname in upper case
4765 body
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
4766 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
4772 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
4774 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4775 if (msg
->response
== 200) {
4776 sip
->status_version
= 0;
4777 send_presence_status(sip
);
4783 process_send_presence_category_publish_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
4785 if (msg
->response
== 409) {
4786 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4787 // TODO need to parse the version #'s?
4788 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
4789 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
4793 purple_debug_info("sipe", "process_send_presence_category_publish_response = %s\n", msg
->body
);
4795 tmp
= get_contact(sip
);
4796 hdr
= g_strdup_printf("Contact: %s\r\n"
4797 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4799 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
4809 static void send_presence_category_publish(struct sipe_account_data
*sip
, const char * note
)
4816 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
) ||
4817 !strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4819 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_DND
)) {
4821 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4823 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4825 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4827 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4829 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_UNKNOWN
)) {
4832 // Offline or invisible
4836 uri
= g_strdup_printf("sip:%s", sip
->username
);
4837 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
4838 sip
->status_version
, code
,
4839 sip
->status_version
, code
,
4840 sip
->status_version
, note
? note
: "",
4841 sip
->status_version
, note
? note
: "",
4842 sip
->status_version
, note
? note
: ""
4844 sip
->status_version
++;
4846 tmp
= get_contact(sip
);
4847 hdr
= g_strdup_printf("Contact: %s\r\n"
4848 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4850 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
4858 static void send_presence_status(struct sipe_account_data
*sip
)
4860 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
4862 if (!status
) return;
4864 note
= purple_status_get_attr_string(status
, "message");
4866 if(sip
->msrtc_event_categories
){
4867 send_presence_category_publish(sip
, note
);
4869 send_presence_soap(sip
, note
);
4873 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
4875 gboolean found
= FALSE
;
4876 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
4877 if (msg
->response
== 0) { /* request */
4878 if (!strcmp(msg
->method
, "MESSAGE")) {
4879 process_incoming_message(sip
, msg
);
4881 } else if (!strcmp(msg
->method
, "NOTIFY")) {
4882 purple_debug_info("sipe","send->process_incoming_notify\n");
4883 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
4885 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
4886 purple_debug_info("sipe","send->process_incoming_benotify\n");
4887 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
4889 } else if (!strcmp(msg
->method
, "INVITE")) {
4890 process_incoming_invite(sip
, msg
);
4892 } else if (!strcmp(msg
->method
, "REFER")) {
4893 process_incoming_refer(sip
, msg
);
4895 } else if (!strcmp(msg
->method
, "OPTIONS")) {
4896 process_incoming_options(sip
, msg
);
4898 } else if (!strcmp(msg
->method
, "INFO")) {
4899 process_incoming_info(sip
, msg
);
4901 } else if (!strcmp(msg
->method
, "ACK")) {
4902 // ACK's don't need any response
4904 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
4905 // LCS 2005 sends us these - just respond 200 OK
4907 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4908 } else if (!strcmp(msg
->method
, "BYE")) {
4909 process_incoming_bye(sip
, msg
);
4912 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
4914 } else { /* response */
4915 struct transaction
*trans
= transactions_find(sip
, msg
);
4917 if (msg
->response
== 407) {
4918 gchar
*resend
, *auth
, *ptmp
;
4920 if (sip
->proxy
.retries
> 30) return;
4921 sip
->proxy
.retries
++;
4922 /* do proxy authentication */
4924 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
4926 fill_auth(sip
, ptmp
, &sip
->proxy
);
4927 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
4928 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
4929 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
4931 resend
= sipmsg_to_string(trans
->msg
);
4932 /* resend request */
4933 sendout_pkt(sip
->gc
, resend
);
4936 if (msg
->response
== 100 || msg
->response
== 180) {
4937 /* ignore provisional response */
4938 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
4940 sip
->proxy
.retries
= 0;
4941 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
4942 if (msg
->response
== 401)
4944 sip
->registrar
.retries
++;
4948 sip
->registrar
.retries
= 0;
4950 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
4952 if (msg
->response
== 401) {
4953 gchar
*resend
, *auth
, *ptmp
;
4955 if (sip
->registrar
.retries
> 4) return;
4956 sip
->registrar
.retries
++;
4959 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4961 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
4964 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4968 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
4970 fill_auth(sip
, ptmp
, &sip
->registrar
);
4971 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
4972 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
4973 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
4975 //sipmsg_remove_header_now(trans->msg, "Authorization");
4976 //sipmsg_add_header(trans->msg, "Authorization", auth);
4978 resend
= sipmsg_to_string(trans
->msg
);
4979 /* resend request */
4980 sendout_pkt(sip
->gc
, resend
);
4985 if (trans
->callback
) {
4986 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
4987 /* call the callback to process response*/
4988 (trans
->callback
)(sip
, msg
, trans
);
4990 /* Not sure if this is needed or what needs to be done
4991 but transactions seem to be removed prematurely so
4992 this only removes them if the response is 200 OK */
4993 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
4994 /*Has a bug and it's unneccesary*/
4995 /*transactions_remove(sip, trans);*/
5001 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
5005 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
5009 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
5017 /* according to the RFC remove CRLF at the beginning */
5018 while (*cur
== '\r' || *cur
== '\n') {
5021 if (cur
!= conn
->inbuf
) {
5022 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
5023 conn
->inbufused
= strlen(conn
->inbuf
);
5026 /* Received a full Header? */
5027 sip
->processing_input
= TRUE
;
5028 while (sip
->processing_input
&&
5029 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
5030 time_t currtime
= time(NULL
);
5033 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
5034 msg
= sipmsg_parse_header(conn
->inbuf
);
5037 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
5038 if (restlen
>= msg
->bodylen
) {
5039 dummy
= g_malloc(msg
->bodylen
+ 1);
5040 memcpy(dummy
, cur
, msg
->bodylen
);
5041 dummy
[msg
->bodylen
] = '\0';
5043 cur
+= msg
->bodylen
;
5044 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
5045 conn
->inbufused
= strlen(conn
->inbuf
);
5047 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
5048 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
5054 purple_debug_info("sipe", "body:\n%s", msg->body);
5057 // Verify the signature before processing it
5058 if (sip
->registrar
.gssapi_context
) {
5059 struct sipmsg_breakdown msgbd
;
5060 gchar
*signature_input_str
;
5063 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
5064 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
5066 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
5068 if (rspauth
!= NULL
) {
5069 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
5070 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
5071 process_input_message(sip
, msg
);
5073 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
5074 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
5075 sip
->gc
->wants_to_die
= TRUE
;
5077 } else if (msg
->response
== 401) {
5078 purple_connection_error(sip
->gc
, _("Wrong Password"));
5079 sip
->gc
->wants_to_die
= TRUE
;
5081 g_free(signature_input_str
);
5084 sipmsg_breakdown_free(&msgbd
);
5086 process_input_message(sip
, msg
);
5093 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
5095 PurpleConnection
*gc
= data
;
5096 struct sipe_account_data
*sip
= gc
->proto_data
;
5101 static char buffer
[65536];
5102 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
5104 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
5105 msg
= sipmsg_parse_msg(buffer
);
5106 if (msg
) process_input_message(sip
, msg
);
5110 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
5112 struct sipe_account_data
*sip
= gc
->proto_data
;
5113 PurpleSslConnection
*gsc
= sip
->gsc
;
5115 purple_debug_error("sipe", "%s",debug
);
5116 purple_connection_error(gc
, msg
);
5118 /* Invalidate this connection. Next send will open a new one */
5120 connection_remove(sip
, gsc
->fd
);
5121 purple_ssl_close(gsc
);
5127 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
5129 PurpleConnection
*gc
= data
;
5130 struct sipe_account_data
*sip
;
5131 struct sip_connection
*conn
;
5133 gboolean firstread
= TRUE
;
5135 /* NOTE: This check *IS* necessary */
5136 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
5137 purple_ssl_close(gsc
);
5141 sip
= gc
->proto_data
;
5142 conn
= connection_find(sip
, gsc
->fd
);
5144 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
5145 gc
->wants_to_die
= TRUE
;
5146 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
5150 /* Read all available data from the SSL connection */
5152 /* Increase input buffer size as needed */
5153 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
5154 conn
->inbuflen
+= SIMPLE_BUF_INC
;
5155 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
5156 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
5159 /* Try to read as much as there is space left in the buffer */
5160 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
5161 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
5163 if (len
< 0 && errno
== EAGAIN
) {
5164 /* Try again later */
5166 } else if (len
< 0) {
5167 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
5169 } else if (firstread
&& (len
== 0)) {
5170 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
5174 conn
->inbufused
+= len
;
5177 /* Equivalence indicates that there is possibly more data to read */
5178 } while (len
== readlen
);
5180 conn
->inbuf
[conn
->inbufused
] = '\0';
5181 process_input(sip
, conn
);
5185 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
5187 PurpleConnection
*gc
= data
;
5188 struct sipe_account_data
*sip
= gc
->proto_data
;
5190 struct sip_connection
*conn
= connection_find(sip
, source
);
5192 purple_debug_error("sipe", "Connection not found!\n");
5196 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
5197 conn
->inbuflen
+= SIMPLE_BUF_INC
;
5198 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
5201 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
5203 if (len
< 0 && errno
== EAGAIN
)
5205 else if (len
<= 0) {
5206 purple_debug_info("sipe", "sipe_input_cb: read error\n");
5207 connection_remove(sip
, source
);
5208 if (sip
->fd
== source
) sip
->fd
= -1;
5212 conn
->inbufused
+= len
;
5213 conn
->inbuf
[conn
->inbufused
] = '\0';
5215 process_input(sip
, conn
);
5218 /* Callback for new connections on incoming TCP port */
5219 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
5221 PurpleConnection
*gc
= data
;
5222 struct sipe_account_data
*sip
= gc
->proto_data
;
5223 struct sip_connection
*conn
;
5225 int newfd
= accept(source
, NULL
, NULL
);
5227 conn
= connection_create(sip
, newfd
);
5229 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
5232 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
5234 PurpleConnection
*gc
= data
;
5235 struct sipe_account_data
*sip
;
5236 struct sip_connection
*conn
;
5238 if (!PURPLE_CONNECTION_IS_VALID(gc
))
5246 purple_connection_error(gc
, _("Could not connect"));
5250 sip
= gc
->proto_data
;
5252 sip
->last_keepalive
= time(NULL
);
5254 conn
= connection_create(sip
, source
);
5258 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
5261 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
5263 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
5264 if (sip
== NULL
) return;
5269 static guint
sipe_ht_hash_nick(const char *nick
)
5271 char *lc
= g_utf8_strdown(nick
, -1);
5272 guint bucket
= g_str_hash(lc
);
5278 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
5280 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
5283 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
5285 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5287 sip
->listen_data
= NULL
;
5289 if (listenfd
== -1) {
5290 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5296 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
5297 sip
->listenfd
= sip
->fd
;
5299 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
5301 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
5305 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
5307 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5309 sip
->query_data
= NULL
;
5311 if (!hosts
|| !hosts
->data
) {
5312 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
5316 hosts
= g_slist_remove(hosts
, hosts
->data
);
5317 g_free(sip
->serveraddr
);
5318 sip
->serveraddr
= hosts
->data
;
5319 hosts
= g_slist_remove(hosts
, hosts
->data
);
5321 hosts
= g_slist_remove(hosts
, hosts
->data
);
5322 g_free(hosts
->data
);
5323 hosts
= g_slist_remove(hosts
, hosts
->data
);
5326 /* create socket for incoming connections */
5327 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
5328 sipe_udp_host_resolved_listen_cb
, sip
);
5329 if (sip
->listen_data
== NULL
) {
5330 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5335 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
5338 PurpleConnection
*gc
= data
;
5339 struct sipe_account_data
*sip
;
5341 /* If the connection is already disconnected, we don't need to do anything else */
5342 if (!PURPLE_CONNECTION_IS_VALID(gc
))
5345 sip
= gc
->proto_data
;
5350 case PURPLE_SSL_CONNECT_FAILED
:
5351 purple_connection_error(gc
, _("Connection Failed"));
5353 case PURPLE_SSL_HANDSHAKE_FAILED
:
5354 purple_connection_error(gc
, _("SSL Handshake Failed"));
5356 case PURPLE_SSL_CERTIFICATE_INVALID
:
5357 purple_connection_error(gc
, _("SSL Certificate Invalid"));
5363 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
5365 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5366 PurpleProxyConnectData
*connect_data
;
5368 sip
->listen_data
= NULL
;
5370 sip
->listenfd
= listenfd
;
5371 if (sip
->listenfd
== -1) {
5372 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5376 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
5377 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
5378 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
5379 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
5380 sipe_newconn_cb
, sip
->gc
);
5381 purple_debug_info("sipe", "connecting to %s port %d\n",
5382 sip
->realhostname
, sip
->realport
);
5383 /* open tcp connection to the server */
5384 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
5385 sip
->realport
, login_cb
, sip
->gc
);
5387 if (connect_data
== NULL
) {
5388 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
5393 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
5395 PurpleAccount
*account
= sip
->account
;
5396 PurpleConnection
*gc
= sip
->gc
;
5398 if (purple_account_get_bool(account
, "useport", FALSE
)) {
5399 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
5400 port
= purple_account_get_int(account
, "port", 0);
5402 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
5405 sip
->realhostname
= hostname
;
5406 sip
->realport
= port
;
5408 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
5411 /* TODO: is there a good default grow size? */
5412 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
5413 sip
->txbuf
= purple_circ_buffer_new(0);
5415 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
5417 if (!purple_ssl_is_supported()) {
5418 gc
->wants_to_die
= TRUE
;
5419 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
5423 purple_debug_info("sipe", "using SSL\n");
5425 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
5426 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
5427 if (sip
->gsc
== NULL
) {
5428 purple_connection_error(gc
, _("Could not create SSL context"));
5431 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
5433 purple_debug_info("sipe", "using UDP\n");
5435 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
5436 if (sip
->query_data
== NULL
) {
5437 purple_connection_error(gc
, _("Could not resolve hostname"));
5441 purple_debug_info("sipe", "using TCP\n");
5442 /* create socket for incoming connections */
5443 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
5444 sipe_tcp_connect_listen_cb
, sip
);
5445 if (sip
->listen_data
== NULL
) {
5446 purple_connection_error(gc
, _("Could not create listen socket"));
5452 /* Service list for autodection */
5453 static const struct sipe_service_data service_autodetect
[] = {
5454 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
5455 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
5456 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
5457 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
5461 /* Service list for SSL/TLS */
5462 static const struct sipe_service_data service_tls
[] = {
5463 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
5464 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
5468 /* Service list for TCP */
5469 static const struct sipe_service_data service_tcp
[] = {
5470 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
5471 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
5475 /* Service list for UDP */
5476 static const struct sipe_service_data service_udp
[] = {
5477 { "sip", "udp", SIPE_TRANSPORT_UDP
},
5481 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
5482 static void resolve_next_service(struct sipe_account_data
*sip
,
5483 const struct sipe_service_data
*start
)
5486 sip
->service_data
= start
;
5488 sip
->service_data
++;
5489 if (sip
->service_data
->service
== NULL
) {
5491 /* Try connecting to the SIP hostname directly */
5492 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
5493 if (sip
->auto_transport
) {
5494 // If SSL is supported, default to using it; OCS servers aren't configured
5495 // by default to accept TCP
5496 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
5497 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
5498 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
5501 hostname
= g_strdup(sip
->sipdomain
);
5502 create_connection(sip
, hostname
, 0);
5507 /* Try to resolve next service */
5508 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
5509 sip
->service_data
->transport
,
5514 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
5516 struct sipe_account_data
*sip
= data
;
5518 sip
->srv_query_data
= NULL
;
5520 /* find the host to connect to */
5522 gchar
*hostname
= g_strdup(resp
->hostname
);
5523 int port
= resp
->port
;
5524 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
5528 sip
->transport
= sip
->service_data
->type
;
5530 create_connection(sip
, hostname
, port
);
5532 resolve_next_service(sip
, NULL
);
5536 static void sipe_login(PurpleAccount
*account
)
5538 PurpleConnection
*gc
;
5539 struct sipe_account_data
*sip
;
5540 gchar
**signinname_login
, **userserver
, **domain_user
;
5541 const char *transport
;
5543 const char *username
= purple_account_get_username(account
);
5544 gc
= purple_account_get_connection(account
);
5546 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
5547 gc
->wants_to_die
= TRUE
;
5548 purple_connection_error(gc
, _("SIP Exchange username contains invalid characters"));
5552 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
5553 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
5554 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
5556 sip
->account
= account
;
5557 sip
->reregister_set
= FALSE
;
5558 sip
->reauthenticate_set
= FALSE
;
5559 sip
->subscribed
= FALSE
;
5560 sip
->subscribed_buddies
= FALSE
;
5562 signinname_login
= g_strsplit(username
, ",", 2);
5564 userserver
= g_strsplit(signinname_login
[0], "@", 2);
5565 purple_connection_set_display_name(gc
, userserver
[0]);
5566 sip
->username
= g_strjoin("@", userserver
[0], userserver
[1], NULL
);
5567 sip
->sipdomain
= g_strdup(userserver
[1]);
5569 if (strpbrk(sip
->username
, " \t\v\r\n") != NULL
) {
5570 gc
->wants_to_die
= TRUE
;
5571 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
5575 domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
5576 sip
->authdomain
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[0]) : NULL
;
5577 sip
->authuser
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[1]) : (signinname_login
? g_strdup(signinname_login
[1]) : NULL
);
5579 sip
->password
= g_strdup(purple_connection_get_password(gc
));
5581 g_strfreev(userserver
);
5582 g_strfreev(domain_user
);
5583 g_strfreev(signinname_login
);
5585 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
5587 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
5589 /* TODO: Set the status correctly. */
5590 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
5592 transport
= purple_account_get_string(account
, "transport", "auto");
5593 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
5594 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
5597 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
5598 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
5599 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
5600 } else if (strcmp(transport
, "auto") == 0) {
5601 sip
->auto_transport
= TRUE
;
5602 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
5603 } else if (strcmp(transport
, "tls") == 0) {
5604 resolve_next_service(sip
, service_tls
);
5605 } else if (strcmp(transport
, "tcp") == 0) {
5606 resolve_next_service(sip
, service_tcp
);
5608 resolve_next_service(sip
, service_udp
);
5612 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
5614 connection_free_all(sip
);
5619 if (sip
->query_data
!= NULL
)
5620 purple_dnsquery_destroy(sip
->query_data
);
5621 sip
->query_data
= NULL
;
5623 if (sip
->srv_query_data
!= NULL
)
5624 purple_srv_cancel(sip
->srv_query_data
);
5625 sip
->srv_query_data
= NULL
;
5627 if (sip
->listen_data
!= NULL
)
5628 purple_network_listen_cancel(sip
->listen_data
);
5629 sip
->listen_data
= NULL
;
5631 if (sip
->gsc
!= NULL
)
5632 purple_ssl_close(sip
->gsc
);
5635 sipe_auth_free(&sip
->registrar
);
5636 sipe_auth_free(&sip
->proxy
);
5639 purple_circ_buffer_destroy(sip
->txbuf
);
5642 g_free(sip
->realhostname
);
5643 sip
->realhostname
= NULL
;
5646 purple_input_remove(sip
->listenpa
);
5648 if (sip
->tx_handler
)
5649 purple_input_remove(sip
->tx_handler
);
5650 sip
->tx_handler
= 0;
5651 if (sip
->resendtimeout
)
5652 purple_timeout_remove(sip
->resendtimeout
);
5653 sip
->resendtimeout
= 0;
5654 if (sip
->timeouts
) {
5655 GSList
*entry
= sip
->timeouts
;
5657 struct scheduled_action
*sched_action
= entry
->data
;
5658 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
5659 purple_timeout_remove(sched_action
->timeout_handler
);
5660 (*sched_action
->destroy
)(sched_action
->payload
);
5661 g_free(sched_action
->name
);
5662 g_free(sched_action
);
5663 entry
= entry
->next
;
5666 g_slist_free(sip
->timeouts
);
5668 if (sip
->allow_events
) {
5669 GSList
*entry
= sip
->allow_events
;
5671 g_free(entry
->data
);
5672 entry
= entry
->next
;
5675 g_slist_free(sip
->allow_events
);
5677 if (sip
->containers
) {
5678 GSList
*entry
= sip
->containers
;
5680 free_container((struct sipe_container
*)entry
->data
);
5681 entry
= entry
->next
;
5684 g_slist_free(sip
->containers
);
5687 g_free(sip
->contact
);
5688 sip
->contact
= NULL
;
5690 g_free(sip
->regcallid
);
5691 sip
->regcallid
= NULL
;
5693 if (sip
->serveraddr
)
5694 g_free(sip
->serveraddr
);
5695 sip
->serveraddr
= NULL
;
5698 sip
->processing_input
= FALSE
;
5702 * A callback for g_hash_table_foreach_remove
5704 static gboolean
sipe_buddy_remove(gpointer key
, gpointer buddy
, gpointer user_data
)
5706 sipe_free_buddy((struct sipe_buddy
*) buddy
);
5708 /* We must return TRUE as the key/value have already been deleted */
5712 static void sipe_close(PurpleConnection
*gc
)
5714 struct sipe_account_data
*sip
= gc
->proto_data
;
5717 /* leave all conversations */
5718 im_session_close_all(sip
);
5721 do_register_exp(sip
, 0);
5723 sipe_connection_cleanup(sip
);
5724 g_free(sip
->sipdomain
);
5725 g_free(sip
->username
);
5726 g_free(sip
->password
);
5727 g_free(sip
->authdomain
);
5728 g_free(sip
->authuser
);
5729 g_free(sip
->status
);
5731 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
5732 g_hash_table_destroy(sip
->buddies
);
5734 g_free(gc
->proto_data
);
5735 gc
->proto_data
= NULL
;
5738 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
5740 PurpleAccount
*acct
= purple_connection_get_account(gc
);
5741 char *id
= g_strdup_printf("sip:%s", (char *)g_list_nth_data(row
, 0));
5742 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
5744 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
5745 purple_conversation_present(conv
);
5749 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
5752 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
5753 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
5756 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,struct transaction
*tc
)
5758 PurpleNotifySearchResults
*results
;
5759 PurpleNotifySearchColumn
*column
;
5760 xmlnode
*searchResults
;
5762 int match_count
= 0;
5763 gboolean more
= FALSE
;
5766 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
5768 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5769 if (!searchResults
) {
5770 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
5774 results
= purple_notify_searchresults_new();
5776 if (results
== NULL
) {
5777 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
5778 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
5780 xmlnode_free(searchResults
);
5784 column
= purple_notify_searchresults_column_new(_("User Name"));
5785 purple_notify_searchresults_column_add(results
, column
);
5787 column
= purple_notify_searchresults_column_new(_("Name"));
5788 purple_notify_searchresults_column_add(results
, column
);
5790 column
= purple_notify_searchresults_column_new(_("Company"));
5791 purple_notify_searchresults_column_add(results
, column
);
5793 column
= purple_notify_searchresults_column_new(_("Country"));
5794 purple_notify_searchresults_column_add(results
, column
);
5796 column
= purple_notify_searchresults_column_new(_("Email"));
5797 purple_notify_searchresults_column_add(results
, column
);
5799 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
5802 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
5803 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
5804 g_strfreev(uri_parts
);
5806 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
5807 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
5808 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
5809 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
5811 purple_notify_searchresults_row_add(results
, row
);
5815 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
5816 char *data
= xmlnode_get_data_unescaped(mrow
);
5817 more
= (g_strcasecmp(data
, "true") == 0);
5821 secondary
= g_strdup_printf(
5822 dngettext(GETTEXT_PACKAGE
,
5823 "Found %d contact%s:",
5824 "Found %d contacts%s:", match_count
),
5825 match_count
, more
? _(" (more matched your query)") : "");
5827 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
5828 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
5829 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
5832 xmlnode_free(searchResults
);
5836 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
5838 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
5839 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
5843 PurpleRequestField
*field
= entries
->data
;
5844 const char *id
= purple_request_field_get_id(field
);
5845 const char *value
= purple_request_field_string_get_value(field
);
5847 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
5849 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
5850 } while ((entries
= g_list_next(entries
)) != NULL
);
5854 gchar
*query
= g_strjoinv(NULL
, attrs
);
5855 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
5856 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
5857 send_soap_request_with_cb(gc
->proto_data
, body
,
5858 (TransCallback
) process_search_contact_response
, NULL
);
5866 static void sipe_show_find_contact(PurplePluginAction
*action
)
5868 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
5869 PurpleRequestFields
*fields
;
5870 PurpleRequestFieldGroup
*group
;
5871 PurpleRequestField
*field
;
5873 fields
= purple_request_fields_new();
5874 group
= purple_request_field_group_new(NULL
);
5875 purple_request_fields_add_group(fields
, group
);
5877 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
5878 purple_request_field_group_add_field(group
, field
);
5879 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
5880 purple_request_field_group_add_field(group
, field
);
5881 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
5882 purple_request_field_group_add_field(group
, field
);
5883 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
5884 purple_request_field_group_add_field(group
, field
);
5886 purple_request_fields(gc
,
5888 _("Search for a Contact"),
5889 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
5891 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
5893 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
5896 GList
*sipe_actions(PurplePlugin
*plugin
, gpointer context
)
5899 PurplePluginAction
*act
;
5901 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
5902 menu
= g_list_prepend(menu
, act
);
5904 menu
= g_list_reverse(menu
);
5909 static void dummy_permit_deny(PurpleConnection
*gc
)
5913 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
5919 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
5925 static char *sipe_status_text(PurpleBuddy
*buddy
)
5927 struct sipe_account_data
*sip
;
5928 struct sipe_buddy
*sbuddy
;
5931 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
5932 if (sip
) //happens on pidgin exit
5934 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
5935 if (sbuddy
&& sbuddy
->annotation
)
5937 text
= g_strdup(sbuddy
->annotation
);
5944 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
5946 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
5947 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
5948 struct sipe_account_data
*sip
;
5949 struct sipe_buddy
*sbuddy
;
5950 char *annotation
= NULL
;
5952 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
5953 if (sip
) //happens on pidgin exit
5955 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
5958 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
5963 if (purple_presence_is_online(presence
))
5965 purple_notify_user_info_add_pair(user_info
, _("Status"), purple_status_get_name(status
));
5970 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
5977 sipe_get_account_text_table(PurpleAccount
*account
)
5980 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
5981 g_hash_table_insert(table
, "login_label", (gpointer
)_("Sign-In Name..."));
5985 static PurpleBuddy
*
5986 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
5989 const gchar
*server_alias
, *email
;
5990 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
5992 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
5994 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
5996 server_alias
= g_strdup(purple_buddy_get_server_alias(buddy
));
5998 purple_blist_server_alias_buddy(clone
, server_alias
);
6001 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
6003 purple_blist_node_set_string((PurpleBlistNode
*)clone
, "email", email
);
6006 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
6008 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
6013 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
6015 PurpleBuddy
*buddy
, *b
;
6016 PurpleConnection
*gc
;
6017 PurpleGroup
* group
= purple_find_group(group_name
);
6019 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
6021 buddy
= (PurpleBuddy
*)node
;
6023 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
6024 gc
= purple_account_get_connection(buddy
->account
);
6026 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
6028 b
= purple_blist_add_buddy_clone(group
, buddy
);
6031 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
6035 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
6037 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6038 gchar
*self
= g_strdup_printf("sip:%s", sip
->username
);
6039 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++sip
->chat_seq
);
6040 struct sip_im_session
*session
;
6042 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: buddy->name=%s\n", buddy
->name
);
6044 session
= create_chat_session(sip
);
6045 session
->roster_manager
= g_strdup(self
);
6047 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, g_strdup(chat_name
));
6048 session
->chat_name
= g_strdup(chat_name
);
6049 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
6050 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, FALSE
);
6057 sipe_is_election_finished(struct sipe_account_data
*sip
,
6058 struct sip_im_session
*session
)
6060 gboolean res
= TRUE
;
6062 SIPE_DIALOG_FOREACH
{
6063 if (dialog
->election_vote
== 0) {
6067 } SIPE_DIALOG_FOREACH_END
;
6070 session
->is_voting_in_progress
= FALSE
;
6076 sipe_election_start(struct sipe_account_data
*sip
,
6077 struct sip_im_session
*session
)
6079 int election_timeout
;
6081 if (session
->is_voting_in_progress
) {
6082 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
6085 session
->is_voting_in_progress
= TRUE
;
6087 session
->bid
= rand();
6089 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
6091 SIPE_DIALOG_FOREACH
{
6092 /* reset election_vote for each chat participant */
6093 dialog
->election_vote
= 0;
6095 /* send RequestRM to each chat participant*/
6096 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
6097 } SIPE_DIALOG_FOREACH_END
;
6099 election_timeout
= 15; /* sec */
6100 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
6104 * @param who a URI to whom to invite to chat
6107 sipe_invite_to_chat(struct sipe_account_data
*sip
,
6108 struct sip_im_session
*session
,
6111 gchar
*self
= g_strdup_printf("sip:%s", sip
->username
);
6113 if (session
->roster_manager
) {
6114 if (!strcmp(session
->roster_manager
, self
)) {
6115 sipe_invite(sip
, session
, who
, NULL
, NULL
, FALSE
);
6117 sipe_refer(sip
, session
, who
);
6120 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: no RM available\n");
6122 session
->pending_invite_queue
= slist_insert_unique_sorted(
6123 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
6125 sipe_election_start(sip
, session
);
6132 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
6133 struct sip_im_session
*session
)
6136 GSList
*entry
= session
->pending_invite_queue
;
6139 invitee
= entry
->data
;
6140 sipe_invite_to_chat(sip
, session
, invitee
);
6141 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
6147 sipe_election_result(struct sipe_account_data
*sip
,
6150 struct sip_im_session
*session
= (struct sip_im_session
*)sess
;
6152 gboolean has_won
= TRUE
;
6154 if (session
->roster_manager
) {
6155 purple_debug_info("sipe",
6156 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
6160 session
->is_voting_in_progress
= FALSE
;
6162 SIPE_DIALOG_FOREACH
{
6163 if (dialog
->election_vote
< 0) {
6165 rival
= dialog
->with
;
6168 } SIPE_DIALOG_FOREACH_END
;
6171 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
6173 session
->roster_manager
= g_strdup_printf("sip:%s", sip
->username
);
6175 SIPE_DIALOG_FOREACH
{
6176 /* send SetRM to each chat participant*/
6177 sipe_send_election_set_rm(sip
, dialog
);
6178 } SIPE_DIALOG_FOREACH_END
;
6180 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
6184 sipe_process_pending_invite_queue(sip
, session
);
6188 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, const char *chat_name
)
6190 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6191 struct sip_im_session
*session
;
6193 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: buddy->name=%s\n", buddy
->name
);
6194 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: chat_name=%s\n", chat_name
);
6196 session
= find_chat_session_by_name(sip
, chat_name
);
6198 sipe_invite_to_chat(sip
, session
, buddy
->name
);
6202 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
6205 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
6207 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
6210 char *mailto
= g_strdup_printf("mailto:%s", email
);
6211 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
6215 char *const parmList
[] = {mailto
, NULL
};
6216 if ((pid
= fork()) == -1)
6218 purple_debug_info("sipe", "fork() error\n");
6222 execvp("xdg-email", parmList
);
6223 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
6231 //@TODO resolve env variable %WINDIR% first
6232 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
6235 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
6244 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
6249 * A menu which appear when right-clicking on buddy in contact list.
6252 sipe_buddy_menu(PurpleBuddy
*buddy
)
6254 PurpleBlistNode
*g_node
;
6255 PurpleGroup
*group
, *gr_parent
;
6256 PurpleMenuAction
*act
;
6258 GList
*menu_groups
= NULL
;
6259 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6260 struct sip_im_session
*session
;
6262 gchar
*self
= g_strdup_printf("sip:%s", sip
->username
);
6264 act
= purple_menu_action_new(_("New Chat"),
6265 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
6267 menu
= g_list_prepend(menu
, act
);
6269 entry
= sip
->im_sessions
;
6271 session
= entry
->data
;
6272 if (strcmp(self
, buddy
->name
) && session
->chat_name
&&
6273 !sipe_dialog_find(session
, buddy
->name
)) {
6274 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_name
);
6275 act
= purple_menu_action_new(label
,
6276 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
6277 g_strdup(session
->chat_name
), NULL
);
6279 menu
= g_list_prepend(menu
, act
);
6281 entry
= entry
->next
;
6284 act
= purple_menu_action_new(_("Send Email..."),
6285 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
6287 menu
= g_list_prepend(menu
, act
);
6289 gr_parent
= purple_buddy_get_group(buddy
);
6290 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
6291 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
6294 group
= (PurpleGroup
*)g_node
;
6295 if (group
== gr_parent
)
6298 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
6301 act
= purple_menu_action_new(purple_group_get_name(group
),
6302 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
6304 menu_groups
= g_list_prepend(menu_groups
, act
);
6306 menu_groups
= g_list_reverse(menu_groups
);
6308 act
= purple_menu_action_new(_("Copy to"),
6311 menu
= g_list_prepend(menu
, act
);
6312 menu
= g_list_reverse(menu
);
6319 sipe_blist_node_menu(PurpleBlistNode
*node
)
6321 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
6322 return sipe_buddy_menu((PurpleBuddy
*) node
);
6329 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
6331 gboolean ret
= TRUE
;
6332 char *username
= (char *)trans
->payload
;
6334 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
6335 PurpleBuddy
*pbuddy
;
6336 struct sipe_buddy
*sbuddy
;
6338 char *server_alias
= NULL
;
6340 const char *device_name
= NULL
;
6342 purple_debug_info("sipe", "Fetching %s's user info for %s\n", username
, sip
->username
);
6344 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, username
);
6345 alias
= purple_buddy_get_local_alias(pbuddy
);
6349 //will query buddy UA's capabilities and send answer to log
6350 sipe_options_request(sip
, username
);
6352 sbuddy
= g_hash_table_lookup(sip
->buddies
, username
);
6355 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
6359 if (msg
->response
!= 200) {
6360 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
6362 xmlnode
*searchResults
;
6365 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
6366 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
6367 if (!searchResults
) {
6368 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
6369 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
6370 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
6371 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
6372 purple_notify_user_info_add_pair(info
, _("Job Title"), g_strdup(xmlnode_get_attrib(mrow
, "title")));
6373 purple_notify_user_info_add_pair(info
, _("Office"), g_strdup(xmlnode_get_attrib(mrow
, "office")));
6374 purple_notify_user_info_add_pair(info
, _("Business Phone"), g_strdup(xmlnode_get_attrib(mrow
, "phone")));
6375 purple_notify_user_info_add_pair(info
, _("Company"), g_strdup(xmlnode_get_attrib(mrow
, "company")));
6376 purple_notify_user_info_add_pair(info
, _("City"), g_strdup(xmlnode_get_attrib(mrow
, "city")));
6377 purple_notify_user_info_add_pair(info
, _("State"), g_strdup(xmlnode_get_attrib(mrow
, "state")));
6378 purple_notify_user_info_add_pair(info
, _("Country"), g_strdup(xmlnode_get_attrib(mrow
, "country")));
6379 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
6380 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
6381 if (!email
|| strcmp("", email
)) {
6382 if (!purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email")) {
6383 purple_blist_node_set_string((PurpleBlistNode
*)pbuddy
, "email", email
);
6387 xmlnode_free(searchResults
);
6390 purple_notify_user_info_add_section_break(info
);
6392 if (!server_alias
|| !strcmp("", server_alias
)) {
6393 g_free(server_alias
);
6394 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
6396 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
6400 // same as server alias, do not present
6401 alias
= (alias
&& server_alias
&& !strcmp(alias
, server_alias
)) ? NULL
: alias
;
6404 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
6407 if (!email
|| !strcmp("", email
)) {
6409 email
= g_strdup(purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email"));
6411 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
6417 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
6420 /* show a buddy's user info in a nice dialog box */
6421 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
6422 username
, /* buddy's username */
6424 NULL
, /* callback called when dialog closed */
6425 NULL
); /* userdata for callback */
6431 * AD search first, LDAP based
6433 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
6435 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
6436 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
6438 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
6439 send_soap_request_with_cb((struct sipe_account_data
*)gc
->proto_data
, body
,
6440 (TransCallback
) process_get_info_response
, (gpointer
)g_strdup(username
));
6445 static PurplePlugin
*my_protocol
= NULL
;
6447 static PurplePluginProtocolInfo prpl_info
=
6450 NULL
, /* user_splits */
6451 NULL
, /* protocol_options */
6452 NO_BUDDY_ICONS
, /* icon_spec */
6453 sipe_list_icon
, /* list_icon */
6454 NULL
, /* list_emblems */
6455 sipe_status_text
, /* status_text */
6456 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
6457 sipe_status_types
, /* away_states */
6458 sipe_blist_node_menu
, /* blist_node_menu */
6459 NULL
, /* chat_info */
6460 NULL
, /* chat_info_defaults */
6461 sipe_login
, /* login */
6462 sipe_close
, /* close */
6463 sipe_im_send
, /* send_im */
6464 NULL
, /* set_info */ // TODO maybe
6465 sipe_send_typing
, /* send_typing */
6466 sipe_get_info
, /* get_info */
6467 sipe_set_status
, /* set_status */
6468 NULL
, /* set_idle */
6469 NULL
, /* change_passwd */
6470 sipe_add_buddy
, /* add_buddy */
6471 NULL
, /* add_buddies */
6472 sipe_remove_buddy
, /* remove_buddy */
6473 NULL
, /* remove_buddies */
6474 sipe_add_permit
, /* add_permit */
6475 sipe_add_deny
, /* add_deny */
6476 sipe_add_deny
, /* rem_permit */
6477 sipe_add_permit
, /* rem_deny */
6478 dummy_permit_deny
, /* set_permit_deny */
6479 NULL
, /* join_chat */
6480 NULL
, /* reject_chat */
6481 NULL
, /* get_chat_name */
6482 NULL
, /* chat_invite */
6483 sipe_chat_leave
, /* chat_leave */
6484 NULL
, /* chat_whisper */
6485 sipe_chat_send
, /* chat_send */
6486 sipe_keep_alive
, /* keepalive */
6487 NULL
, /* register_user */
6488 NULL
, /* get_cb_info */ // deprecated
6489 NULL
, /* get_cb_away */ // deprecated
6490 sipe_alias_buddy
, /* alias_buddy */
6491 sipe_group_buddy
, /* group_buddy */
6492 sipe_rename_group
, /* rename_group */
6493 NULL
, /* buddy_free */
6494 sipe_convo_closed
, /* convo_closed */
6495 purple_normalize_nocase
, /* normalize */
6496 NULL
, /* set_buddy_icon */
6497 sipe_remove_group
, /* remove_group */
6498 NULL
, /* get_cb_real_name */ // TODO?
6499 NULL
, /* set_chat_topic */
6500 NULL
, /* find_blist_chat */
6501 NULL
, /* roomlist_get_list */
6502 NULL
, /* roomlist_cancel */
6503 NULL
, /* roomlist_expand_category */
6504 NULL
, /* can_receive_file */
6505 NULL
, /* send_file */
6506 NULL
, /* new_xfer */
6507 NULL
, /* offline_message */
6508 NULL
, /* whiteboard_prpl_ops */
6509 sipe_send_raw
, /* send_raw */
6510 NULL
, /* roomlist_room_serialize */
6511 NULL
, /* unregister_user */
6512 NULL
, /* send_attention */
6513 NULL
, /* get_attention_types */
6515 sizeof(PurplePluginProtocolInfo
), /* struct_size */
6516 sipe_get_account_text_table
, /* get_account_text_table */
6520 static PurplePluginInfo info
= {
6521 PURPLE_PLUGIN_MAGIC
,
6522 PURPLE_MAJOR_VERSION
,
6523 PURPLE_MINOR_VERSION
,
6524 PURPLE_PLUGIN_PROTOCOL
, /**< type */
6525 NULL
, /**< ui_requirement */
6527 NULL
, /**< dependencies */
6528 PURPLE_PRIORITY_DEFAULT
, /**< priority */
6529 "prpl-sipe", /**< id */
6530 "Microsoft LCS/OCS", /**< name */
6531 VERSION
, /**< version */
6532 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
6533 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
6534 "Anibal Avelar <avelar@gmail.com>, " /**< author */
6535 "Gabriel Burt <gburt@novell.com>", /**< author */
6536 PURPLE_WEBSITE
, /**< homepage */
6537 sipe_plugin_load
, /**< load */
6538 sipe_plugin_unload
, /**< unload */
6539 sipe_plugin_destroy
, /**< destroy */
6540 NULL
, /**< ui_info */
6541 &prpl_info
, /**< extra_info */
6550 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
6554 entry
= prpl_info
.protocol_options
;
6556 purple_account_option_destroy(entry
->data
);
6557 entry
= g_list_delete_link(entry
, entry
);
6559 prpl_info
.protocol_options
= NULL
;
6561 entry
= prpl_info
.user_splits
;
6563 purple_account_user_split_destroy(entry
->data
);
6564 entry
= g_list_delete_link(entry
, entry
);
6566 prpl_info
.user_splits
= NULL
;
6569 static void init_plugin(PurplePlugin
*plugin
)
6571 PurpleAccountUserSplit
*split
;
6572 PurpleAccountOption
*option
;
6577 purple_debug_info(PACKAGE
, "bindtextdomain = %s\n", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
6578 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s\n",
6579 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
6580 textdomain(GETTEXT_PACKAGE
);
6583 purple_plugin_register(plugin
);
6585 split
= purple_account_user_split_new(_("Login \n domain\\user or\n someone@linux.com "), NULL
, ',');
6586 purple_account_user_split_set_reverse(split
, FALSE
);
6587 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
6589 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
6590 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6591 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
6592 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6594 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
6595 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6596 // Translators: noun (networking port)
6597 option
= purple_account_option_int_new(_("Port"), "port", 5061);
6598 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6600 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
6601 purple_account_option_add_list_item(option
, _("Auto"), "auto");
6602 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
6603 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
6604 purple_account_option_add_list_item(option
, _("UDP"), "udp");
6605 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6607 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
6608 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
6610 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
6611 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6614 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
6615 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6617 /* Suitable for sspi/NTLM, sspi/Kerberos and krb5 security mechanisms
6618 * No login/password is taken into account if this option present,
6619 * instead used default credentials stored in OS.
6621 option
= purple_account_option_bool_new(_("Use Single Sign-On"), "sso", TRUE
);
6622 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6624 my_protocol
= plugin
;
6627 /* I had to redefined the function for it load, but works */
6628 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
6629 plugin
->info
= &(info
);
6630 init_plugin((plugin
));
6631 sipe_plugin_load((plugin
));
6632 return purple_plugin_register(plugin
);