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_
55 #include "accountopt.h"
57 #include "conversation.h"
71 #include "sipe-chat.h"
72 #include "sipe-conf.h"
73 #include "sipe-dialog.h"
75 #include "sipe-session.h"
76 #include "sipe-utils.h"
78 #include "sipe-sign.h"
82 /* Keep in sync with sipe_transport_type! */
83 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
84 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
86 /* Status identifiers (see also: sipe_status_types()) */
87 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
88 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
89 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
90 /* PURPLE_STATUS_UNAVAILABLE: */
91 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
92 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
93 #define SIPE_STATUS_ID_ONPHONE "on-the-phone" /* On The Phone */
94 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
95 /* PURPLE_STATUS_AWAY: */
96 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
97 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
98 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
99 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
100 /* ??? PURPLE_STATUS_MOBILE */
101 /* ??? PURPLE_STATUS_TUNE */
103 /* Action name templates */
104 #define ACTION_NAME_PRESENCE "<presence><%s>"
107 static gchar
*get_epid(struct sipe_account_data
*sip
)
110 sip
->epid
= sipe_uuid_get_macaddr(purple_network_get_my_ip(-1));
112 return g_strdup(sip
->epid
);
115 static char *genbranch()
117 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
118 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
119 rand() & 0xFFFF, rand() & 0xFFFF);
122 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
127 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
129 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
131 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
132 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
135 static void sipe_close(PurpleConnection
*gc
);
137 static void send_presence_status(struct sipe_account_data
*sip
);
139 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
141 static void sipe_keep_alive(PurpleConnection
*gc
)
143 struct sipe_account_data
*sip
= gc
->proto_data
;
144 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
145 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
146 gchar buf
[2] = {0, 0};
147 purple_debug_info("sipe", "sending keep alive\n");
148 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
150 time_t now
= time(NULL
);
151 if ((sip
->keepalive_timeout
> 0) &&
152 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
153 #if PURPLE_VERSION_CHECK(2,4,0)
154 && ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
157 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
158 sendout_pkt(gc
, "\r\n\r\n");
159 sip
->last_keepalive
= now
;
164 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
166 struct sip_connection
*ret
= NULL
;
167 GSList
*entry
= sip
->openconns
;
170 if (ret
->fd
== fd
) return ret
;
176 static void sipe_auth_free(struct sip_auth
*auth
)
178 g_free(auth
->opaque
);
182 g_free(auth
->target
);
184 auth
->type
= AUTH_TYPE_UNSET
;
187 g_free(auth
->gssapi_data
);
188 auth
->gssapi_data
= NULL
;
189 sip_sec_destroy_context(auth
->gssapi_context
);
190 auth
->gssapi_context
= NULL
;
193 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
195 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
197 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
201 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
203 struct sip_connection
*conn
= connection_find(sip
, fd
);
205 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
206 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
212 static void connection_free_all(struct sipe_account_data
*sip
)
214 struct sip_connection
*ret
= NULL
;
215 GSList
*entry
= sip
->openconns
;
218 connection_remove(sip
, ret
->fd
);
219 entry
= sip
->openconns
;
223 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
226 const char *authuser
= sip
->authuser
;
230 if (!authuser
|| strlen(authuser
) < 1) {
231 authuser
= sip
->username
;
234 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
235 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
237 // If we have a signature for the message, include that
238 if (msg
->signature
) {
239 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
);
242 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
243 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
247 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
250 purple_account_get_bool(sip
->account
, "sso", TRUE
),
251 sip
->authdomain
? sip
->authdomain
: "",
256 if (!gssapi_data
|| !auth
->gssapi_context
)
259 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
260 ret
= g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth_protocol
, opaque
, auth
->realm
, auth
->target
, gssapi_data
);
266 return g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth_protocol
, auth
->realm
, auth
->target
);
268 } else { /* Digest */
270 /* Calculate new session key */
272 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
273 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
274 authuser
, auth
->realm
, sip
->password
,
275 auth
->gssapi_data
, NULL
);
278 sprintf(noncecount
, "%08d", auth
->nc
++);
279 response
= purple_cipher_http_digest_calculate_response("md5",
280 msg
->method
, msg
->target
, NULL
, NULL
,
281 auth
->gssapi_data
, noncecount
, NULL
,
283 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
285 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
);
291 static char *parse_attribute(const char *attrname
, const char *source
)
293 const char *tmp
, *tmp2
;
295 int len
= strlen(attrname
);
297 if (!strncmp(source
, attrname
, len
)) {
299 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
301 retval
= g_strndup(tmp
, tmp2
- tmp
);
303 retval
= g_strdup(tmp
);
309 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
315 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
319 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
320 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
321 auth
->type
= AUTH_TYPE_NTLM
;
324 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
325 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
326 auth
->type
= AUTH_TYPE_KERBEROS
;
330 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
331 auth
->type
= AUTH_TYPE_DIGEST
;
335 parts
= g_strsplit(hdr
, "\", ", 0);
336 for (i
= 0; parts
[i
]; i
++) {
339 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
341 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
342 g_free(auth
->gssapi_data
);
343 auth
->gssapi_data
= tmp
;
345 if (auth
->type
== AUTH_TYPE_NTLM
) {
346 /* NTLM module extracts nonce from gssapi-data */
350 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
351 /* Only used with AUTH_TYPE_DIGEST */
352 g_free(auth
->gssapi_data
);
353 auth
->gssapi_data
= tmp
;
354 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
355 g_free(auth
->opaque
);
357 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
361 if (auth
->type
== AUTH_TYPE_DIGEST
) {
362 /* Throw away old session key */
363 g_free(auth
->opaque
);
368 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
369 g_free(auth
->target
);
378 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
380 PurpleConnection
*gc
= data
;
381 struct sipe_account_data
*sip
= gc
->proto_data
;
385 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
387 if (max_write
== 0) {
388 if (sip
->tx_handler
!= 0){
389 purple_input_remove(sip
->tx_handler
);
395 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
397 if (written
< 0 && errno
== EAGAIN
)
399 else if (written
<= 0) {
400 /*TODO: do we really want to disconnect on a failure to write?*/
401 purple_connection_error(gc
, _("Could not write"));
405 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
408 static void sipe_canwrite_cb_ssl(gpointer data
, gint src
, PurpleInputCondition cond
)
410 PurpleConnection
*gc
= data
;
411 struct sipe_account_data
*sip
= gc
->proto_data
;
415 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
417 if (max_write
== 0) {
418 if (sip
->tx_handler
!= 0) {
419 purple_input_remove(sip
->tx_handler
);
425 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
427 if (written
< 0 && errno
== EAGAIN
)
429 else if (written
<= 0) {
430 /*TODO: do we really want to disconnect on a failure to write?*/
431 purple_connection_error(gc
, _("Could not write"));
435 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
438 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
440 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
442 PurpleConnection
*gc
= data
;
443 struct sipe_account_data
*sip
;
444 struct sip_connection
*conn
;
446 if (!PURPLE_CONNECTION_IS_VALID(gc
))
454 purple_connection_error(gc
, _("Could not connect"));
458 sip
= gc
->proto_data
;
460 sip
->connecting
= FALSE
;
461 sip
->last_keepalive
= time(NULL
);
463 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
465 /* If there is more to write now, we need to register a handler */
466 if (sip
->txbuf
->bufused
> 0)
467 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
469 conn
= connection_create(sip
, source
);
470 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
473 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
475 struct sipe_account_data
*sip
;
476 struct sip_connection
*conn
;
478 if (!PURPLE_CONNECTION_IS_VALID(gc
))
480 if (gsc
) purple_ssl_close(gsc
);
484 sip
= gc
->proto_data
;
487 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
488 sip
->connecting
= FALSE
;
489 sip
->last_keepalive
= time(NULL
);
491 conn
= connection_create(sip
, gsc
->fd
);
493 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
498 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
500 PurpleConnection
*gc
= data
;
501 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
502 if (sip
== NULL
) return;
504 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
506 /* If there is more to write now */
507 if (sip
->txbuf
->bufused
> 0) {
508 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
513 static void sendlater(PurpleConnection
*gc
, const char *buf
)
515 struct sipe_account_data
*sip
= gc
->proto_data
;
517 if (!sip
->connecting
) {
518 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
519 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
520 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
522 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
523 purple_connection_error(gc
, _("Couldn't create socket"));
526 sip
->connecting
= TRUE
;
529 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
530 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
532 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
535 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
537 struct sipe_account_data
*sip
= gc
->proto_data
;
538 time_t currtime
= time(NULL
);
539 int writelen
= strlen(buf
);
541 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
542 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
543 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
544 purple_debug_info("sipe", "could not send packet\n");
553 if (sip
->tx_handler
) {
558 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
560 ret
= write(sip
->fd
, buf
, writelen
);
564 if (ret
< 0 && errno
== EAGAIN
)
566 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
571 if (ret
< writelen
) {
572 if (!sip
->tx_handler
){
574 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
577 sip
->tx_handler
= purple_input_add(sip
->fd
,
578 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
583 /* XXX: is it OK to do this? You might get part of a request sent
584 with part of another. */
585 if (sip
->txbuf
->bufused
> 0)
586 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
588 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
594 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
596 sendout_pkt(gc
, buf
);
600 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
602 GSList
*tmp
= msg
->headers
;
605 GString
*outstr
= g_string_new("");
606 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
608 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
609 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
610 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
611 tmp
= g_slist_next(tmp
);
613 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
614 sendout_pkt(sip
->gc
, outstr
->str
);
615 g_string_free(outstr
, TRUE
);
618 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
621 if (sip
->registrar
.gssapi_context
) {
622 struct sipmsg_breakdown msgbd
;
623 gchar
*signature_input_str
;
625 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
626 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
627 sip
->registrar
.ntlm_num
++;
628 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
629 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
630 if (signature_input_str
!= NULL
) {
631 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
632 msg
->signature
= signature_hex
;
633 msg
->rand
= g_strdup(msgbd
.rand
);
634 msg
->num
= g_strdup(msgbd
.num
);
635 g_free(signature_input_str
);
637 sipmsg_breakdown_free(&msgbd
);
640 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
641 buf
= auth_header(sip
, &sip
->registrar
, msg
);
642 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
644 } 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")) {
645 sip
->registrar
.nc
= 3;
647 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
649 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
652 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
657 buf
= auth_header(sip
, &sip
->registrar
, msg
);
658 sipmsg_add_header_now_pos(msg
, "Proxy-Authorization", buf
, 5);
661 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
667 static char *get_contact_service(struct sipe_account_data *sip)
669 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()));
670 //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);
674 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
675 const char *text
, const char *body
)
679 GString
*outstr
= g_string_new("");
680 struct sipe_account_data
*sip
= gc
->proto_data
;
683 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
685 contact
= get_contact(sip
);
686 sipmsg_add_header(msg
, "Contact", contact
);
691 sprintf(len
, "%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
692 sipmsg_add_header(msg
, "Content-Length", len
);
694 sipmsg_add_header(msg
, "Content-Length", "0");
697 msg
->response
= code
;
699 sipmsg_strip_headers(msg
, keepers
);
700 sipmsg_merge_new_headers(msg
);
701 sign_outgoing_message(msg
, sip
, msg
->method
);
703 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
706 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
707 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
709 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
710 tmp
= g_slist_next(tmp
);
712 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
713 sendout_pkt(gc
, outstr
->str
);
714 g_string_free(outstr
, TRUE
);
717 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
719 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
720 if (trans
->msg
) sipmsg_free(trans
->msg
);
725 static struct transaction
*
726 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
728 gchar
*call_id
= NULL
;
730 struct transaction
*trans
= g_new0(struct transaction
, 1);
732 trans
->time
= time(NULL
);
733 trans
->msg
= (struct sipmsg
*)msg
;
734 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
735 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
736 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
737 trans
->callback
= callback
;
738 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
742 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
744 struct transaction
*trans
;
745 GSList
*transactions
= sip
->transactions
;
746 gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
747 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
748 gchar
*key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
750 while (transactions
) {
751 trans
= transactions
->data
;
752 if (!g_strcasecmp(trans
->key
, key
)) {
756 transactions
= transactions
->next
;
764 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
765 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
766 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
768 struct sipe_account_data
*sip
= gc
->proto_data
;
769 const char *addh
= "";
772 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
773 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
774 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
775 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
776 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
777 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
778 gchar
*route
= strdup("");
779 gchar
*epid
= get_epid(sip
); // TODO generate one per account/login
780 int cseq
= dialog
? ++dialog
->cseq
:
781 /* This breaks OCS2007: own presence, contact search, ?
782 1 .* as Call-Id is new in this case */
784 struct transaction
*trans
;
786 if (dialog
&& dialog
->routes
)
788 GSList
*iter
= dialog
->routes
;
793 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
795 iter
= g_slist_next(iter
);
799 if (!ourtag
&& !dialog
) {
803 if (!strcmp(method
, "REGISTER")) {
804 if (sip
->regcallid
) {
806 callid
= g_strdup(sip
->regcallid
);
808 sip
->regcallid
= g_strdup(callid
);
812 if (addheaders
) addh
= addheaders
;
814 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
815 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
816 "From: <sip:%s>%s%s;epid=%s\r\n"
817 "To: <%s>%s%s%s%s\r\n"
818 "Max-Forwards: 70\r\n"
823 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
825 dialog
&& dialog
->request
? dialog
->request
: url
,
826 TRANSPORT_DESCRIPTOR
,
827 purple_network_get_my_ip(-1),
829 branch
? ";branch=" : "",
830 branch
? branch
: "",
832 ourtag
? ";tag=" : "",
833 ourtag
? ourtag
: "",
836 theirtag
? ";tag=" : "",
837 theirtag
? theirtag
: "",
838 theirepid
? ";epid=" : "",
839 theirepid
? theirepid
: "",
846 body
? (gsize
) strlen(body
) : 0,
850 //printf ("parsing msg buf:\n%s\n\n", buf);
851 msg
= sipmsg_parse_msg(buf
);
862 sign_outgoing_message (msg
, sip
, method
);
864 buf
= sipmsg_to_string (msg
);
866 /* add to ongoing transactions */
867 trans
= transactions_add_buf(sip
, msg
, tc
);
868 sendout_pkt(gc
, buf
);
874 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
876 gchar
*from
= sip_uri_self(sip
);
877 gchar
*contact
= get_contact(sip
);
878 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
879 "Content-Type: application/SOAP+xml\r\n",contact
);
881 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
882 tr
->payload
= payload
;
889 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
891 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
894 static char *get_contact_register(struct sipe_account_data
*sip
)
896 char *epid
= get_epid(sip
);
897 char *uuid
= generateUUIDfromEPID(epid
);
898 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
);
904 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
906 char *expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
907 char *uri
= sip_uri_from_name(sip
->sipdomain
);
908 char *to
= sip_uri_self(sip
);
909 char *contact
= get_contact_register(sip
);
910 char *hdr
= g_strdup_printf("Contact: %s\r\n"
911 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
912 "Event: registration\r\n"
913 "Allow-Events: presence\r\n"
914 "ms-keep-alive: UAC;hop-hop=yes\r\n"
915 "%s", contact
, expires
);
919 sip
->registerstatus
= 1;
921 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
922 process_register_response
);
929 static void do_register_cb(struct sipe_account_data
*sip
, void *unused
)
931 do_register_exp(sip
, -1);
932 sip
->reregister_set
= FALSE
;
935 static void do_register(struct sipe_account_data
*sip
)
937 do_register_exp(sip
, -1);
941 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
943 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
944 send_soap_request(sip
, body
);
949 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
952 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
954 purple_debug_info("sipe", "Blocking contact %s\n", who
);
957 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
961 void sipe_auth_user_cb(void * data
)
963 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
966 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
971 void sipe_deny_user_cb(void * data
)
973 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
976 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
981 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
983 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
984 sipe_contact_allow_deny(sip
, name
, TRUE
);
988 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
990 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
991 sipe_contact_allow_deny(sip
, name
, FALSE
);
995 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
997 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
998 sipe_contact_set_acl(sip, name, "");
1002 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1006 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1007 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1009 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1011 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1012 if (!watchers
) return;
1014 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1015 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1016 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1017 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1019 // TODO pull out optional displayName to pass as alias
1021 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1022 job
->who
= remote_user
;
1024 purple_account_request_authorization(
1038 xmlnode_free(watchers
);
1043 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1045 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1046 if (!purple_group
) {
1047 purple_group
= purple_group_new(group
->name
);
1048 purple_blist_add_group(purple_group
, NULL
);
1052 group
->purple_group
= purple_group
;
1053 sip
->groups
= g_slist_append(sip
->groups
, group
);
1054 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1056 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1060 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1062 struct sipe_group
*group
;
1068 entry
= sip
->groups
;
1070 group
= entry
->data
;
1071 if (group
->id
== id
) {
1074 entry
= entry
->next
;
1079 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1081 struct sipe_group
*group
;
1087 entry
= sip
->groups
;
1089 group
= entry
->data
;
1090 if (!strcmp(group
->name
, name
)) {
1093 entry
= entry
->next
;
1099 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1102 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1103 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1104 send_soap_request(sip
, body
);
1106 g_free(group
->name
);
1107 group
->name
= g_strdup(name
);
1111 * Only appends if no such value already stored.
1114 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1115 GSList
* res
= list
;
1116 if (!g_slist_find_custom(list
, data
, func
)) {
1117 res
= g_slist_insert_sorted(list
, data
, func
);
1123 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1124 return group1
->id
- group2
->id
;
1128 * Returns string like "2 4 7 8" - group ids buddy belong to.
1131 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1134 //creating array from GList, converting int to gchar*
1135 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1136 GSList
*entry
= buddy
->groups
;
1138 struct sipe_group
* group
= entry
->data
;
1139 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1140 entry
= entry
->next
;
1144 res
= g_strjoinv(" ", ids_arr
);
1145 g_strfreev(ids_arr
);
1150 * Sends buddy update to server
1153 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1155 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1156 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1158 if (buddy
&& purple_buddy
) {
1159 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1161 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1162 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1164 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1165 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1167 send_soap_request(sip
, body
);
1173 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1175 if (msg
->response
== 200) {
1176 struct sipe_group
*group
;
1177 struct group_user_context
*ctx
= (struct group_user_context
*)tc
->payload
;
1181 struct sipe_buddy
*buddy
;
1183 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1189 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1196 group_id
= xmlnode_get_data(node
);
1203 group
= g_new0(struct sipe_group
, 1);
1204 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1206 group
->name
= ctx
->group_name
;
1208 sipe_group_add(sip
, group
);
1210 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1212 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1215 sipe_group_set_user(sip
, ctx
->user_name
);
1224 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1226 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1228 ctx
->group_name
= g_strdup(name
);
1229 ctx
->user_name
= g_strdup(who
);
1231 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1232 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1237 * Data structure for scheduled actions
1239 typedef void (*Action
) (struct sipe_account_data
*, void *);
1241 struct scheduled_action
{
1244 * Format is <Event>[<Data>...]
1245 * Example: <presence><sip:user@domain.com> or <registration>
1248 guint timeout_handler
;
1249 gboolean repetitive
;
1251 GDestroyNotify destroy
;
1252 struct sipe_account_data
*sip
;
1258 * Should return FALSE if repetitive action is not needed
1260 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1263 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1264 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1265 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1266 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1267 ret
= sched_action
->repetitive
;
1268 if (sched_action
->destroy
) {
1269 (*sched_action
->destroy
)(sched_action
->payload
);
1271 g_free(sched_action
->name
);
1272 g_free(sched_action
);
1277 * Kills action timer effectively cancelling
1280 * @param name of action
1282 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1286 if (!sip
->timeouts
|| !name
) return;
1288 entry
= sip
->timeouts
;
1290 struct scheduled_action
*sched_action
= entry
->data
;
1291 if(!strcmp(sched_action
->name
, name
)) {
1292 GSList
*to_delete
= entry
;
1293 entry
= entry
->next
;
1294 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1295 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1296 purple_timeout_remove(sched_action
->timeout_handler
);
1297 if (sched_action
->destroy
) {
1298 (*sched_action
->destroy
)(sched_action
->payload
);
1300 g_free(sched_action
->name
);
1301 g_free(sched_action
);
1303 entry
= entry
->next
;
1309 sipe_schedule_action0(const gchar
*name
,
1313 GDestroyNotify destroy
,
1314 struct sipe_account_data
*sip
,
1317 struct scheduled_action
*sched_action
;
1319 /* Make sure each action only exists once */
1320 sipe_cancel_scheduled_action(sip
, name
);
1322 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1323 sched_action
= g_new0(struct scheduled_action
, 1);
1324 sched_action
->repetitive
= FALSE
;
1325 sched_action
->name
= g_strdup(name
);
1326 sched_action
->action
= action
;
1327 sched_action
->destroy
= destroy
;
1328 sched_action
->sip
= sip
;
1329 sched_action
->payload
= payload
;
1330 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1331 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1332 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1333 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1337 * Do schedule action for execution in the future.
1338 * Non repetitive execution.
1340 * @param name of action (will be copied)
1341 * @param timeout in seconds
1342 * @action callback function
1343 * @payload callback data (can be NULL, otherwise caller must allocate memory)
1346 sipe_schedule_action(const gchar
*name
,
1349 GDestroyNotify destroy
,
1350 struct sipe_account_data
*sip
,
1353 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1357 * Same as sipe_schedule_action() but timeout is in milliseconds.
1360 sipe_schedule_action_msec(const gchar
*name
,
1363 GDestroyNotify destroy
,
1364 struct sipe_account_data
*sip
,
1367 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1371 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1373 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1375 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1377 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1382 static void sipe_subscribe_resource_uri(const char *name
, gpointer value
, gchar
**resources_uri
)
1384 gchar
*tmp
= *resources_uri
;
1385 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1389 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1391 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1392 if (sbuddy
&& !sbuddy
->resubscribed
) { // Only not resubscribed contacts; the first time everybody are included
1393 gchar
*tmp
= *resources_uri
;
1394 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"><context/></resource>\n", tmp
, name
);
1400 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1401 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1402 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1403 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1404 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1407 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1409 gchar
*contact
= get_contact(sip
);
1412 gchar
*require
= "";
1414 gchar
*autoextend
= "";
1415 gchar
*content_type
;
1417 if (sip
->msrtc_event_categories
) {
1418 require
= ", categoryList";
1419 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1420 content_type
= "application/msrtc-adrl-categorylist+xml";
1421 content
= g_strdup_printf(
1422 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1423 "<action name=\"subscribe\" id=\"63792024\">\n"
1424 "<adhocList>\n%s</adhocList>\n"
1425 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1426 "<category name=\"note\"/>\n"
1427 "<category name=\"state\"/>\n"
1430 "</batchSub>", sip
->username
, resources_uri
);
1432 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1433 content_type
= "application/adrl+xml";
1434 content
= g_strdup_printf(
1435 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1436 "<create xmlns=\"\">\n%s</create>\n"
1437 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1439 g_free(resources_uri
);
1441 request
= g_strdup_printf(
1442 "Require: adhoclist%s\r\n"
1443 "Supported: eventlist\r\n"
1444 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1445 "Supported: ms-piggyback-first-notify\r\n"
1446 "%sSupported: ms-benotify\r\n"
1447 "Proxy-Require: ms-benotify\r\n"
1448 "Event: presence\r\n"
1449 "Content-Type: %s\r\n"
1450 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1453 /* subscribe to buddy presence */
1454 //send_sip_request(sip->gc, "SUBSCRIBE", resource_uri, resource_uri, request, content, NULL, process_subscribe_response);
1455 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1462 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
, void *unused
)
1464 gchar
*to
= sip_uri_self(sip
);
1465 gchar
*resources_uri
= g_strdup("");
1466 if (sip
->msrtc_event_categories
) {
1467 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1469 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1471 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1474 struct presence_batched_routed
{
1479 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1481 struct presence_batched_routed
*data
= payload
;
1482 GSList
*buddies
= data
->buddies
;
1484 g_free(buddies
->data
);
1485 buddies
= buddies
->next
;
1487 g_slist_free(data
->buddies
);
1492 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
1494 struct presence_batched_routed
*data
= payload
;
1495 GSList
*buddies
= data
->buddies
;
1496 gchar
*resources_uri
= g_strdup("");
1498 gchar
*tmp
= resources_uri
;
1499 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
1501 buddies
= buddies
->next
;
1503 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
1504 g_strdup(data
->host
));
1508 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1509 * The user sends a single SUBSCRIBE request to the subscribed contact.
1510 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1514 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
1516 gchar
*to
= sip_uri((char *)buddy_name
);
1517 gchar
*tmp
= get_contact(sip
);
1520 gchar
*autoextend
= "";
1522 if (!sip
->msrtc_event_categories
)
1523 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1525 request
= g_strdup_printf(
1526 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1527 "Supported: ms-piggyback-first-notify\r\n"
1528 "%sSupported: ms-benotify\r\n"
1529 "Proxy-Require: ms-benotify\r\n"
1530 "Event: presence\r\n"
1531 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1532 "Contact: %s\r\n", autoextend
,tmp
);
1534 content
= g_strdup_printf(
1535 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1536 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1537 "<resource uri=\"%s\"/>\n"
1539 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1540 "<category name=\"note\"/>\n"
1541 "<category name=\"state\"/>\n"
1544 "</batchSub>", sip
->username
, to
1549 /* subscribe to buddy presence */
1550 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1557 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1559 if (!purple_status_is_active(status
))
1563 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
1566 g_free(sip
->status
);
1567 sip
->status
= g_strdup(purple_status_get_id(status
));
1568 send_presence_status(sip
);
1574 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
)
1576 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1577 sipe_group_set_user(sip
, name
);
1581 sipe_group_buddy(PurpleConnection
*gc
,
1583 const char *old_group_name
,
1584 const char *new_group_name
)
1586 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1587 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1588 struct sipe_group
* old_group
= NULL
;
1589 struct sipe_group
* new_group
;
1591 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1592 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1594 if(!buddy
) { // buddy not in roaming list
1598 if (old_group_name
) {
1599 old_group
= sipe_group_find_by_name(sip
, g_strdup(old_group_name
));
1601 new_group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1604 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1605 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1609 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1611 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1612 sipe_group_set_user(sip
, who
);
1616 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1618 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1619 struct sipe_buddy
*b
;
1621 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1623 // Prepend sip: if needed
1624 if (strncmp("sip:", buddy
->name
, 4)) {
1625 gchar
*buf
= sip_uri_from_name(buddy
->name
);
1626 purple_blist_rename_buddy(buddy
, buf
);
1630 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1631 b
= g_new0(struct sipe_buddy
, 1);
1632 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1633 b
->name
= g_strdup(buddy
->name
);
1634 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1635 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1636 sipe_subscribe_presence_single(sip
, b
->name
); //@TODO should go to callback
1638 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1642 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1646 * We are calling g_hash_table_foreach_steal(). That means that no
1647 * key/value deallocation functions are called. Therefore the glib
1648 * hash code does not touch the key (buddy->name) or value (buddy)
1649 * of the to-be-deleted hash node at all. It follows that we
1651 * - MUST free the memory for the key ourselves and
1652 * - ARE allowed to do it in this function
1654 * Conclusion: glib must be broken on the Windows platform if sipe
1655 * crashes with SIGTRAP when closing. You'll have to live
1656 * with the memory leak until this is fixed.
1658 g_free(buddy
->name
);
1660 g_free(buddy
->annotation
);
1661 g_free(buddy
->device_name
);
1662 g_slist_free(buddy
->groups
);
1667 * Unassociates buddy from group first.
1668 * Then see if no groups left, removes buddy completely.
1669 * Otherwise updates buddy groups on server.
1671 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1673 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1674 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1675 struct sipe_group
*g
= NULL
;
1677 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1682 g
= sipe_group_find_by_name(sip
, group
->name
);
1686 b
->groups
= g_slist_remove(b
->groups
, g
);
1687 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
1690 if (g_slist_length(b
->groups
) < 1) {
1691 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
1692 sipe_cancel_scheduled_action(sip
, action_name
);
1693 g_free(action_name
);
1695 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1698 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1699 send_soap_request(sip
, body
);
1705 //updates groups on server
1706 sipe_group_set_user(sip
, b
->name
);
1712 sipe_rename_group(PurpleConnection
*gc
,
1713 const char *old_name
,
1715 GList
*moved_buddies
)
1717 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1718 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1720 sipe_group_rename(sip
, s_group
, group
->name
);
1722 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1727 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1729 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1730 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1733 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1734 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1735 send_soap_request(sip
, body
);
1738 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1739 g_free(s_group
->name
);
1742 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1746 static GList
*sipe_status_types(PurpleAccount
*acc
)
1748 PurpleStatusType
*type
;
1749 GList
*types
= NULL
;
1752 type
= purple_status_type_new_with_attrs(
1753 PURPLE_STATUS_AVAILABLE
, NULL
, _("Online"), TRUE
, TRUE
, FALSE
,
1754 // Translators: noun
1755 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1757 types
= g_list_append(types
, type
);
1760 type
= purple_status_type_new_with_attrs(
1761 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_BUSY
, _("Busy"), TRUE
, TRUE
, FALSE
,
1762 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1764 types
= g_list_append(types
, type
);
1766 // Do Not Disturb (not user settable)
1767 type
= purple_status_type_new_with_attrs(
1768 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_DND
, _("Do Not Disturb"), TRUE
, FALSE
, FALSE
,
1769 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1771 types
= g_list_append(types
, type
);
1774 type
= purple_status_type_new_with_attrs(
1775 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_BRB
, _("Be Right Back"), TRUE
, TRUE
, FALSE
,
1776 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1778 types
= g_list_append(types
, type
);
1781 type
= purple_status_type_new_with_attrs(
1782 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1783 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1785 types
= g_list_append(types
, type
);
1788 type
= purple_status_type_new_with_attrs(
1789 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_ONPHONE
, _("On The Phone"), TRUE
, TRUE
, FALSE
,
1790 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1792 types
= g_list_append(types
, type
);
1795 type
= purple_status_type_new_with_attrs(
1796 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_LUNCH
, _("Out To Lunch"), TRUE
, TRUE
, FALSE
,
1797 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1799 types
= g_list_append(types
, type
);
1802 type
= purple_status_type_new_full(
1803 PURPLE_STATUS_INVISIBLE
, NULL
, _("Appear Offline"), TRUE
, TRUE
, FALSE
);
1804 types
= g_list_append(types
, type
);
1807 type
= purple_status_type_new_full(
1808 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1809 types
= g_list_append(types
, type
);
1815 * A callback for g_hash_table_foreach
1817 static void sipe_buddy_subscribe_cb(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1819 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
1820 int time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1821 int timeout
= (time_range
* rand()) / RAND_MAX
; /* random period within the range */
1822 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy
->name
));
1826 * Removes entries from purple buddy list
1827 * that does not correspond ones in the roaming contact list.
1829 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
1830 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1831 GSList
*entry
= buddies
;
1832 struct sipe_buddy
*buddy
;
1836 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
1837 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
1840 g
= purple_buddy_get_group(b
);
1841 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
1843 gboolean in_sipe_groups
= FALSE
;
1844 GSList
*entry2
= buddy
->groups
;
1846 struct sipe_group
*group
= entry2
->data
;
1847 if (!strcmp(group
->name
, g
->name
)) {
1848 in_sipe_groups
= TRUE
;
1851 entry2
= entry2
->next
;
1853 if(!in_sipe_groups
) {
1854 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
1855 purple_blist_remove_buddy(b
);
1858 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
1859 purple_blist_remove_buddy(b
);
1861 entry
= entry
->next
;
1865 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1867 int len
= msg
->bodylen
;
1869 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1872 const gchar
*contacts_delta
;
1873 xmlnode
*group_node
;
1874 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1878 /* Convert the contact from XML to Purple Buddies */
1879 isc
= xmlnode_from_str(msg
->body
, len
);
1884 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
1885 if (contacts_delta
) {
1886 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1889 if (!strcmp(isc
->name
, "contactList")) {
1892 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1893 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1894 const char *name
= xmlnode_get_attrib(group_node
, "name");
1896 if (!strncmp(name
, "~", 1)) {
1897 name
= _("Other Contacts");
1899 group
->name
= g_strdup(name
);
1900 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1902 sipe_group_add(sip
, group
);
1905 // Make sure we have at least one group
1906 if (g_slist_length(sip
->groups
) == 0) {
1907 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1908 PurpleGroup
*purple_group
;
1909 group
->name
= g_strdup(_("Other Contacts"));
1911 purple_group
= purple_group_new(group
->name
);
1912 purple_blist_add_group(purple_group
, NULL
);
1913 sip
->groups
= g_slist_append(sip
->groups
, group
);
1916 /* Parse contacts */
1917 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1918 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
1919 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
1920 gchar
* groups
= g_strdup(xmlnode_get_attrib(item
, "groups"));
1921 gchar
* buddy_name
= sip_uri_from_name(uri
);
1922 gchar
**item_groups
;
1923 struct sipe_group
*group
= NULL
;
1924 struct sipe_buddy
*buddy
= NULL
;
1927 // assign to group Other Contacts if nothing else received
1928 if(!groups
|| !strcmp("", groups
) ) {
1929 group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
1930 groups
= group
? g_strdup_printf("%d", group
->id
) : "1";
1933 item_groups
= g_strsplit(groups
, " ", 0);
1935 while (item_groups
[i
]) {
1936 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
1938 // If couldn't find the right group for this contact, just put them in the first group we have
1939 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1940 group
= sip
->groups
->data
;
1943 if (group
!= NULL
) {
1944 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1946 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1947 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1950 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
1951 if (name
!= NULL
&& strlen(name
) != 0) {
1952 purple_blist_alias_buddy(b
, name
);
1957 buddy
= g_new0(struct sipe_buddy
, 1);
1958 buddy
->name
= g_strdup(b
->name
);
1959 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
1962 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1964 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
1966 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
1971 } // while, contact groups
1972 g_strfreev(item_groups
);
1980 sipe_cleanup_local_blist(sip
);
1984 //subscribe to buddies
1985 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
1986 if(sip
->batched_support
){
1987 sipe_subscribe_presence_batched(sip
, NULL
);
1990 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
1992 sip
->subscribed_buddies
= TRUE
;
1999 * Subscribe roaming contacts
2001 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2003 gchar
*to
= sip_uri_self(sip
);
2004 gchar
*tmp
= get_contact(sip
);
2005 gchar
*hdr
= g_strdup_printf(
2006 "Event: vnd-microsoft-roaming-contacts\r\n"
2007 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2008 "Supported: com.microsoft.autoextend\r\n"
2009 "Supported: ms-benotify\r\n"
2010 "Proxy-Require: ms-benotify\r\n"
2011 "Supported: ms-piggyback-first-notify\r\n"
2012 "Contact: %s\r\n", tmp
);
2015 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2020 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
, void *unused
)
2022 gchar
*to
= sip_uri_self(sip
);
2023 gchar
*tmp
= get_contact(sip
);
2024 gchar
*hdr
= g_strdup_printf(
2025 "Event: presence.wpending\r\n"
2026 "Accept: text/xml+msrtc.wpending\r\n"
2027 "Supported: com.microsoft.autoextend\r\n"
2028 "Supported: ms-benotify\r\n"
2029 "Proxy-Require: ms-benotify\r\n"
2030 "Supported: ms-piggyback-first-notify\r\n"
2031 "Contact: %s\r\n", tmp
);
2034 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2040 * Fires on deregistration event initiated by server.
2041 * [MS-SIPREGE] SIP extension.
2046 // Content-Type: text/registration-event
2047 // subscription-state: terminated;expires=0
2048 // ms-diagnostics-public: 4141;reason="User disabled"
2050 // deregistered;event=rejected
2052 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2054 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2055 gchar
*event
= NULL
;
2056 gchar
*reason
= NULL
;
2057 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2059 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2060 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2062 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2063 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2064 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2065 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2067 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2071 if (warning
!= NULL
) {
2072 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2073 } else { // for LCS2005
2075 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2076 error_id
= 4140; // [MS-SIPREGE]
2077 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2078 reason
= g_strdup(_("You have been signed off because you've signed in at another location"));
2079 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2081 reason
= g_strdup(_("User disabled")); // [MS-OCER]
2082 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2084 reason
= g_strdup(_("User moved")); // [MS-OCER]
2088 warning
= g_strdup_printf(_("Unregistered by Server: %s."), reason
? reason
: _("no reason given"));
2091 sip
->gc
->wants_to_die
= TRUE
;
2092 purple_connection_error(sip
->gc
, warning
);
2097 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2099 xmlnode
*xn_provision_group_list
;
2102 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2104 /* provisionGroup */
2105 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2106 if (!strcmp("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2107 g_free(sip
->focus_factory_uri
);
2108 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2109 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2110 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2114 xmlnode_free(xn_provision_group_list
);
2117 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2119 const gchar
*contacts_delta
;
2122 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2128 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2131 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2138 free_container(struct sipe_container
*container
)
2142 if (!container
) return;
2144 entry
= container
->members
;
2146 g_free(entry
->data
);
2147 entry
= g_slist_remove(entry
, entry
->data
);
2153 * Finds locally stored MS-PRES container member
2155 static struct sipe_container_member
*
2156 sipe_find_container_member(struct sipe_container
*container
,
2160 struct sipe_container_member
*member
;
2163 if (container
== NULL
|| type
== NULL
) {
2167 entry
= container
->members
;
2169 member
= entry
->data
;
2170 if (!g_strcasecmp(member
->type
, type
)
2171 && ((!member
->value
&& !value
)
2172 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2176 entry
= entry
->next
;
2182 * Finds locally stored MS-PRES container by id
2184 static struct sipe_container
*
2185 sipe_find_container(struct sipe_account_data
*sip
,
2188 struct sipe_container
*container
;
2195 entry
= sip
->containers
;
2197 container
= entry
->data
;
2198 if (id
== container
->id
) {
2201 entry
= entry
->next
;
2215 sipe_find_access_level(struct sipe_account_data
*sip
,
2219 guint containers
[] = {32000, 400, 300, 200, 100};
2222 for (i
= 0; i
< 5; i
++) {
2223 struct sipe_container_member
*member
;
2224 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2225 if (!container
) continue;
2227 member
= sipe_find_container_member(container
, type
, value
);
2229 return containers
[i
];
2237 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2239 guint container_version
,
2240 const gchar
* action
,
2244 gchar
*self
= sip_uri_self(sip
);
2245 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2248 gchar
*body
= g_strdup_printf(
2249 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2250 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2251 "</setContainerMembers>",
2259 contact
= get_contact(sip
);
2260 hdr
= g_strdup_printf("Contact: %s\r\n"
2261 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2264 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2273 * When we receive some self (BE) NOTIFY with a new subscriber
2274 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2277 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2284 char *display_name
= NULL
;
2285 PurpleBuddy
*pbuddy
;
2290 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2292 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2295 contact
= get_contact(sip
);
2296 to
= sip_uri_self(sip
);
2299 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2300 guint id
= atoi(xmlnode_get_attrib(node
, "id"));
2301 struct sipe_container
*container
= sipe_find_container(sip
, id
);
2304 sip
->containers
= g_slist_remove(sip
->containers
, container
);
2305 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
2306 free_container(container
);
2308 container
= g_new0(struct sipe_container
, 1);
2310 container
->version
= atoi(xmlnode_get_attrib(node
, "version"));
2311 sip
->containers
= g_slist_append(sip
->containers
, container
);
2312 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
2314 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
2315 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
2316 member
->type
= xmlnode_get_attrib(node2
, "type");
2317 member
->value
= xmlnode_get_attrib(node2
, "value");
2318 container
->members
= g_slist_append(container
->members
, member
);
2319 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
2320 member
->type
, member
->value
? member
->value
: "");
2324 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
2325 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
2326 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
2327 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
2328 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
2329 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
2330 /* initial set-up to let counterparties see your status */
2331 if (sameEnterpriseAL
< 0) {
2332 struct sipe_container
*container
= sipe_find_container(sip
, 200);
2333 guint version
= container
? container
->version
: 0;
2334 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
2336 if (federatedAL
< 0) {
2337 struct sipe_container
*container
= sipe_find_container(sip
, 100);
2338 guint version
= container
? container
->version
: 0;
2339 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
2341 sip
->access_level_set
= TRUE
;
2345 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2347 const char *acknowledged
;
2351 user
= xmlnode_get_attrib(node
, "user");
2352 if (!user
) continue;
2353 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
2354 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
2355 uri_user
= sip_uri_from_name(user
);
2356 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri_user
);
2358 alias
= purple_buddy_get_local_alias(pbuddy
);
2359 uri_alias
= sip_uri_from_name(alias
);
2360 if (display_name
&& !g_ascii_strcasecmp(uri_user
, uri_alias
)) { // 'bad' alias
2361 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri_user
, display_name
);
2362 purple_blist_alias_buddy(pbuddy
, display_name
);
2367 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
2368 if(!g_ascii_strcasecmp(acknowledged
,"false")){
2369 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
2370 if (!purple_find_buddy(sip
->account
, uri_user
)) {
2371 purple_account_request_add(sip
->account
, uri_user
, _("you"), display_name
, NULL
);
2374 hdr
= g_strdup_printf(
2376 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2378 body
= g_strdup_printf(
2379 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2380 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2381 "</setSubscribers>", user
);
2383 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
2387 g_free(display_name
);
2396 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2398 gchar
*to
= sip_uri_self(sip
);
2399 gchar
*tmp
= get_contact(sip
);
2400 gchar
*hdr
= g_strdup_printf(
2401 "Event: vnd-microsoft-roaming-ACL\r\n"
2402 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
2403 "Supported: com.microsoft.autoextend\r\n"
2404 "Supported: ms-benotify\r\n"
2405 "Proxy-Require: ms-benotify\r\n"
2406 "Supported: ms-piggyback-first-notify\r\n"
2407 "Contact: %s\r\n", tmp
);
2410 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2416 * To request for presence information about the user, access level settings that have already been configured by the user
2417 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
2418 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
2421 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2423 gchar
*to
= sip_uri_self(sip
);
2424 gchar
*tmp
= get_contact(sip
);
2425 gchar
*hdr
= g_strdup_printf(
2426 "Event: vnd-microsoft-roaming-self\r\n"
2427 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
2428 "Supported: ms-benotify\r\n"
2429 "Proxy-Require: ms-benotify\r\n"
2430 "Supported: ms-piggyback-first-notify\r\n"
2432 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
2434 gchar
*body
=g_strdup(
2435 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
2436 "<roaming type=\"categories\"/>"
2437 "<roaming type=\"containers\"/>"
2438 "<roaming type=\"subscribers\"/></roamingList>");
2441 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2450 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2452 gchar
*to
= sip_uri_self(sip
);
2453 gchar
*tmp
= get_contact(sip
);
2454 gchar
*hdr
= g_strdup_printf(
2455 "Event: vnd-microsoft-provisioning\r\n"
2456 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
2457 "Supported: com.microsoft.autoextend\r\n"
2458 "Supported: ms-benotify\r\n"
2459 "Proxy-Require: ms-benotify\r\n"
2460 "Supported: ms-piggyback-first-notify\r\n"
2462 "Contact: %s\r\n", tmp
);
2465 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
2470 /** Subscription for provisioning information to help with initial
2471 * configuration. This subscription is a one-time query (denoted by the Expires header,
2472 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
2473 * configuration, meeting policies, and policy settings that Communicator must enforce.
2474 * TODO: for what we need this information.
2477 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2479 gchar
*to
= sip_uri_self(sip
);
2480 gchar
*tmp
= get_contact(sip
);
2481 gchar
*hdr
= g_strdup_printf(
2482 "Event: vnd-microsoft-provisioning-v2\r\n"
2483 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
2484 "Supported: com.microsoft.autoextend\r\n"
2485 "Supported: ms-benotify\r\n"
2486 "Proxy-Require: ms-benotify\r\n"
2487 "Supported: ms-piggyback-first-notify\r\n"
2490 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
2491 gchar
*body
= g_strdup(
2492 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
2493 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
2494 "<provisioningGroup name=\"ucPolicy\"/>"
2495 "</provisioningGroupList>");
2498 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2504 /* IM Session (INVITE and MESSAGE methods) */
2506 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
2508 get_end_points (struct sipe_account_data
*sip
,
2509 struct sip_session
*session
)
2513 if (session
== NULL
) {
2517 res
= g_strdup_printf("<sip:%s>", sip
->username
);
2519 SIPE_DIALOG_FOREACH
{
2521 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
2524 if (dialog
->theirepid
) {
2526 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
2529 } SIPE_DIALOG_FOREACH_END
;
2535 process_options_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2537 gboolean ret
= TRUE
;
2539 if (msg
->response
!= 200) {
2540 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
2544 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
2550 * Asks UA/proxy about its capabilities.
2552 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
2554 gchar
*to
= sip_uri(who
);
2555 gchar
*contact
= get_contact(sip
);
2556 gchar
*request
= g_strdup_printf(
2557 "Accept: application/sdp\r\n"
2558 "Contact: %s\r\n", contact
);
2561 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
2568 sipe_present_err(struct sipe_account_data
*sip
,
2569 struct sip_session
*session
,
2570 const gchar
*message
)
2572 PurpleConversation
*conv
;
2574 if (!session
->conv
) {
2575 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
2577 conv
= session
->conv
;
2579 purple_conversation_write(conv
, NULL
, message
, PURPLE_MESSAGE_ERROR
, time(NULL
));
2583 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
2584 struct sip_session
*session
,
2586 const gchar
*message
)
2588 char *msg
, *msg_tmp
;
2590 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
2591 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2593 msg_tmp
= g_strdup_printf( _("This message was not delivered to %s because one or more recipients are offline:\n%s") ,
2594 who
? who
: "", msg
? msg
: "");
2595 sipe_present_err(sip
, session
, msg_tmp
);
2601 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
);
2604 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2606 gboolean ret
= TRUE
;
2607 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2608 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
2609 struct sip_dialog
*dialog
;
2615 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2620 dialog
= sipe_dialog_find(session
, with
);
2622 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2627 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2628 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
2630 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2632 if (msg
->response
>= 400) {
2633 PurpleBuddy
*pbuddy
;
2634 gchar
*alias
= with
;
2636 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
2638 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
2639 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
2642 sipe_present_message_undelivered_err(sip
, session
, alias
, message
);
2645 gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
2647 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
));
2648 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
2649 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
2652 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2653 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
2654 key
, g_hash_table_size(session
->unconfirmed_messages
));
2660 if (ret
) sipe_im_process_queue(sip
, session
);
2665 sipe_is_election_finished(struct sipe_account_data
*sip
,
2666 struct sip_session
*session
);
2669 sipe_election_result(struct sipe_account_data
*sip
,
2673 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2675 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2676 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2677 struct sip_dialog
*dialog
;
2678 struct sip_session
*session
;
2680 session
= sipe_session_find_chat_by_callid(sip
, callid
);
2682 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
2686 if (msg
->response
== 200 && !strncmp(contenttype
, "application/x-ms-mim", 20)) {
2687 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2688 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
2689 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
2691 if (xn_request_rm_response
) {
2692 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
2693 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
2695 dialog
= sipe_dialog_find(session
, with
);
2697 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
2701 if (allow
&& !g_strcasecmp(allow
, "true")) {
2702 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
2703 dialog
->election_vote
= 1;
2704 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
2705 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
2706 dialog
->election_vote
= -1;
2709 if (sipe_is_election_finished(sip
, session
)) {
2710 sipe_election_result(sip
, session
);
2713 } else if (xn_set_rm_response
) {
2716 xmlnode_free(xn_action
);
2723 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
)
2732 sipe_parse_html(msg
, &msgformat
, &msgtext
);
2733 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
2735 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2738 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2741 msgr
= g_strdup("");
2744 tmp
= get_contact(sip
);
2745 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
2746 //hdr = g_strdup("Content-Type: text/rtf\r\n");
2747 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
2748 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n", tmp
, msgr
);
2752 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
2759 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
2761 GSList
*entry2
= session
->outgoing_message_queue
;
2763 char *queued_msg
= entry2
->data
;
2765 /* for multiparty chat or conference */
2766 if (session
->is_multiparty
|| session
->focus_uri
) {
2767 gchar
*who
= sip_uri_self(sip
);
2768 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
2769 PURPLE_MESSAGE_SEND
, queued_msg
, time(NULL
));
2773 SIPE_DIALOG_FOREACH
{
2776 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
2778 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
2779 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
2780 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
2781 key
, g_hash_table_size(session
->unconfirmed_messages
));
2784 sipe_send_message(sip
, dialog
, queued_msg
);
2785 } SIPE_DIALOG_FOREACH_END
;
2787 entry2
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2793 sipe_refer_notify(struct sipe_account_data
*sip
,
2794 struct sip_session
*session
,
2801 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
2803 hdr
= g_strdup_printf(
2805 "Subscription-State: %s\r\n"
2806 "Content-Type: message/sipfrag\r\n",
2807 status
>= 200 ? "terminated" : "active");
2809 body
= g_strdup_printf(
2810 "SIP/2.0 %d %s\r\n",
2813 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "NOTIFY",
2814 who
, who
, hdr
, body
, dialog
, NULL
);
2821 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2823 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2824 struct sip_session
*session
;
2825 struct sip_dialog
*dialog
;
2829 struct sipmsg
*request_msg
= trans
->msg
;
2831 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2834 session
= sipe_session_find_chat_by_callid(sip
, callid
);
2836 session
= sipe_session_find_im(sip
, with
);
2839 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2844 dialog
= sipe_dialog_find(session
, with
);
2846 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2851 sipe_dialog_parse(dialog
, msg
, TRUE
);
2853 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2854 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
2856 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2858 if (msg
->response
!= 200) {
2859 PurpleBuddy
*pbuddy
;
2860 gchar
*alias
= with
;
2862 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2864 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
2865 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
2869 sipe_present_message_undelivered_err(sip
, session
, alias
, message
);
2871 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
2872 sipe_present_err(sip
, session
, tmp_msg
);
2876 sipe_dialog_remove(session
, with
);
2884 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
2885 dialog
->outgoing_invite
= NULL
;
2886 dialog
->is_established
= TRUE
;
2888 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
2890 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
2891 g_free(referred_by
);
2894 /* add user to chat if it is a multiparty session */
2895 if (session
->is_multiparty
) {
2896 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
2898 PURPLE_CBFLAGS_NONE
, TRUE
);
2901 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
2902 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
2903 if (session
->outgoing_message_queue
) {
2904 char *queued_msg
= session
->outgoing_message_queue
->data
;
2905 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2910 sipe_im_process_queue(sip
, session
);
2912 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2913 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
2914 key
, g_hash_table_size(session
->unconfirmed_messages
));
2923 sipe_invite(struct sipe_account_data
*sip
,
2924 struct sip_session
*session
,
2926 const gchar
*msg_body
,
2927 const gchar
*referred_by
,
2928 const gboolean is_triggered
)
2935 char *ms_text_format
= g_strdup("");
2936 gchar
*roster_manager
;
2938 gchar
*referred_by_str
;
2939 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
2941 if (dialog
&& dialog
->is_established
) {
2942 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
2947 dialog
= sipe_dialog_add(session
);
2948 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
2949 dialog
->with
= g_strdup(who
);
2952 if (!(dialog
->ourtag
)) {
2953 dialog
->ourtag
= gentag();
2966 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
2967 purple_debug_info("sipe", "sipe_invite: msgformat=%s", msgformat
);
2969 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2973 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2977 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
2978 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
2983 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
2984 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
2985 purple_debug_info("sipe", "sipe_im_send: added message %s to unconfirmed_messages(count=%d)\n",
2986 key
, g_hash_table_size(session
->unconfirmed_messages
));
2990 contact
= get_contact(sip
);
2991 end_points
= get_end_points(sip
, session
);
2992 self
= sip_uri_self(sip
);
2993 roster_manager
= g_strdup_printf(
2994 "Roster-Manager: %s\r\n"
2995 "EndPoints: %s\r\n",
2998 referred_by_str
= referred_by
?
3000 "Referred-By: %s\r\n",
3003 hdr
= g_strdup_printf(
3004 "Supported: ms-sender\r\n"
3010 "Content-Type: application/sdp\r\n",
3011 (session
->roster_manager
&& !strcmp(session
->roster_manager
, self
)) ? roster_manager
: "",
3013 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
3014 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
3017 g_free(ms_text_format
);
3020 body
= g_strdup_printf(
3022 "o=- 0 0 IN IP4 %s\r\n"
3026 "m=message %d sip null\r\n"
3027 "a=accept-types:text/plain text/html image/gif "
3028 "multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3029 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
3031 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
3032 to
, to
, hdr
, body
, dialog
, process_invite_response
);
3035 g_free(roster_manager
);
3037 g_free(referred_by_str
);
3044 sipe_refer(struct sipe_account_data
*sip
,
3045 struct sip_session
*session
,
3050 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
3051 session
->roster_manager
);
3053 contact
= get_contact(sip
);
3054 hdr
= g_strdup_printf(
3056 "Refer-to: <%s>\r\n"
3057 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
3058 "Require: com.microsoft.rtc-multiparty\r\n",
3062 dialog
->ourtag
? ";tag=" : "",
3063 dialog
->ourtag
? dialog
->ourtag
: "",
3066 send_sip_request(sip
->gc
, "REFER",
3067 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
3074 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
3075 struct sip_dialog
*dialog
,
3078 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
3080 gchar
*body
= g_strdup_printf(
3081 "<?xml version=\"1.0\"?>\r\n"
3082 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3083 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
3084 sip
->username
, bid
);
3086 send_sip_request(sip
->gc
, "INFO",
3087 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
3093 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
3094 struct sip_dialog
*dialog
)
3096 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
3098 gchar
*body
= g_strdup_printf(
3099 "<?xml version=\"1.0\"?>\r\n"
3100 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3101 "<SetRM uri=\"sip:%s\"/></action>\r\n",
3104 send_sip_request(sip
->gc
, "INFO",
3105 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
3111 im_session_close (struct sipe_account_data
*sip
, struct sip_session
* session
)
3114 SIPE_DIALOG_FOREACH
{
3115 /* @TODO slow down BYE message sending rate */
3116 /* @see single subscription code */
3117 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
3118 } SIPE_DIALOG_FOREACH_END
;
3120 sipe_session_remove(sip
, session
);
3125 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
3127 struct sipe_account_data
*sip
= gc
->proto_data
;
3129 purple_debug_info("sipe", "conversation with %s closed\n", who
);
3130 im_session_close(sip
, sipe_session_find_im(sip
, who
));
3134 sipe_chat_leave (PurpleConnection
*gc
, int id
)
3136 struct sipe_account_data
*sip
= gc
->proto_data
;
3137 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
3138 if (session
&& session
->focus_uri
) {
3139 conf_session_close(sip
, session
);
3141 im_session_close(sip
, session
);
3144 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
3146 struct sipe_account_data
*sip
= gc
->proto_data
;
3147 struct sip_session
*session
;
3148 struct sip_dialog
*dialog
;
3150 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
3152 session
= sipe_session_find_or_add_im(sip
, who
);
3153 dialog
= sipe_dialog_find(session
, who
);
3155 // Queue the message
3156 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
3158 if (dialog
&& dialog
->callid
) {
3159 sipe_im_process_queue(sip
, session
);
3160 } else if (!dialog
|| !dialog
->outgoing_invite
) {
3161 // Need to send the INVITE to get the outgoing dialog setup
3162 sipe_invite(sip
, session
, who
, what
, NULL
, FALSE
);
3168 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
, PurpleMessageFlags flags
)
3170 struct sipe_account_data
*sip
= gc
->proto_data
;
3171 struct sip_session
*session
;
3173 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
3175 session
= sipe_session_find_chat_by_id(sip
, id
);
3177 // Queue the message
3179 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
3181 sipe_im_process_queue(sip
, session
);
3187 /* End IM Session (INVITE and MESSAGE methods) */
3189 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3191 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
3192 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3193 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3195 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
3197 session
= sipe_session_find_im(sip
, from
);
3204 if (!strncmp(contenttype
, "application/x-ms-mim", 20)) {
3205 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3206 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
3207 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
3209 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
3211 if (xn_request_rm
) {
3212 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
3213 int bid
= atoi(xmlnode_get_attrib(xn_request_rm
, "bid"));
3214 gchar
*body
= g_strdup_printf(
3215 "<?xml version=\"1.0\"?>\r\n"
3216 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3217 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
3219 session
->bid
< bid
? "true" : "false");
3220 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3222 } else if (xn_set_rm
) {
3224 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
3225 g_free(session
->roster_manager
);
3226 session
->roster_manager
= g_strdup(rm
);
3228 body
= g_strdup_printf(
3229 "<?xml version=\"1.0\"?>\r\n"
3230 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3231 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
3233 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3236 xmlnode_free(xn_action
);
3239 /* looks like purple lacks typing notification for chat */
3240 if (!session
->is_multiparty
&& !session
->focus_uri
) {
3241 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
3244 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3249 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3251 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3252 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3253 struct sip_session
*session
;
3255 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3257 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3259 session
= sipe_session_find_im(sip
, from
);
3266 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
3267 g_free(session
->roster_manager
);
3268 session
->roster_manager
= NULL
;
3271 /* This what BYE is essentially for - terminating dialog */
3272 sipe_dialog_remove(session
, from
);
3273 if (session
->is_multiparty
) {
3274 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
3280 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3282 gchar
*self
= sip_uri_self(sip
);
3283 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3284 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3285 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
3286 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
3287 struct sip_session
*session
;
3288 struct sip_dialog
*dialog
;
3290 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3291 dialog
= sipe_dialog_find(session
, from
);
3293 if (!session
|| !dialog
|| !session
->roster_manager
|| strcmp(session
->roster_manager
, self
)) {
3294 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
3296 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
3298 sipe_invite(sip
, session
, refer_to
, NULL
, referred_by
, FALSE
);
3304 g_free(referred_by
);
3308 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
3310 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
3311 struct sip_session
*session
;
3312 struct sip_dialog
*dialog
;
3314 if (state
== PURPLE_NOT_TYPING
)
3317 session
= sipe_session_find_im(sip
, who
);
3318 dialog
= sipe_dialog_find(session
, who
);
3320 if (session
&& dialog
) {
3321 send_sip_request(gc
, "INFO", who
, who
,
3322 "Content-Type: application/xml\r\n",
3323 SIPE_SEND_TYPING
, dialog
, NULL
);
3325 return SIPE_TYPING_SEND_TIMEOUT
;
3328 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
3330 GSList
*tmp
= sip
->transactions
;
3331 time_t currtime
= time(NULL
);
3333 struct transaction
*trans
= tmp
->data
;
3335 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
3336 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
3339 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
3341 sendout_sipmsg(sip
, trans
->msg
);
3348 static void do_reauthenticate_cb(struct sipe_account_data
*sip
, void *unused
)
3350 /* register again when security token expires */
3351 /* we have to start a new authentication as the security token
3352 * is almost expired by sending a not signed REGISTER message */
3353 purple_debug_info("sipe", "do a full reauthentication\n");
3354 sipe_auth_free(&sip
->registrar
);
3355 sipe_auth_free(&sip
->proxy
);
3356 sip
->registerstatus
= 0;
3358 sip
->reauthenticate_set
= FALSE
;
3361 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3365 gboolean found
= FALSE
;
3367 from
= parse_from(sipmsg_find_header(msg
, "From"));
3371 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
3373 contenttype
= sipmsg_find_header(msg
, "Content-Type");
3374 if (!strncmp(contenttype
, "text/plain", 10)
3375 || !strncmp(contenttype
, "text/html", 9)
3376 || !strncmp(contenttype
, "multipart/related", 21))
3378 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3379 gchar
*html
= get_html_message(contenttype
, msg
->body
);
3381 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
3383 session
= sipe_session_find_im(sip
, from
);
3386 if (session
&& session
->focus_uri
) { /* a conference */
3387 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
3388 gchar
*sender
= parse_from(tmp
);
3390 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
3391 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3393 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
3394 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
3395 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3397 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3400 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3403 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
3404 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3409 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
3413 state
= xmlnode_get_child(isc
, "state");
3416 purple_debug_info("sipe", "process_incoming_message: no state found\n");
3421 statedata
= xmlnode_get_data(state
);
3423 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
3424 else serv_got_typing_stopped(sip
->gc
, from
);
3429 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3433 purple_debug_info("sipe", "got unknown mime-type");
3434 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
3439 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3441 gchar
*ms_text_format
;
3446 gboolean is_multiparty
= FALSE
;
3447 gboolean is_triggered
= FALSE
;
3448 gboolean was_multiparty
= TRUE
;
3449 gboolean just_joined
= FALSE
;
3451 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3452 gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
3453 gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
3454 gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
3455 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
3456 GSList
*end_points
= NULL
;
3457 struct sip_session
*session
;
3459 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? msg
->body
: "");
3461 /* Invitation to join conference */
3462 if (!strncmp(content_type
, "application/ms-conf-invite+xml", 30)) {
3463 process_incoming_invite_conf(sip
, msg
);
3467 /* Only accept text invitations */
3468 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
3469 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3473 // TODO There *must* be a better way to clean up the To header to add a tag...
3474 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
3475 oldHeader
= sipmsg_find_header(msg
, "To");
3477 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
3478 sipmsg_remove_header_now(msg
, "To");
3479 sipmsg_add_header_now(msg
, "To", newHeader
);
3482 if (end_points_hdr
) {
3483 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
3485 if (g_slist_length(end_points
) > 2) {
3486 is_multiparty
= TRUE
;
3489 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
3490 is_triggered
= TRUE
;
3491 is_multiparty
= TRUE
;
3494 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3495 /* Convert to multiparty */
3496 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
3497 g_free(session
->with
);
3498 session
->with
= NULL
;
3499 was_multiparty
= FALSE
;
3500 session
->is_multiparty
= TRUE
;
3501 session
->chat_id
= rand();
3504 if (!session
&& is_multiparty
) {
3505 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
3508 from
= parse_from(sipmsg_find_header(msg
, "From"));
3510 session
= sipe_session_find_or_add_im(sip
, from
);
3513 if (!session
->callid
) {
3514 session
->callid
= g_strdup(callid
);
3517 session
->is_multiparty
= is_multiparty
;
3518 if (roster_manager
) {
3519 session
->roster_manager
= g_strdup(roster_manager
);
3522 if (is_multiparty
&& end_points
) {
3523 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
3524 GSList
*entry
= end_points
;
3526 struct sip_dialog
*dialog
;
3527 struct sipendpoint
*end_point
= entry
->data
;
3528 entry
= entry
->next
;
3530 if (!g_strcasecmp(from
, end_point
->contact
) ||
3531 !g_strcasecmp(to
, end_point
->contact
))
3534 dialog
= sipe_dialog_find(session
, end_point
->contact
);
3536 g_free(dialog
->theirepid
);
3537 dialog
->theirepid
= end_point
->epid
;
3538 end_point
->epid
= NULL
;
3540 dialog
= sipe_dialog_add(session
);
3542 dialog
->callid
= g_strdup(session
->callid
);
3543 dialog
->with
= end_point
->contact
;
3544 end_point
->contact
= NULL
;
3545 dialog
->theirepid
= end_point
->epid
;
3546 end_point
->epid
= NULL
;
3550 /* send triggered INVITE */
3551 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, TRUE
);
3558 GSList
*entry
= end_points
;
3560 struct sipendpoint
*end_point
= entry
->data
;
3561 entry
= entry
->next
;
3562 g_free(end_point
->contact
);
3563 g_free(end_point
->epid
);
3566 g_slist_free(end_points
);
3570 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
3572 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
3574 dialog
= sipe_dialog_add(session
);
3576 dialog
->callid
= g_strdup(session
->callid
);
3577 dialog
->with
= g_strdup(from
);
3578 sipe_dialog_parse(dialog
, msg
, FALSE
);
3580 if (!dialog
->ourtag
) {
3581 dialog
->ourtag
= newTag
;
3588 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
3592 if (is_multiparty
&& !session
->conv
) {
3593 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++sip
->chat_seq
);
3594 gchar
*self
= sip_uri_self(sip
);
3595 /* create prpl chat */
3596 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_name
);
3597 session
->chat_name
= g_strdup(chat_name
);
3598 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
3600 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3602 PURPLE_CBFLAGS_NONE
, FALSE
);
3607 if (is_multiparty
&& !was_multiparty
) {
3608 /* add current IM counterparty to chat */
3609 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3610 sipe_dialog_first(session
)->with
, NULL
,
3611 PURPLE_CBFLAGS_NONE
, FALSE
);
3614 /* add inviting party */
3616 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3618 PURPLE_CBFLAGS_NONE
, TRUE
);
3621 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
3622 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
3623 if (ms_text_format
) {
3624 if (!strncmp(ms_text_format
, "text/plain", 10) || !strncmp(ms_text_format
, "text/html", 9)) {
3626 gchar
*html
= get_html_message(ms_text_format
, NULL
);
3628 if (is_multiparty
) {
3629 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
3630 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3632 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3635 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message received
3641 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
3642 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3643 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
3645 body
= g_strdup_printf(
3647 "o=- 0 0 IN IP4 %s\r\n"
3651 "m=message %d sip sip:%s\r\n"
3652 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3653 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
3654 sip
->realport
, sip
->username
);
3655 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3659 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3663 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
3664 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3665 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
3667 body
= g_strdup_printf(
3669 "o=- 0 0 IN IP4 0.0.0.0\r\n"
3671 "c=IN IP4 0.0.0.0\r\n"
3673 "m=message %d sip sip:%s\r\n"
3674 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3675 sip
->realport
, sip
->username
);
3676 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3680 static void sipe_connection_cleanup(struct sipe_account_data
*);
3681 static void create_connection(struct sipe_account_data
*, gchar
*, int);
3683 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3686 const gchar
*expires_header
;
3688 GSList
*hdr
= msg
->headers
;
3689 struct siphdrelement
*elem
;
3691 expires_header
= sipmsg_find_header(msg
, "Expires");
3692 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
3693 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
3695 switch (msg
->response
) {
3698 sip
->registerstatus
= 0;
3700 gchar
*contact_hdr
= NULL
;
3706 if (!sip
->reregister_set
) {
3707 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
3708 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
3709 g_free(action_name
);
3710 sip
->reregister_set
= TRUE
;
3713 sip
->registerstatus
= 3;
3716 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3718 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3721 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3724 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3725 fill_auth(sip
, tmp
, &sip
->registrar
);
3727 if (!sip
->reauthenticate_set
) {
3728 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
3729 guint reauth_timeout
;
3730 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
3731 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
3732 reauth_timeout
= sip
->registrar
.expires
- 300;
3734 /* NTLM: we have to reauthenticate as our security token expires
3735 after eight hours (be five minutes early) */
3736 reauth_timeout
= (8 * 3600) - 300;
3738 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
3739 g_free(action_name
);
3740 sip
->reauthenticate_set
= TRUE
;
3743 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
3745 epid
= get_epid(sip
);
3746 uuid
= generateUUIDfromEPID(epid
);
3749 // There can be multiple Contact headers (one per location where the user is logged in) so
3750 // make sure to only get the one for this uuid
3751 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
3752 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
3753 if (valid_contact
) {
3754 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
3755 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
3756 g_free(valid_contact
);
3759 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
3764 g_free(sip
->contact
);
3766 sip
->contact
= g_strdup_printf("<%s>", gruu
);
3769 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
3770 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
);
3772 sip
->msrtc_event_categories
= FALSE
;
3773 sip
->batched_support
= FALSE
;
3778 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
3779 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
3780 sip
->msrtc_event_categories
= TRUE
;
3781 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->msrtc_event_categories
);
3783 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
3784 sip
->batched_support
= TRUE
;
3785 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->batched_support
);
3788 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
3789 gchar
**caps
= g_strsplit(elem
->value
,",",0);
3792 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
3793 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
3798 hdr
= g_slist_next(hdr
);
3802 if (!sip
->subscribed
) { //do it just once, not every re-register
3804 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
3805 (GCompareFunc
)g_ascii_strcasecmp
)) {
3806 sipe_subscribe_roaming_contacts(sip
, msg
);
3809 /* For 2007+ it does not make sence to subscribe to:
3810 * vnd-microsoft-roaming-ACL
3811 * vnd-microsoft-provisioning (not v2)
3813 * These are for backward compatibility.
3815 if (sip
->msrtc_event_categories
)
3817 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
3818 (GCompareFunc
)g_ascii_strcasecmp
)) {
3819 sipe_subscribe_roaming_self(sip
, msg
);
3821 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
3822 (GCompareFunc
)g_ascii_strcasecmp
)) {
3823 sipe_subscribe_roaming_provisioning_v2(sip
, msg
);
3826 /* For 2005- servers */
3829 //sipe_options_request(sip, sip->sipdomain);
3831 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
3832 (GCompareFunc
)g_ascii_strcasecmp
)) {
3833 sipe_subscribe_roaming_acl(sip
, msg
);
3835 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
3836 (GCompareFunc
)g_ascii_strcasecmp
)) {
3837 sipe_subscribe_roaming_provisioning(sip
, msg
);
3839 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
3840 (GCompareFunc
)g_ascii_strcasecmp
)) {
3841 sipe_subscribe_presence_wpending(sip
, msg
);
3844 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
3845 sip
->subscribed
= TRUE
;
3848 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
3849 "timeout=", ";", NULL
);
3850 if (timeout
!= NULL
) {
3851 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
3852 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
3853 sip
->keepalive_timeout
);
3857 // Should we remove the transaction here?
3858 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
3859 transactions_remove(sip
, tc
);
3864 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
3866 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
3867 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
3871 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
3874 tmp
= g_strsplit(parts
[0], ":", 0);
3875 hostname
= g_strdup(tmp
[0]);
3876 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
3880 tmp
= g_strsplit(parts
[i
], "=", 0);
3882 if (g_strcasecmp("transport", tmp
[0]) == 0) {
3883 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
3884 transport
= SIPE_TRANSPORT_TCP
;
3885 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
3886 transport
= SIPE_TRANSPORT_UDP
;
3895 /* Close old connection */
3896 sipe_connection_cleanup(sip
);
3898 /* Create new connection */
3899 sip
->transport
= transport
;
3900 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
3901 hostname
, port
, TRANSPORT_DESCRIPTOR
);
3902 create_connection(sip
, hostname
, port
);
3908 if (sip
->registerstatus
!= 2) {
3909 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
3910 if (sip
->registrar
.retries
> 3) {
3911 sip
->gc
->wants_to_die
= TRUE
;
3912 purple_connection_error(sip
->gc
, _("Wrong Password"));
3916 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3918 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3921 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3924 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3925 fill_auth(sip
, tmp
, &sip
->registrar
);
3926 sip
->registerstatus
= 2;
3927 if (sip
->account
->disconnecting
) {
3928 do_register_exp(sip
, 0);
3936 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
3937 if (warning
!= NULL
) {
3939 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
3941 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
3942 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
3945 warning
= g_strdup(_("You have been rejected by the server"));
3948 sip
->gc
->wants_to_die
= TRUE
;
3949 purple_connection_error(sip
->gc
, warning
);
3956 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3957 if (warning
!= NULL
) {
3958 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3959 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
3962 warning
= g_strdup(_("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator"));
3965 sip
->gc
->wants_to_die
= TRUE
;
3966 purple_connection_error(sip
->gc
, warning
);
3973 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3974 if (warning
!= NULL
) {
3975 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3976 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
3979 warning
= g_strdup(_("Service unavailable: no reason given"));
3982 sip
->gc
->wants_to_die
= TRUE
;
3983 purple_connection_error(sip
->gc
, warning
);
3992 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3995 xmlnode
*xn_categories
;
3996 xmlnode
*xn_category
;
3998 const char *activity
= NULL
;
4000 xn_categories
= xmlnode_from_str(data
, len
);
4001 uri
= xmlnode_get_attrib(xn_categories
, "uri");
4003 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
4005 xn_category
= xmlnode_get_next_twin(xn_category
) )
4007 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
4009 if (!strcmp(attrVar
, "note"))
4012 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4017 xn_node
= xmlnode_get_child(xn_category
, "note");
4018 if (!xn_node
) continue;
4019 xn_node
= xmlnode_get_child(xn_node
, "body");
4020 if (!xn_node
) continue;
4021 note
= xmlnode_get_data(xn_node
);
4022 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s),note(%s)\n",uri
,note
? note
: "");
4023 g_free(sbuddy
->annotation
);
4024 sbuddy
->annotation
= NULL
;
4025 if (note
) sbuddy
->annotation
= g_strdup(note
);
4031 else if(!strcmp(attrVar
, "state"))
4035 xn_node
= xmlnode_get_child(xn_category
, "state");
4036 if (!xn_node
) continue;
4037 xn_node
= xmlnode_get_child(xn_node
, "availability");
4038 if (!xn_node
) continue;
4040 data
= xmlnode_get_data(xn_node
);
4045 activity
= SIPE_STATUS_ID_UNKNOWN
;
4046 else if (avail
< 4500)
4047 activity
= SIPE_STATUS_ID_AVAILABLE
;
4048 else if (avail
< 6000)
4049 activity
= SIPE_STATUS_ID_BRB
;
4050 else if (avail
< 7500)
4051 activity
= SIPE_STATUS_ID_ONPHONE
;
4052 else if (avail
< 9000)
4053 activity
= SIPE_STATUS_ID_BUSY
;
4054 else if (avail
< 12000)
4055 activity
= SIPE_STATUS_ID_DND
;
4056 else if (avail
< 18000)
4057 activity
= SIPE_STATUS_ID_AWAY
;
4059 activity
= SIPE_STATUS_ID_OFFLINE
;
4063 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", activity
);
4064 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
4067 xmlnode_free(xn_categories
);
4070 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
4072 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4073 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
4074 payload
->host
= g_strdup(host
);
4075 payload
->buddies
= server
;
4076 sipe_subscribe_presence_batched_routed(sip
, payload
);
4077 sipe_subscribe_presence_batched_routed_free(payload
);
4080 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4083 xmlnode
*xn_resource
;
4084 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
4089 xn_list
= xmlnode_from_str(data
, len
);
4091 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
4093 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
4095 const char *uri
, *state
;
4096 xmlnode
*xn_instance
;
4098 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
4099 if (!xn_instance
) continue;
4101 uri
= xmlnode_get_attrib(xn_resource
, "uri");
4102 state
= xmlnode_get_attrib(xn_instance
, "state");
4103 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
4105 if (strstr(state
, "resubscribe")) {
4106 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
4107 struct sipe_buddy
*sbuddy
;
4108 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
4109 gchar
*user
= g_strdup(uri
);
4110 host
= g_strdup(poolFqdn
);
4111 server
= g_hash_table_lookup(servers
, host
);
4112 server
= g_slist_append(server
, user
);
4113 g_hash_table_insert(servers
, host
, server
);
4115 sipe_subscribe_presence_single(sip
, (void *) uri
);
4117 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4119 sbuddy
->resubscribed
= TRUE
;
4124 /* Send out any deferred poolFqdn subscriptions */
4125 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
4126 g_hash_table_destroy(servers
);
4128 xmlnode_free(xn_list
);
4131 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4135 gchar
*activity
= NULL
;
4137 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
4138 gboolean isonline
= FALSE
;
4139 xmlnode
*display_name_node
;
4141 pidf
= xmlnode_from_str(data
, len
);
4143 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data
);
4147 uri
= xmlnode_get_attrib(pidf
, "entity");
4149 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
4151 if ((status
= xmlnode_get_child(tuple
, "status"))) {
4152 basicstatus
= xmlnode_get_child(status
, "basic");
4157 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
4162 getbasic
= xmlnode_get_data(basicstatus
);
4164 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
4169 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
4170 if (strstr(getbasic
, "open")) {
4175 display_name_node
= xmlnode_get_child(pidf
, "display-name");
4176 // updating display name if alias was just URI
4177 if (display_name_node
) {
4178 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
4179 GSList
*entry
= buddies
;
4180 PurpleBuddy
*p_buddy
;
4181 char * display_name
= xmlnode_get_data(display_name_node
);
4184 const char *server_alias
;
4187 p_buddy
= entry
->data
;
4189 alias
= (char *)purple_buddy_get_alias(p_buddy
);
4190 alias
= alias
? sip_uri_from_name(alias
) : NULL
;
4191 if (!alias
|| !g_ascii_strcasecmp(uri
, alias
)) {
4192 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
4193 purple_blist_alias_buddy(p_buddy
, display_name
);
4197 server_alias
= purple_buddy_get_server_alias(p_buddy
);
4199 ( (server_alias
&& strcmp(display_name
, server_alias
))
4200 || !server_alias
|| strlen(server_alias
) == 0 )
4202 purple_blist_server_alias_buddy(p_buddy
, display_name
);
4205 entry
= entry
->next
;
4207 g_free(display_name
);
4210 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
4211 if ((status
= xmlnode_get_child(tuple
, "status"))) {
4212 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
4213 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
4214 activity
= xmlnode_get_data(basicstatus
);
4215 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
4222 const gchar
* status_id
= NULL
;
4224 if (strstr(activity
, "busy")) {
4225 status_id
= SIPE_STATUS_ID_BUSY
;
4226 } else if (strstr(activity
, "away")) {
4227 status_id
= SIPE_STATUS_ID_AWAY
;
4232 status_id
= SIPE_STATUS_ID_AVAILABLE
;
4235 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
4236 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
4238 purple_prpl_got_user_status(sip
->account
, uri
, SIPE_STATUS_ID_OFFLINE
, NULL
);
4245 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4247 const char *availability
;
4248 const char *activity
;
4249 const char *display_name
= NULL
;
4250 const char *activity_name
= NULL
;
4255 struct sipe_buddy
*sbuddy
;
4257 xmlnode
*xn_presentity
= xmlnode_from_str(data
, len
);
4259 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
4260 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
4261 xmlnode
*xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
4262 xmlnode
*xn_email
= xmlnode_get_child(xn_presentity
, "email");
4263 const char *email
= xn_email
? xmlnode_get_attrib(xn_email
, "email") : NULL
;
4264 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
4265 xmlnode
*xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
):NULL
;
4266 const char *avail
= xn_state
? xmlnode_get_attrib(xn_state
, "avail") : NULL
;
4268 xmlnode
*xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
4269 char *note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
4270 xmlnode
*xn_devices
= xmlnode_get_child(xn_presentity
, "devices");
4271 xmlnode
*xn_device_presence
= xn_devices
? xmlnode_get_child(xn_devices
, "devicePresence") : NULL
;
4272 xmlnode
*xn_device_name
= xn_device_presence
? xmlnode_get_child(xn_device_presence
, "deviceName") : NULL
;
4273 const char *device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
4275 name
= xmlnode_get_attrib(xn_presentity
, "uri");
4276 uri
= sip_uri_from_name(name
);
4277 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
4278 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
4280 // updating display name if alias was just URI
4281 if (xn_display_name
) {
4282 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
4283 GSList
*entry
= buddies
;
4284 PurpleBuddy
*p_buddy
;
4285 display_name
= xmlnode_get_attrib(xn_display_name
, "displayName");
4288 const char *email_str
, *server_alias
;
4290 p_buddy
= entry
->data
;
4292 if (!g_ascii_strcasecmp(name
, purple_buddy_get_alias(p_buddy
))) {
4293 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
4294 purple_blist_alias_buddy(p_buddy
, display_name
);
4297 server_alias
= purple_buddy_get_server_alias(p_buddy
);
4299 ( (server_alias
&& strcmp(display_name
, server_alias
))
4300 || !server_alias
|| strlen(server_alias
) == 0 )
4302 purple_blist_server_alias_buddy(p_buddy
, display_name
);
4306 email_str
= purple_blist_node_get_string((PurpleBlistNode
*)p_buddy
, "email");
4307 if (!email_str
|| g_ascii_strcasecmp(email_str
, email
)) {
4308 purple_blist_node_set_string((PurpleBlistNode
*)p_buddy
, "email", email
);
4312 entry
= entry
->next
;
4316 avl
= atoi(availability
);
4317 act
= atoi(activity
);
4319 if(sip
->msrtc_event_categories
){
4320 if (act
== 100 && avl
== 0)
4321 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4322 else if (act
== 100 && avl
== 300)
4323 activity_name
= SIPE_STATUS_ID_AWAY
;
4324 else if (act
== 300 && avl
== 300)
4325 activity_name
= SIPE_STATUS_ID_BRB
;
4326 else if (act
== 400 && avl
== 300)
4327 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4328 else if (act
== 500 && act
== 300)
4329 activity_name
= SIPE_STATUS_ID_ONPHONE
;
4330 else if (act
== 600 && avl
== 300)
4331 activity_name
= SIPE_STATUS_ID_BUSY
;
4332 else if (act
== 0 && avl
== 0){ //MSRTC elements are zero
4333 if(avail
){ //Check for LegacyInterop elements
4336 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4337 else if (avl
== 3500)
4338 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4339 else if (avl
== 15500)
4340 activity_name
= SIPE_STATUS_ID_AWAY
;
4341 else if (avl
== 6500)
4342 activity_name
= SIPE_STATUS_ID_BUSY
;
4343 else if (avl
== 12500)
4344 activity_name
= SIPE_STATUS_ID_BRB
;
4349 if(activity_name
== NULL
){
4351 activity_name
= SIPE_STATUS_ID_AWAY
;
4352 else if (act
<= 150)
4353 activity_name
= SIPE_STATUS_ID_LUNCH
;
4354 else if (act
<= 300)
4355 activity_name
= SIPE_STATUS_ID_BRB
;
4356 else if (act
<= 400)
4357 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4358 else if (act
<= 500)
4359 activity_name
= SIPE_STATUS_ID_ONPHONE
;
4360 else if (act
<= 600)
4361 activity_name
= SIPE_STATUS_ID_BUSY
;
4363 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4366 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4369 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4372 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
4373 sbuddy
->annotation
= NULL
;
4374 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
4376 if (sbuddy
->device_name
) { g_free(sbuddy
->device_name
); }
4377 sbuddy
->device_name
= NULL
;
4378 if (device_name
) { sbuddy
->device_name
= g_strdup(device_name
); }
4381 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
4382 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
4384 xmlnode_free(xn_presentity
);
4388 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4390 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4392 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
4394 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
4395 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
4397 const char *content
= msg
->body
;
4398 unsigned length
= msg
->bodylen
;
4399 PurpleMimeDocument
*mime
= NULL
;
4401 if (strstr(ctype
, "multipart"))
4403 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
4404 const char *content_type
;
4406 mime
= purple_mime_document_parse(doc
);
4407 parts
= purple_mime_document_get_parts(mime
);
4409 content
= purple_mime_part_get_data(parts
->data
);
4410 length
= purple_mime_part_get_length(parts
->data
);
4411 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
4412 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
4414 process_incoming_notify_rlmi_resub(sip
, content
, length
);
4416 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
4418 process_incoming_notify_msrtc(sip
, content
, length
);
4422 process_incoming_notify_rlmi(sip
, content
, length
);
4424 parts
= parts
->next
;
4430 purple_mime_document_free(mime
);
4433 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
4435 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
4437 else if(strstr(ctype
, "application/rlmi+xml"))
4439 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
4442 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
4444 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
4448 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
4452 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
4454 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4455 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
4457 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
4460 strstr(ctype
, "multipart") &&
4461 (strstr(ctype
, "application/rlmi+xml") ||
4462 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
4463 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
4464 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
4465 GList
*parts
= purple_mime_document_get_parts(mime
);
4466 GSList
*buddies
= NULL
;
4467 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4470 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
4471 purple_mime_part_get_length(parts
->data
));
4472 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
4474 buddies
= g_slist_append(buddies
, uri
);
4477 parts
= parts
->next
;
4480 if (mime
) purple_mime_document_free(mime
);
4482 payload
->host
= who
;
4483 payload
->buddies
= buddies
;
4484 sipe_schedule_action(action_name
, timeout
,
4485 sipe_subscribe_presence_batched_routed
,
4486 sipe_subscribe_presence_batched_routed_free
,
4488 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
4491 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, NULL
, sip
, who
);
4492 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
4494 g_free(action_name
);
4498 * Dispatcher for all incoming subscription information
4499 * whether it comes from NOTIFY, BENOTIFY requests or
4500 * piggy-backed to subscription's OK responce.
4502 * @param request whether initiated from BE/NOTIFY request or OK-response message.
4503 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
4505 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
4507 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4508 gchar
*event
= sipmsg_find_header(msg
, "Event");
4509 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
4512 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event
? event
: "", msg
->body
);
4513 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n\n", subscription_state
? subscription_state
: "");
4515 /* implicit subscriptions */
4516 if (content_type
&& purple_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
4517 sipe_process_imdn(sip
, msg
);
4522 const gchar
*expires_header
;
4523 expires_header
= sipmsg_find_header(msg
, "Expires");
4524 timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
4525 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n\n", timeout
);
4526 timeout
= (timeout
- 60) > 60 ? (timeout
- 60) : timeout
; // 1 min ahead of expiration
4529 /* for one off subscriptions (send with Expire: 0) */
4530 if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning-v2"))
4532 sipe_process_provisioning_v2(sip
, msg
);
4535 if (!subscription_state
|| strstr(subscription_state
, "active"))
4537 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
4539 sipe_process_presence(sip
, msg
);
4541 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
4543 sipe_process_roaming_contacts(sip
, msg
, NULL
);
4545 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self"))
4547 sipe_process_roaming_self(sip
, msg
);
4549 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
4551 sipe_process_roaming_acl(sip
, msg
);
4553 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
4555 sipe_process_presence_wpending(sip
, msg
);
4557 else if (event
&& !g_ascii_strcasecmp(event
, "conference"))
4559 sipe_process_conference(sip
, msg
);
4563 //The server sends a (BE)NOTIFY with the status 'terminated'
4564 if (request
&& subscription_state
&& strstr(subscription_state
, "terminated") ) {
4565 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4566 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from
);
4570 if (timeout
&& event
) {// For LSC 2005 and OCS 2007
4571 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
4572 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
4574 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
4575 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_contacts, NULL, sip, msg);
4576 g_free(action_name);
4578 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
4579 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
4581 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
4582 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_acl, NULL, sip, msg);
4583 g_free(action_name);
4586 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
4587 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
4589 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
4590 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
4591 g_free(action_name
);
4593 else if (!g_ascii_strcasecmp(event
, "presence") &&
4594 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
4596 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
4597 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
4598 if(sip
->batched_support
) {
4599 gchar
*my_self
= sip_uri_self(sip
);
4600 if(!g_ascii_strcasecmp(who
, my_self
)){
4601 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_batched
, NULL
, sip
, NULL
);
4602 purple_debug_info("sipe", "Resubscription full batched list in %d\n",timeout
);
4603 g_free(who
); /* unused */
4606 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
4611 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, who
);
4612 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
,timeout
);
4614 g_free(action_name
);
4615 /* "who" will be freed by the action we just scheduled */
4619 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
4621 sipe_process_registration_notify(sip
, msg
);
4624 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
4625 if (request
&& !benotify
)
4627 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4634 static gchar* gen_xpidf(struct sipe_account_data *sip)
4636 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4638 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
4639 "<display name=\"sip:%s\"/>\r\n"
4640 "<atom id=\"1234\">\r\n"
4641 "<address uri=\"sip:%s\">\r\n"
4642 "<status status=\"%s\"/>\r\n"
4655 static gchar* gen_pidf(struct sipe_account_data *sip)
4657 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4658 "<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"
4659 "<tuple id=\"0\">\r\n"
4661 "<basic>open</basic>\r\n"
4662 "<ep:activities>\r\n"
4663 " <ep:activity>%s</ep:activity>\r\n"
4667 "<ci:display-name>%s</ci:display-name>\r\n"
4676 static void send_presence_soap(struct sipe_account_data
*sip
, const char * note
)
4678 int availability
= 300; // online
4679 int activity
= 400; // Available
4682 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
)) {
4684 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4686 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4688 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4690 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4692 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4694 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_INVISIBLE
) ||
4695 !strcmp(sip
->status
, SIPE_STATUS_ID_OFFLINE
)) {
4696 availability
= 0; // offline
4699 activity
= 400; // available
4702 name
= g_strdup_printf("sip: sip:%s", sip
->username
);
4703 //@TODO: send user data - state; add hostname in upper case
4704 body
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
4705 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
4711 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
4713 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4714 if (msg
->response
== 200) {
4715 sip
->status_version
= 0;
4716 send_presence_status(sip
);
4722 process_send_presence_category_publish_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
4724 if (msg
->response
== 409) {
4725 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4726 // TODO need to parse the version #'s?
4727 gchar
*uri
= sip_uri_self(sip
);
4728 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
4732 purple_debug_info("sipe", "process_send_presence_category_publish_response = %s\n", msg
->body
);
4734 tmp
= get_contact(sip
);
4735 hdr
= g_strdup_printf("Contact: %s\r\n"
4736 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4738 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
4748 static void send_presence_category_publish(struct sipe_account_data
*sip
, const char * note
)
4755 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
) ||
4756 !strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4758 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_DND
)) {
4760 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4762 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4764 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4766 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4768 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_UNKNOWN
)) {
4771 // Offline or invisible
4775 uri
= sip_uri_self(sip
);
4776 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
4777 sip
->status_version
, code
,
4778 sip
->status_version
, code
,
4779 sip
->status_version
, note
? note
: "",
4780 sip
->status_version
, note
? note
: "",
4781 sip
->status_version
, note
? note
: ""
4783 sip
->status_version
++;
4785 tmp
= get_contact(sip
);
4786 hdr
= g_strdup_printf("Contact: %s\r\n"
4787 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4789 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
4797 static void send_presence_status(struct sipe_account_data
*sip
)
4799 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
4801 if (!status
) return;
4803 note
= purple_status_get_attr_string(status
, "message");
4805 if(sip
->msrtc_event_categories
){
4806 send_presence_category_publish(sip
, note
);
4808 send_presence_soap(sip
, note
);
4812 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
4814 gboolean found
= FALSE
;
4815 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
4816 if (msg
->response
== 0) { /* request */
4817 if (!strcmp(msg
->method
, "MESSAGE")) {
4818 process_incoming_message(sip
, msg
);
4820 } else if (!strcmp(msg
->method
, "NOTIFY")) {
4821 purple_debug_info("sipe","send->process_incoming_notify\n");
4822 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
4824 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
4825 purple_debug_info("sipe","send->process_incoming_benotify\n");
4826 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
4828 } else if (!strcmp(msg
->method
, "INVITE")) {
4829 process_incoming_invite(sip
, msg
);
4831 } else if (!strcmp(msg
->method
, "REFER")) {
4832 process_incoming_refer(sip
, msg
);
4834 } else if (!strcmp(msg
->method
, "OPTIONS")) {
4835 process_incoming_options(sip
, msg
);
4837 } else if (!strcmp(msg
->method
, "INFO")) {
4838 process_incoming_info(sip
, msg
);
4840 } else if (!strcmp(msg
->method
, "ACK")) {
4841 // ACK's don't need any response
4843 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
4844 // LCS 2005 sends us these - just respond 200 OK
4846 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4847 } else if (!strcmp(msg
->method
, "BYE")) {
4848 process_incoming_bye(sip
, msg
);
4851 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
4853 } else { /* response */
4854 struct transaction
*trans
= transactions_find(sip
, msg
);
4856 if (msg
->response
== 407) {
4857 gchar
*resend
, *auth
, *ptmp
;
4859 if (sip
->proxy
.retries
> 30) return;
4860 sip
->proxy
.retries
++;
4861 /* do proxy authentication */
4863 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
4865 fill_auth(sip
, ptmp
, &sip
->proxy
);
4866 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
4867 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
4868 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
4870 resend
= sipmsg_to_string(trans
->msg
);
4871 /* resend request */
4872 sendout_pkt(sip
->gc
, resend
);
4875 if (msg
->response
== 100 || msg
->response
== 180) {
4876 /* ignore provisional response */
4877 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
4879 sip
->proxy
.retries
= 0;
4880 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
4881 if (msg
->response
== 401)
4883 sip
->registrar
.retries
++;
4887 sip
->registrar
.retries
= 0;
4889 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
4891 if (msg
->response
== 401) {
4892 gchar
*resend
, *auth
, *ptmp
;
4894 if (sip
->registrar
.retries
> 4) return;
4895 sip
->registrar
.retries
++;
4898 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4900 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
4903 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4907 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
4909 fill_auth(sip
, ptmp
, &sip
->registrar
);
4910 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
4911 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
4912 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
4914 //sipmsg_remove_header_now(trans->msg, "Authorization");
4915 //sipmsg_add_header(trans->msg, "Authorization", auth);
4917 resend
= sipmsg_to_string(trans
->msg
);
4918 /* resend request */
4919 sendout_pkt(sip
->gc
, resend
);
4924 if (trans
->callback
) {
4925 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
4926 /* call the callback to process response*/
4927 (trans
->callback
)(sip
, msg
, trans
);
4929 /* Not sure if this is needed or what needs to be done
4930 but transactions seem to be removed prematurely so
4931 this only removes them if the response is 200 OK */
4932 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
4933 /*Has a bug and it's unneccesary*/
4934 /*transactions_remove(sip, trans);*/
4940 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
4944 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
4948 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
4956 /* according to the RFC remove CRLF at the beginning */
4957 while (*cur
== '\r' || *cur
== '\n') {
4960 if (cur
!= conn
->inbuf
) {
4961 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
4962 conn
->inbufused
= strlen(conn
->inbuf
);
4965 /* Received a full Header? */
4966 sip
->processing_input
= TRUE
;
4967 while (sip
->processing_input
&&
4968 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
4969 time_t currtime
= time(NULL
);
4972 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
4973 msg
= sipmsg_parse_header(conn
->inbuf
);
4976 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
4977 if (restlen
>= msg
->bodylen
) {
4978 dummy
= g_malloc(msg
->bodylen
+ 1);
4979 memcpy(dummy
, cur
, msg
->bodylen
);
4980 dummy
[msg
->bodylen
] = '\0';
4982 cur
+= msg
->bodylen
;
4983 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
4984 conn
->inbufused
= strlen(conn
->inbuf
);
4986 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
4987 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
4993 purple_debug_info("sipe", "body:\n%s", msg->body);
4996 // Verify the signature before processing it
4997 if (sip
->registrar
.gssapi_context
) {
4998 struct sipmsg_breakdown msgbd
;
4999 gchar
*signature_input_str
;
5002 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
5003 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
5005 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
5007 if (rspauth
!= NULL
) {
5008 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
5009 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
5010 process_input_message(sip
, msg
);
5012 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
5013 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
5014 sip
->gc
->wants_to_die
= TRUE
;
5016 } else if (msg
->response
== 401) {
5017 purple_connection_error(sip
->gc
, _("Wrong Password"));
5018 sip
->gc
->wants_to_die
= TRUE
;
5020 g_free(signature_input_str
);
5023 sipmsg_breakdown_free(&msgbd
);
5025 process_input_message(sip
, msg
);
5032 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
5034 PurpleConnection
*gc
= data
;
5035 struct sipe_account_data
*sip
= gc
->proto_data
;
5040 static char buffer
[65536];
5041 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
5043 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
5044 msg
= sipmsg_parse_msg(buffer
);
5045 if (msg
) process_input_message(sip
, msg
);
5049 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
5051 struct sipe_account_data
*sip
= gc
->proto_data
;
5052 PurpleSslConnection
*gsc
= sip
->gsc
;
5054 purple_debug_error("sipe", "%s",debug
);
5055 purple_connection_error(gc
, msg
);
5057 /* Invalidate this connection. Next send will open a new one */
5059 connection_remove(sip
, gsc
->fd
);
5060 purple_ssl_close(gsc
);
5066 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
5068 PurpleConnection
*gc
= data
;
5069 struct sipe_account_data
*sip
;
5070 struct sip_connection
*conn
;
5072 gboolean firstread
= TRUE
;
5074 /* NOTE: This check *IS* necessary */
5075 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
5076 purple_ssl_close(gsc
);
5080 sip
= gc
->proto_data
;
5081 conn
= connection_find(sip
, gsc
->fd
);
5083 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
5084 gc
->wants_to_die
= TRUE
;
5085 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
5089 /* Read all available data from the SSL connection */
5091 /* Increase input buffer size as needed */
5092 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
5093 conn
->inbuflen
+= SIMPLE_BUF_INC
;
5094 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
5095 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
5098 /* Try to read as much as there is space left in the buffer */
5099 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
5100 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
5102 if (len
< 0 && errno
== EAGAIN
) {
5103 /* Try again later */
5105 } else if (len
< 0) {
5106 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
5108 } else if (firstread
&& (len
== 0)) {
5109 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
5113 conn
->inbufused
+= len
;
5116 /* Equivalence indicates that there is possibly more data to read */
5117 } while (len
== readlen
);
5119 conn
->inbuf
[conn
->inbufused
] = '\0';
5120 process_input(sip
, conn
);
5124 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
5126 PurpleConnection
*gc
= data
;
5127 struct sipe_account_data
*sip
= gc
->proto_data
;
5129 struct sip_connection
*conn
= connection_find(sip
, source
);
5131 purple_debug_error("sipe", "Connection not found!\n");
5135 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
5136 conn
->inbuflen
+= SIMPLE_BUF_INC
;
5137 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
5140 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
5142 if (len
< 0 && errno
== EAGAIN
)
5144 else if (len
<= 0) {
5145 purple_debug_info("sipe", "sipe_input_cb: read error\n");
5146 connection_remove(sip
, source
);
5147 if (sip
->fd
== source
) sip
->fd
= -1;
5151 conn
->inbufused
+= len
;
5152 conn
->inbuf
[conn
->inbufused
] = '\0';
5154 process_input(sip
, conn
);
5157 /* Callback for new connections on incoming TCP port */
5158 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
5160 PurpleConnection
*gc
= data
;
5161 struct sipe_account_data
*sip
= gc
->proto_data
;
5162 struct sip_connection
*conn
;
5164 int newfd
= accept(source
, NULL
, NULL
);
5166 conn
= connection_create(sip
, newfd
);
5168 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
5171 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
5173 PurpleConnection
*gc
= data
;
5174 struct sipe_account_data
*sip
;
5175 struct sip_connection
*conn
;
5177 if (!PURPLE_CONNECTION_IS_VALID(gc
))
5185 purple_connection_error(gc
, _("Could not connect"));
5189 sip
= gc
->proto_data
;
5191 sip
->last_keepalive
= time(NULL
);
5193 conn
= connection_create(sip
, source
);
5197 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
5200 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
5202 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
5203 if (sip
== NULL
) return;
5208 static guint
sipe_ht_hash_nick(const char *nick
)
5210 char *lc
= g_utf8_strdown(nick
, -1);
5211 guint bucket
= g_str_hash(lc
);
5217 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
5219 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
5222 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
5224 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5226 sip
->listen_data
= NULL
;
5228 if (listenfd
== -1) {
5229 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5235 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
5236 sip
->listenfd
= sip
->fd
;
5238 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
5240 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
5244 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
5246 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5248 sip
->query_data
= NULL
;
5250 if (!hosts
|| !hosts
->data
) {
5251 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
5255 hosts
= g_slist_remove(hosts
, hosts
->data
);
5256 g_free(sip
->serveraddr
);
5257 sip
->serveraddr
= hosts
->data
;
5258 hosts
= g_slist_remove(hosts
, hosts
->data
);
5260 hosts
= g_slist_remove(hosts
, hosts
->data
);
5261 g_free(hosts
->data
);
5262 hosts
= g_slist_remove(hosts
, hosts
->data
);
5265 /* create socket for incoming connections */
5266 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
5267 sipe_udp_host_resolved_listen_cb
, sip
);
5268 if (sip
->listen_data
== NULL
) {
5269 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5274 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
5277 PurpleConnection
*gc
= data
;
5278 struct sipe_account_data
*sip
;
5280 /* If the connection is already disconnected, we don't need to do anything else */
5281 if (!PURPLE_CONNECTION_IS_VALID(gc
))
5284 sip
= gc
->proto_data
;
5289 case PURPLE_SSL_CONNECT_FAILED
:
5290 purple_connection_error(gc
, _("Connection Failed"));
5292 case PURPLE_SSL_HANDSHAKE_FAILED
:
5293 purple_connection_error(gc
, _("SSL Handshake Failed"));
5295 case PURPLE_SSL_CERTIFICATE_INVALID
:
5296 purple_connection_error(gc
, _("SSL Certificate Invalid"));
5302 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
5304 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5305 PurpleProxyConnectData
*connect_data
;
5307 sip
->listen_data
= NULL
;
5309 sip
->listenfd
= listenfd
;
5310 if (sip
->listenfd
== -1) {
5311 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5315 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
5316 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
5317 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
5318 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
5319 sipe_newconn_cb
, sip
->gc
);
5320 purple_debug_info("sipe", "connecting to %s port %d\n",
5321 sip
->realhostname
, sip
->realport
);
5322 /* open tcp connection to the server */
5323 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
5324 sip
->realport
, login_cb
, sip
->gc
);
5326 if (connect_data
== NULL
) {
5327 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
5332 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
5334 PurpleAccount
*account
= sip
->account
;
5335 PurpleConnection
*gc
= sip
->gc
;
5337 if (purple_account_get_bool(account
, "useport", FALSE
)) {
5338 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
5339 port
= purple_account_get_int(account
, "port", 0);
5341 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
5344 sip
->realhostname
= hostname
;
5345 sip
->realport
= port
;
5347 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
5350 /* TODO: is there a good default grow size? */
5351 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
5352 sip
->txbuf
= purple_circ_buffer_new(0);
5354 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
5356 if (!purple_ssl_is_supported()) {
5357 gc
->wants_to_die
= TRUE
;
5358 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
5362 purple_debug_info("sipe", "using SSL\n");
5364 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
5365 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
5366 if (sip
->gsc
== NULL
) {
5367 purple_connection_error(gc
, _("Could not create SSL context"));
5370 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
5372 purple_debug_info("sipe", "using UDP\n");
5374 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
5375 if (sip
->query_data
== NULL
) {
5376 purple_connection_error(gc
, _("Could not resolve hostname"));
5380 purple_debug_info("sipe", "using TCP\n");
5381 /* create socket for incoming connections */
5382 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
5383 sipe_tcp_connect_listen_cb
, sip
);
5384 if (sip
->listen_data
== NULL
) {
5385 purple_connection_error(gc
, _("Could not create listen socket"));
5391 /* Service list for autodection */
5392 static const struct sipe_service_data service_autodetect
[] = {
5393 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
5394 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
5395 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
5396 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
5400 /* Service list for SSL/TLS */
5401 static const struct sipe_service_data service_tls
[] = {
5402 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
5403 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
5407 /* Service list for TCP */
5408 static const struct sipe_service_data service_tcp
[] = {
5409 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
5410 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
5414 /* Service list for UDP */
5415 static const struct sipe_service_data service_udp
[] = {
5416 { "sip", "udp", SIPE_TRANSPORT_UDP
},
5420 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
5421 static void resolve_next_service(struct sipe_account_data
*sip
,
5422 const struct sipe_service_data
*start
)
5425 sip
->service_data
= start
;
5427 sip
->service_data
++;
5428 if (sip
->service_data
->service
== NULL
) {
5430 /* Try connecting to the SIP hostname directly */
5431 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
5432 if (sip
->auto_transport
) {
5433 // If SSL is supported, default to using it; OCS servers aren't configured
5434 // by default to accept TCP
5435 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
5436 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
5437 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
5440 hostname
= g_strdup(sip
->sipdomain
);
5441 create_connection(sip
, hostname
, 0);
5446 /* Try to resolve next service */
5447 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
5448 sip
->service_data
->transport
,
5453 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
5455 struct sipe_account_data
*sip
= data
;
5457 sip
->srv_query_data
= NULL
;
5459 /* find the host to connect to */
5461 gchar
*hostname
= g_strdup(resp
->hostname
);
5462 int port
= resp
->port
;
5463 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
5467 sip
->transport
= sip
->service_data
->type
;
5469 create_connection(sip
, hostname
, port
);
5471 resolve_next_service(sip
, NULL
);
5475 static void sipe_login(PurpleAccount
*account
)
5477 PurpleConnection
*gc
;
5478 struct sipe_account_data
*sip
;
5479 gchar
**signinname_login
, **userserver
, **domain_user
;
5480 const char *transport
;
5482 const char *username
= purple_account_get_username(account
);
5483 gc
= purple_account_get_connection(account
);
5485 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
5486 gc
->wants_to_die
= TRUE
;
5487 purple_connection_error(gc
, _("SIP Exchange username contains invalid characters"));
5491 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
5492 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
5493 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
5495 sip
->account
= account
;
5496 sip
->reregister_set
= FALSE
;
5497 sip
->reauthenticate_set
= FALSE
;
5498 sip
->subscribed
= FALSE
;
5499 sip
->subscribed_buddies
= FALSE
;
5501 signinname_login
= g_strsplit(username
, ",", 2);
5503 userserver
= g_strsplit(signinname_login
[0], "@", 2);
5504 purple_connection_set_display_name(gc
, userserver
[0]);
5505 sip
->username
= g_strjoin("@", userserver
[0], userserver
[1], NULL
);
5506 sip
->sipdomain
= g_strdup(userserver
[1]);
5508 if (strpbrk(sip
->username
, " \t\v\r\n") != NULL
) {
5509 gc
->wants_to_die
= TRUE
;
5510 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
5514 domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
5515 sip
->authdomain
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[0]) : NULL
;
5516 sip
->authuser
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[1]) : (signinname_login
? g_strdup(signinname_login
[1]) : NULL
);
5518 sip
->password
= g_strdup(purple_connection_get_password(gc
));
5520 g_strfreev(userserver
);
5521 g_strfreev(domain_user
);
5522 g_strfreev(signinname_login
);
5524 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
5526 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
5528 /* TODO: Set the status correctly. */
5529 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
5531 transport
= purple_account_get_string(account
, "transport", "auto");
5532 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
5533 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
5536 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
5537 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
5538 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
5539 } else if (strcmp(transport
, "auto") == 0) {
5540 sip
->auto_transport
= TRUE
;
5541 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
5542 } else if (strcmp(transport
, "tls") == 0) {
5543 resolve_next_service(sip
, service_tls
);
5544 } else if (strcmp(transport
, "tcp") == 0) {
5545 resolve_next_service(sip
, service_tcp
);
5547 resolve_next_service(sip
, service_udp
);
5551 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
5553 connection_free_all(sip
);
5558 if (sip
->query_data
!= NULL
)
5559 purple_dnsquery_destroy(sip
->query_data
);
5560 sip
->query_data
= NULL
;
5562 if (sip
->srv_query_data
!= NULL
)
5563 purple_srv_cancel(sip
->srv_query_data
);
5564 sip
->srv_query_data
= NULL
;
5566 if (sip
->listen_data
!= NULL
)
5567 purple_network_listen_cancel(sip
->listen_data
);
5568 sip
->listen_data
= NULL
;
5570 if (sip
->gsc
!= NULL
)
5571 purple_ssl_close(sip
->gsc
);
5574 sipe_auth_free(&sip
->registrar
);
5575 sipe_auth_free(&sip
->proxy
);
5578 purple_circ_buffer_destroy(sip
->txbuf
);
5581 g_free(sip
->realhostname
);
5582 sip
->realhostname
= NULL
;
5585 purple_input_remove(sip
->listenpa
);
5587 if (sip
->tx_handler
)
5588 purple_input_remove(sip
->tx_handler
);
5589 sip
->tx_handler
= 0;
5590 if (sip
->resendtimeout
)
5591 purple_timeout_remove(sip
->resendtimeout
);
5592 sip
->resendtimeout
= 0;
5593 if (sip
->timeouts
) {
5594 GSList
*entry
= sip
->timeouts
;
5596 struct scheduled_action
*sched_action
= entry
->data
;
5597 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
5598 purple_timeout_remove(sched_action
->timeout_handler
);
5599 if (sched_action
->destroy
) {
5600 (*sched_action
->destroy
)(sched_action
->payload
);
5602 g_free(sched_action
->name
);
5603 g_free(sched_action
);
5604 entry
= entry
->next
;
5607 g_slist_free(sip
->timeouts
);
5609 if (sip
->allow_events
) {
5610 GSList
*entry
= sip
->allow_events
;
5612 g_free(entry
->data
);
5613 entry
= entry
->next
;
5616 g_slist_free(sip
->allow_events
);
5618 if (sip
->containers
) {
5619 GSList
*entry
= sip
->containers
;
5621 free_container((struct sipe_container
*)entry
->data
);
5622 entry
= entry
->next
;
5625 g_slist_free(sip
->containers
);
5628 g_free(sip
->contact
);
5629 sip
->contact
= NULL
;
5631 g_free(sip
->regcallid
);
5632 sip
->regcallid
= NULL
;
5634 if (sip
->serveraddr
)
5635 g_free(sip
->serveraddr
);
5636 sip
->serveraddr
= NULL
;
5638 if (sip
->focus_factory_uri
)
5639 g_free(sip
->focus_factory_uri
);
5640 sip
->focus_factory_uri
= NULL
;
5643 sip
->processing_input
= FALSE
;
5647 * A callback for g_hash_table_foreach_remove
5649 static gboolean
sipe_buddy_remove(gpointer key
, gpointer buddy
, gpointer user_data
)
5651 sipe_free_buddy((struct sipe_buddy
*) buddy
);
5653 /* We must return TRUE as the key/value have already been deleted */
5657 static void sipe_close(PurpleConnection
*gc
)
5659 struct sipe_account_data
*sip
= gc
->proto_data
;
5662 /* leave all conversations */
5663 sipe_session_remove_all(sip
);
5666 do_register_exp(sip
, 0);
5668 sipe_connection_cleanup(sip
);
5669 g_free(sip
->sipdomain
);
5670 g_free(sip
->username
);
5671 g_free(sip
->password
);
5672 g_free(sip
->authdomain
);
5673 g_free(sip
->authuser
);
5674 g_free(sip
->status
);
5676 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
5677 g_hash_table_destroy(sip
->buddies
);
5679 g_free(gc
->proto_data
);
5680 gc
->proto_data
= NULL
;
5683 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
5685 PurpleAccount
*acct
= purple_connection_get_account(gc
);
5686 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
5687 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
5689 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
5690 purple_conversation_present(conv
);
5694 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
5697 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
5698 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
5701 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,struct transaction
*tc
)
5703 PurpleNotifySearchResults
*results
;
5704 PurpleNotifySearchColumn
*column
;
5705 xmlnode
*searchResults
;
5707 int match_count
= 0;
5708 gboolean more
= FALSE
;
5711 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
5713 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5714 if (!searchResults
) {
5715 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
5719 results
= purple_notify_searchresults_new();
5721 if (results
== NULL
) {
5722 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
5723 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
5725 xmlnode_free(searchResults
);
5729 column
= purple_notify_searchresults_column_new(_("User Name"));
5730 purple_notify_searchresults_column_add(results
, column
);
5732 column
= purple_notify_searchresults_column_new(_("Name"));
5733 purple_notify_searchresults_column_add(results
, column
);
5735 column
= purple_notify_searchresults_column_new(_("Company"));
5736 purple_notify_searchresults_column_add(results
, column
);
5738 column
= purple_notify_searchresults_column_new(_("Country"));
5739 purple_notify_searchresults_column_add(results
, column
);
5741 column
= purple_notify_searchresults_column_new(_("Email"));
5742 purple_notify_searchresults_column_add(results
, column
);
5744 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
5747 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
5748 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
5749 g_strfreev(uri_parts
);
5751 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
5752 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
5753 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
5754 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
5756 purple_notify_searchresults_row_add(results
, row
);
5760 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
5761 char *data
= xmlnode_get_data_unescaped(mrow
);
5762 more
= (g_strcasecmp(data
, "true") == 0);
5766 secondary
= g_strdup_printf(
5767 dngettext(GETTEXT_PACKAGE
,
5768 "Found %d contact%s:",
5769 "Found %d contacts%s:", match_count
),
5770 match_count
, more
? _(" (more matched your query)") : "");
5772 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
5773 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
5774 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
5777 xmlnode_free(searchResults
);
5781 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
5783 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
5784 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
5788 PurpleRequestField
*field
= entries
->data
;
5789 const char *id
= purple_request_field_get_id(field
);
5790 const char *value
= purple_request_field_string_get_value(field
);
5792 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
5794 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
5795 } while ((entries
= g_list_next(entries
)) != NULL
);
5799 gchar
*query
= g_strjoinv(NULL
, attrs
);
5800 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
5801 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
5802 send_soap_request_with_cb(gc
->proto_data
, body
,
5803 (TransCallback
) process_search_contact_response
, NULL
);
5811 static void sipe_show_find_contact(PurplePluginAction
*action
)
5813 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
5814 PurpleRequestFields
*fields
;
5815 PurpleRequestFieldGroup
*group
;
5816 PurpleRequestField
*field
;
5818 fields
= purple_request_fields_new();
5819 group
= purple_request_field_group_new(NULL
);
5820 purple_request_fields_add_group(fields
, group
);
5822 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
5823 purple_request_field_group_add_field(group
, field
);
5824 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
5825 purple_request_field_group_add_field(group
, field
);
5826 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
5827 purple_request_field_group_add_field(group
, field
);
5828 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
5829 purple_request_field_group_add_field(group
, field
);
5831 purple_request_fields(gc
,
5833 _("Search for a Contact"),
5834 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
5836 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
5838 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
5841 GList
*sipe_actions(PurplePlugin
*plugin
, gpointer context
)
5844 PurplePluginAction
*act
;
5846 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
5847 menu
= g_list_prepend(menu
, act
);
5849 menu
= g_list_reverse(menu
);
5854 static void dummy_permit_deny(PurpleConnection
*gc
)
5858 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
5864 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
5870 static char *sipe_status_text(PurpleBuddy
*buddy
)
5872 struct sipe_account_data
*sip
;
5873 struct sipe_buddy
*sbuddy
;
5876 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
5877 if (sip
) //happens on pidgin exit
5879 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
5880 if (sbuddy
&& sbuddy
->annotation
)
5882 text
= g_strdup(sbuddy
->annotation
);
5889 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
5891 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
5892 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
5893 struct sipe_account_data
*sip
;
5894 struct sipe_buddy
*sbuddy
;
5895 char *annotation
= NULL
;
5897 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
5898 if (sip
) //happens on pidgin exit
5900 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
5903 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
5908 if (purple_presence_is_online(presence
))
5910 purple_notify_user_info_add_pair(user_info
, _("Status"), purple_status_get_name(status
));
5915 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
5922 sipe_get_account_text_table(PurpleAccount
*account
)
5925 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
5926 g_hash_table_insert(table
, "login_label", (gpointer
)_("Sign-In Name..."));
5930 static PurpleBuddy
*
5931 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
5934 const gchar
*server_alias
, *email
;
5935 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
5937 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
5939 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
5941 server_alias
= g_strdup(purple_buddy_get_server_alias(buddy
));
5943 purple_blist_server_alias_buddy(clone
, server_alias
);
5946 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
5948 purple_blist_node_set_string((PurpleBlistNode
*)clone
, "email", email
);
5951 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
5953 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
5958 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
5960 PurpleBuddy
*buddy
, *b
;
5961 PurpleConnection
*gc
;
5962 PurpleGroup
* group
= purple_find_group(group_name
);
5964 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
5966 buddy
= (PurpleBuddy
*)node
;
5968 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
5969 gc
= purple_account_get_connection(buddy
->account
);
5971 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
5973 b
= purple_blist_add_buddy_clone(group
, buddy
);
5976 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
5980 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
5982 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
5984 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: buddy->name=%s\n", buddy
->name
);
5986 /* 2007+ conference */
5987 if (sip
->msrtc_event_categories
)
5989 sipe_conf_add(sip
, buddy
->name
);
5991 else /* 2005- multiparty chat */
5993 gchar
*self
= sip_uri_self(sip
);
5994 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++sip
->chat_seq
);
5995 struct sip_session
*session
;
5997 session
= sipe_session_add_chat(sip
);
5998 session
->roster_manager
= g_strdup(self
);
6000 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, g_strdup(chat_name
));
6001 session
->chat_name
= g_strdup(chat_name
);
6002 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
6003 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
6004 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, FALSE
);
6012 sipe_is_election_finished(struct sipe_account_data
*sip
,
6013 struct sip_session
*session
)
6015 gboolean res
= TRUE
;
6017 SIPE_DIALOG_FOREACH
{
6018 if (dialog
->election_vote
== 0) {
6022 } SIPE_DIALOG_FOREACH_END
;
6025 session
->is_voting_in_progress
= FALSE
;
6031 sipe_election_start(struct sipe_account_data
*sip
,
6032 struct sip_session
*session
)
6034 int election_timeout
;
6036 if (session
->is_voting_in_progress
) {
6037 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
6040 session
->is_voting_in_progress
= TRUE
;
6042 session
->bid
= rand();
6044 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
6046 SIPE_DIALOG_FOREACH
{
6047 /* reset election_vote for each chat participant */
6048 dialog
->election_vote
= 0;
6050 /* send RequestRM to each chat participant*/
6051 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
6052 } SIPE_DIALOG_FOREACH_END
;
6054 election_timeout
= 15; /* sec */
6055 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
6059 * @param who a URI to whom to invite to chat
6062 sipe_invite_to_chat(struct sipe_account_data
*sip
,
6063 struct sip_session
*session
,
6067 if (session
->focus_uri
)
6069 sipe_invite_conf(sip
, session
, who
);
6071 else /* a multi-party chat */
6073 gchar
*self
= sip_uri_self(sip
);
6074 if (session
->roster_manager
) {
6075 if (!strcmp(session
->roster_manager
, self
)) {
6076 sipe_invite(sip
, session
, who
, NULL
, NULL
, FALSE
);
6078 sipe_refer(sip
, session
, who
);
6081 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: no RM available\n");
6083 session
->pending_invite_queue
= slist_insert_unique_sorted(
6084 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
6086 sipe_election_start(sip
, session
);
6093 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
6094 struct sip_session
*session
)
6097 GSList
*entry
= session
->pending_invite_queue
;
6100 invitee
= entry
->data
;
6101 sipe_invite_to_chat(sip
, session
, invitee
);
6102 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
6108 sipe_election_result(struct sipe_account_data
*sip
,
6111 struct sip_session
*session
= (struct sip_session
*)sess
;
6113 gboolean has_won
= TRUE
;
6115 if (session
->roster_manager
) {
6116 purple_debug_info("sipe",
6117 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
6121 session
->is_voting_in_progress
= FALSE
;
6123 SIPE_DIALOG_FOREACH
{
6124 if (dialog
->election_vote
< 0) {
6126 rival
= dialog
->with
;
6129 } SIPE_DIALOG_FOREACH_END
;
6132 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
6134 session
->roster_manager
= sip_uri_self(sip
);
6136 SIPE_DIALOG_FOREACH
{
6137 /* send SetRM to each chat participant*/
6138 sipe_send_election_set_rm(sip
, dialog
);
6139 } SIPE_DIALOG_FOREACH_END
;
6141 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
6145 sipe_process_pending_invite_queue(sip
, session
);
6149 * For 2007+ conference only.
6152 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_name
)
6154 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6155 struct sip_session
*session
;
6157 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
6158 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_name=%s\n", chat_name
);
6160 session
= sipe_session_find_chat_by_name(sip
, chat_name
);
6162 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
6166 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, const char *chat_name
)
6168 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6169 struct sip_session
*session
;
6171 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: buddy->name=%s\n", buddy
->name
);
6172 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: chat_name=%s\n", chat_name
);
6174 session
= sipe_session_find_chat_by_name(sip
, chat_name
);
6176 sipe_invite_to_chat(sip
, session
, buddy
->name
);
6180 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
6183 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
6185 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
6188 char *mailto
= g_strdup_printf("mailto:%s", email
);
6189 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
6193 char *const parmList
[] = {mailto
, NULL
};
6194 if ((pid
= fork()) == -1)
6196 purple_debug_info("sipe", "fork() error\n");
6200 execvp("xdg-email", parmList
);
6201 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
6209 //@TODO resolve env variable %WINDIR% first
6210 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
6213 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
6222 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
6227 * A menu which appear when right-clicking on buddy in contact list.
6230 sipe_buddy_menu(PurpleBuddy
*buddy
)
6232 PurpleBlistNode
*g_node
;
6233 PurpleGroup
*group
, *gr_parent
;
6234 PurpleMenuAction
*act
;
6236 GList
*menu_groups
= NULL
;
6237 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6238 gchar
*self
= sip_uri_self(sip
);
6240 SIPE_SESSION_FOREACH
{
6241 if (strcmp(self
, buddy
->name
) && session
->chat_name
&& session
->conv
)
6243 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
6245 PurpleConvChatBuddyFlags flags
;
6246 PurpleConvChatBuddyFlags flags_us
;
6248 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
6249 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
6250 if (session
->focus_uri
&&
6251 PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) && /* Not conf OP */
6252 PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
6254 gchar
*label
= g_strdup_printf(_("Make Leader of '%s'"), session
->chat_name
);
6255 act
= purple_menu_action_new(label
,
6256 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
6257 g_strdup(session
->chat_name
), NULL
);
6259 menu
= g_list_prepend(menu
, act
);
6264 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_name
);
6265 act
= purple_menu_action_new(label
,
6266 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
6267 g_strdup(session
->chat_name
), NULL
);
6269 menu
= g_list_prepend(menu
, act
);
6272 } SIPE_SESSION_FOREACH_END
;
6274 act
= purple_menu_action_new(_("New Chat"),
6275 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
6277 menu
= g_list_prepend(menu
, act
);
6279 act
= purple_menu_action_new(_("Send Email..."),
6280 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
6282 menu
= g_list_prepend(menu
, act
);
6284 gr_parent
= purple_buddy_get_group(buddy
);
6285 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
6286 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
6289 group
= (PurpleGroup
*)g_node
;
6290 if (group
== gr_parent
)
6293 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
6296 act
= purple_menu_action_new(purple_group_get_name(group
),
6297 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
6299 menu_groups
= g_list_prepend(menu_groups
, act
);
6301 menu_groups
= g_list_reverse(menu_groups
);
6303 act
= purple_menu_action_new(_("Copy to"),
6306 menu
= g_list_prepend(menu
, act
);
6307 menu
= g_list_reverse(menu
);
6314 sipe_blist_node_menu(PurpleBlistNode
*node
)
6316 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
6317 return sipe_buddy_menu((PurpleBuddy
*) node
);
6324 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
6326 gboolean ret
= TRUE
;
6327 char *username
= (char *)trans
->payload
;
6329 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
6330 PurpleBuddy
*pbuddy
;
6331 struct sipe_buddy
*sbuddy
;
6333 char *server_alias
= NULL
;
6335 const char *device_name
= NULL
;
6337 purple_debug_info("sipe", "Fetching %s's user info for %s\n", username
, sip
->username
);
6339 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, username
);
6340 alias
= purple_buddy_get_local_alias(pbuddy
);
6344 //will query buddy UA's capabilities and send answer to log
6345 sipe_options_request(sip
, username
);
6347 sbuddy
= g_hash_table_lookup(sip
->buddies
, username
);
6350 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
6354 if (msg
->response
!= 200) {
6355 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
6357 xmlnode
*searchResults
;
6360 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
6361 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
6362 if (!searchResults
) {
6363 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
6364 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
6365 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
6366 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
6367 purple_notify_user_info_add_pair(info
, _("Job Title"), g_strdup(xmlnode_get_attrib(mrow
, "title")));
6368 purple_notify_user_info_add_pair(info
, _("Office"), g_strdup(xmlnode_get_attrib(mrow
, "office")));
6369 purple_notify_user_info_add_pair(info
, _("Business Phone"), g_strdup(xmlnode_get_attrib(mrow
, "phone")));
6370 purple_notify_user_info_add_pair(info
, _("Company"), g_strdup(xmlnode_get_attrib(mrow
, "company")));
6371 purple_notify_user_info_add_pair(info
, _("City"), g_strdup(xmlnode_get_attrib(mrow
, "city")));
6372 purple_notify_user_info_add_pair(info
, _("State"), g_strdup(xmlnode_get_attrib(mrow
, "state")));
6373 purple_notify_user_info_add_pair(info
, _("Country"), g_strdup(xmlnode_get_attrib(mrow
, "country")));
6374 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
6375 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
6376 if (!email
|| strcmp("", email
)) {
6377 if (!purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email")) {
6378 purple_blist_node_set_string((PurpleBlistNode
*)pbuddy
, "email", email
);
6382 xmlnode_free(searchResults
);
6385 purple_notify_user_info_add_section_break(info
);
6387 if (!server_alias
|| !strcmp("", server_alias
)) {
6388 g_free(server_alias
);
6389 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
6391 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
6395 // same as server alias, do not present
6396 alias
= (alias
&& server_alias
&& !strcmp(alias
, server_alias
)) ? NULL
: alias
;
6399 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
6402 if (!email
|| !strcmp("", email
)) {
6404 email
= g_strdup(purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email"));
6406 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
6412 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
6415 /* show a buddy's user info in a nice dialog box */
6416 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
6417 username
, /* buddy's username */
6419 NULL
, /* callback called when dialog closed */
6420 NULL
); /* userdata for callback */
6426 * AD search first, LDAP based
6428 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
6430 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
6431 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
6433 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
6434 send_soap_request_with_cb((struct sipe_account_data
*)gc
->proto_data
, body
,
6435 (TransCallback
) process_get_info_response
, (gpointer
)g_strdup(username
));
6440 static PurplePlugin
*my_protocol
= NULL
;
6442 static PurplePluginProtocolInfo prpl_info
=
6444 OPT_PROTO_CHAT_TOPIC
,
6445 NULL
, /* user_splits */
6446 NULL
, /* protocol_options */
6447 NO_BUDDY_ICONS
, /* icon_spec */
6448 sipe_list_icon
, /* list_icon */
6449 NULL
, /* list_emblems */
6450 sipe_status_text
, /* status_text */
6451 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
6452 sipe_status_types
, /* away_states */
6453 sipe_blist_node_menu
, /* blist_node_menu */
6454 NULL
, /* chat_info */
6455 NULL
, /* chat_info_defaults */
6456 sipe_login
, /* login */
6457 sipe_close
, /* close */
6458 sipe_im_send
, /* send_im */
6459 NULL
, /* set_info */ // TODO maybe
6460 sipe_send_typing
, /* send_typing */
6461 sipe_get_info
, /* get_info */
6462 sipe_set_status
, /* set_status */
6463 NULL
, /* set_idle */
6464 NULL
, /* change_passwd */
6465 sipe_add_buddy
, /* add_buddy */
6466 NULL
, /* add_buddies */
6467 sipe_remove_buddy
, /* remove_buddy */
6468 NULL
, /* remove_buddies */
6469 sipe_add_permit
, /* add_permit */
6470 sipe_add_deny
, /* add_deny */
6471 sipe_add_deny
, /* rem_permit */
6472 sipe_add_permit
, /* rem_deny */
6473 dummy_permit_deny
, /* set_permit_deny */
6474 NULL
, /* join_chat */
6475 NULL
, /* reject_chat */
6476 NULL
, /* get_chat_name */
6477 sipe_chat_invite
, /* chat_invite */
6478 sipe_chat_leave
, /* chat_leave */
6479 NULL
, /* chat_whisper */
6480 sipe_chat_send
, /* chat_send */
6481 sipe_keep_alive
, /* keepalive */
6482 NULL
, /* register_user */
6483 NULL
, /* get_cb_info */ // deprecated
6484 NULL
, /* get_cb_away */ // deprecated
6485 sipe_alias_buddy
, /* alias_buddy */
6486 sipe_group_buddy
, /* group_buddy */
6487 sipe_rename_group
, /* rename_group */
6488 NULL
, /* buddy_free */
6489 sipe_convo_closed
, /* convo_closed */
6490 purple_normalize_nocase
, /* normalize */
6491 NULL
, /* set_buddy_icon */
6492 sipe_remove_group
, /* remove_group */
6493 NULL
, /* get_cb_real_name */ // TODO?
6494 NULL
, /* set_chat_topic */
6495 NULL
, /* find_blist_chat */
6496 NULL
, /* roomlist_get_list */
6497 NULL
, /* roomlist_cancel */
6498 NULL
, /* roomlist_expand_category */
6499 NULL
, /* can_receive_file */
6500 NULL
, /* send_file */
6501 NULL
, /* new_xfer */
6502 NULL
, /* offline_message */
6503 NULL
, /* whiteboard_prpl_ops */
6504 sipe_send_raw
, /* send_raw */
6505 NULL
, /* roomlist_room_serialize */
6506 NULL
, /* unregister_user */
6507 NULL
, /* send_attention */
6508 NULL
, /* get_attention_types */
6510 sizeof(PurplePluginProtocolInfo
), /* struct_size */
6511 sipe_get_account_text_table
, /* get_account_text_table */
6515 static PurplePluginInfo info
= {
6516 PURPLE_PLUGIN_MAGIC
,
6517 PURPLE_MAJOR_VERSION
,
6518 PURPLE_MINOR_VERSION
,
6519 PURPLE_PLUGIN_PROTOCOL
, /**< type */
6520 NULL
, /**< ui_requirement */
6522 NULL
, /**< dependencies */
6523 PURPLE_PRIORITY_DEFAULT
, /**< priority */
6524 "prpl-sipe", /**< id */
6525 "Microsoft LCS/OCS", /**< name */
6526 VERSION
, /**< version */
6527 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
6528 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
6529 "Anibal Avelar <avelar@gmail.com>, " /**< author */
6530 "Gabriel Burt <gburt@novell.com>", /**< author */
6531 PURPLE_WEBSITE
, /**< homepage */
6532 sipe_plugin_load
, /**< load */
6533 sipe_plugin_unload
, /**< unload */
6534 sipe_plugin_destroy
, /**< destroy */
6535 NULL
, /**< ui_info */
6536 &prpl_info
, /**< extra_info */
6545 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
6549 entry
= prpl_info
.protocol_options
;
6551 purple_account_option_destroy(entry
->data
);
6552 entry
= g_list_delete_link(entry
, entry
);
6554 prpl_info
.protocol_options
= NULL
;
6556 entry
= prpl_info
.user_splits
;
6558 purple_account_user_split_destroy(entry
->data
);
6559 entry
= g_list_delete_link(entry
, entry
);
6561 prpl_info
.user_splits
= NULL
;
6564 static void init_plugin(PurplePlugin
*plugin
)
6566 PurpleAccountUserSplit
*split
;
6567 PurpleAccountOption
*option
;
6572 purple_debug_info(PACKAGE
, "bindtextdomain = %s\n", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
6573 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s\n",
6574 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
6575 textdomain(GETTEXT_PACKAGE
);
6578 purple_plugin_register(plugin
);
6580 split
= purple_account_user_split_new(_("Login \n domain\\user or\n someone@linux.com "), NULL
, ',');
6581 purple_account_user_split_set_reverse(split
, FALSE
);
6582 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
6584 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
6585 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6586 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
6587 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6589 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
6590 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6591 // Translators: noun (networking port)
6592 option
= purple_account_option_int_new(_("Port"), "port", 5061);
6593 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6595 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
6596 purple_account_option_add_list_item(option
, _("Auto"), "auto");
6597 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
6598 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
6599 purple_account_option_add_list_item(option
, _("UDP"), "udp");
6600 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6602 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
6603 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
6605 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
6606 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6609 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
6610 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6612 /* Suitable for sspi/NTLM, sspi/Kerberos and krb5 security mechanisms
6613 * No login/password is taken into account if this option present,
6614 * instead used default credentials stored in OS.
6616 option
= purple_account_option_bool_new(_("Use Single Sign-On"), "sso", TRUE
);
6617 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6619 my_protocol
= plugin
;
6622 /* I had to redefined the function for it load, but works */
6623 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
6624 plugin
->info
= &(info
);
6625 init_plugin((plugin
));
6626 sipe_plugin_load((plugin
));
6627 return purple_plugin_register(plugin
);