6 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
7 * Copyright (C) 2009 pier11 <pier11@kinozal.tv>
8 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
9 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
10 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
13 * Thanks to Google's Summer of Code Program and the helpful mentors
16 * Session-based SIP MESSAGE documentation:
17 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 #include <sys/socket.h>
36 #include <sys/ioctl.h>
37 #include <sys/types.h>
38 #include <netinet/in.h>
44 #define _LIBC_INTERNAL_
56 #include "accountopt.h"
58 #include "conversation.h"
72 #include "sipe-chat.h"
73 #include "sipe-conf.h"
74 #include "sipe-dialog.h"
76 #include "sipe-session.h"
77 #include "sipe-utils.h"
79 #include "sipe-sign.h"
83 /* Keep in sync with sipe_transport_type! */
84 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
85 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
87 /* Status identifiers (see also: sipe_status_types()) */
88 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
89 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
90 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
91 /* PURPLE_STATUS_UNAVAILABLE: */
92 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
93 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
94 #define SIPE_STATUS_ID_ONPHONE "on-the-phone" /* On The Phone */
95 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
96 /* PURPLE_STATUS_AWAY: */
97 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
98 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
99 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
100 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
101 /* ??? PURPLE_STATUS_MOBILE */
102 /* ??? PURPLE_STATUS_TUNE */
104 /* Action name templates */
105 #define ACTION_NAME_PRESENCE "<presence><%s>"
108 static gchar
*get_epid(struct sipe_account_data
*sip
)
111 sip
->epid
= sipe_uuid_get_macaddr(purple_network_get_my_ip(-1));
113 return g_strdup(sip
->epid
);
116 static char *genbranch()
118 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
119 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
120 rand() & 0xFFFF, rand() & 0xFFFF);
123 static const char *sipe_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount
*a
,
124 SIPE_UNUSED_PARAMETER PurpleBuddy
*b
)
129 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
131 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
133 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
134 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
137 static void sipe_close(PurpleConnection
*gc
);
139 static void send_presence_status(struct sipe_account_data
*sip
);
141 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
143 static void sipe_keep_alive(PurpleConnection
*gc
)
145 struct sipe_account_data
*sip
= gc
->proto_data
;
146 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
147 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
148 gchar buf
[2] = {0, 0};
149 purple_debug_info("sipe", "sending keep alive\n");
150 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
152 time_t now
= time(NULL
);
153 if ((sip
->keepalive_timeout
> 0) &&
154 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
155 #if PURPLE_VERSION_CHECK(2,4,0)
156 && ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
159 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
160 sendout_pkt(gc
, "\r\n\r\n");
161 sip
->last_keepalive
= now
;
166 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
168 struct sip_connection
*ret
= NULL
;
169 GSList
*entry
= sip
->openconns
;
172 if (ret
->fd
== fd
) return ret
;
178 static void sipe_auth_free(struct sip_auth
*auth
)
180 g_free(auth
->opaque
);
184 g_free(auth
->target
);
186 auth
->type
= AUTH_TYPE_UNSET
;
189 g_free(auth
->gssapi_data
);
190 auth
->gssapi_data
= NULL
;
191 sip_sec_destroy_context(auth
->gssapi_context
);
192 auth
->gssapi_context
= NULL
;
195 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
197 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
199 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
203 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
205 struct sip_connection
*conn
= connection_find(sip
, fd
);
207 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
208 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
214 static void connection_free_all(struct sipe_account_data
*sip
)
216 struct sip_connection
*ret
= NULL
;
217 GSList
*entry
= sip
->openconns
;
220 connection_remove(sip
, ret
->fd
);
221 entry
= sip
->openconns
;
225 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
228 const char *authuser
= sip
->authuser
;
232 if (!authuser
|| strlen(authuser
) < 1) {
233 authuser
= sip
->username
;
236 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
237 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
239 // If we have a signature for the message, include that
240 if (msg
->signature
) {
241 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
);
244 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
245 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
249 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
252 purple_account_get_bool(sip
->account
, "sso", TRUE
),
253 sip
->authdomain
? sip
->authdomain
: "",
258 if (!gssapi_data
|| !auth
->gssapi_context
)
261 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
262 ret
= g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth_protocol
, opaque
, auth
->realm
, auth
->target
, gssapi_data
);
268 return g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth_protocol
, auth
->realm
, auth
->target
);
270 } else { /* Digest */
272 /* Calculate new session key */
274 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
275 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
276 authuser
, auth
->realm
, sip
->password
,
277 auth
->gssapi_data
, NULL
);
280 sprintf(noncecount
, "%08d", auth
->nc
++);
281 response
= purple_cipher_http_digest_calculate_response("md5",
282 msg
->method
, msg
->target
, NULL
, NULL
,
283 auth
->gssapi_data
, noncecount
, NULL
,
285 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
287 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
);
293 static char *parse_attribute(const char *attrname
, const char *source
)
295 const char *tmp
, *tmp2
;
297 int len
= strlen(attrname
);
299 if (!strncmp(source
, attrname
, len
)) {
301 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
303 retval
= g_strndup(tmp
, tmp2
- tmp
);
305 retval
= g_strdup(tmp
);
311 static void fill_auth(gchar
*hdr
, struct sip_auth
*auth
)
317 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
321 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
322 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
323 auth
->type
= AUTH_TYPE_NTLM
;
326 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
327 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
328 auth
->type
= AUTH_TYPE_KERBEROS
;
332 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
333 auth
->type
= AUTH_TYPE_DIGEST
;
337 parts
= g_strsplit(hdr
, "\", ", 0);
338 for (i
= 0; parts
[i
]; i
++) {
341 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
343 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
344 g_free(auth
->gssapi_data
);
345 auth
->gssapi_data
= tmp
;
347 if (auth
->type
== AUTH_TYPE_NTLM
) {
348 /* NTLM module extracts nonce from gssapi-data */
352 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
353 /* Only used with AUTH_TYPE_DIGEST */
354 g_free(auth
->gssapi_data
);
355 auth
->gssapi_data
= tmp
;
356 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
357 g_free(auth
->opaque
);
359 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
363 if (auth
->type
== AUTH_TYPE_DIGEST
) {
364 /* Throw away old session key */
365 g_free(auth
->opaque
);
370 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
371 g_free(auth
->target
);
380 static void sipe_canwrite_cb(gpointer data
,
381 SIPE_UNUSED_PARAMETER gint source
,
382 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
384 PurpleConnection
*gc
= data
;
385 struct sipe_account_data
*sip
= gc
->proto_data
;
389 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
391 if (max_write
== 0) {
392 if (sip
->tx_handler
!= 0){
393 purple_input_remove(sip
->tx_handler
);
399 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
401 if (written
< 0 && errno
== EAGAIN
)
403 else if (written
<= 0) {
404 /*TODO: do we really want to disconnect on a failure to write?*/
405 purple_connection_error(gc
, _("Could not write"));
409 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
412 static void sipe_canwrite_cb_ssl(gpointer data
,
413 SIPE_UNUSED_PARAMETER gint src
,
414 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
416 PurpleConnection
*gc
= data
;
417 struct sipe_account_data
*sip
= gc
->proto_data
;
421 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
423 if (max_write
== 0) {
424 if (sip
->tx_handler
!= 0) {
425 purple_input_remove(sip
->tx_handler
);
431 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
433 if (written
< 0 && errno
== EAGAIN
)
435 else if (written
<= 0) {
436 /*TODO: do we really want to disconnect on a failure to write?*/
437 purple_connection_error(gc
, _("Could not write"));
441 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
444 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
446 static void send_later_cb(gpointer data
, gint source
,
447 SIPE_UNUSED_PARAMETER
const gchar
*error
)
449 PurpleConnection
*gc
= data
;
450 struct sipe_account_data
*sip
;
451 struct sip_connection
*conn
;
453 if (!PURPLE_CONNECTION_IS_VALID(gc
))
461 purple_connection_error(gc
, _("Could not connect"));
465 sip
= gc
->proto_data
;
467 sip
->connecting
= FALSE
;
468 sip
->last_keepalive
= time(NULL
);
470 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
472 /* If there is more to write now, we need to register a handler */
473 if (sip
->txbuf
->bufused
> 0)
474 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
476 conn
= connection_create(sip
, source
);
477 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
480 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
482 struct sipe_account_data
*sip
;
483 struct sip_connection
*conn
;
485 if (!PURPLE_CONNECTION_IS_VALID(gc
))
487 if (gsc
) purple_ssl_close(gsc
);
491 sip
= gc
->proto_data
;
494 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
495 sip
->connecting
= FALSE
;
496 sip
->last_keepalive
= time(NULL
);
498 conn
= connection_create(sip
, gsc
->fd
);
500 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
505 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
506 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
508 PurpleConnection
*gc
= data
;
509 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
510 if (sip
== NULL
) return;
512 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
514 /* If there is more to write now */
515 if (sip
->txbuf
->bufused
> 0) {
516 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
521 static void sendlater(PurpleConnection
*gc
, const char *buf
)
523 struct sipe_account_data
*sip
= gc
->proto_data
;
525 if (!sip
->connecting
) {
526 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
527 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
528 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
530 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
531 purple_connection_error(gc
, _("Couldn't create socket"));
534 sip
->connecting
= TRUE
;
537 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
538 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
540 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
543 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
545 struct sipe_account_data
*sip
= gc
->proto_data
;
546 time_t currtime
= time(NULL
);
547 int writelen
= strlen(buf
);
549 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
550 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
551 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
552 purple_debug_info("sipe", "could not send packet\n");
561 if (sip
->tx_handler
) {
566 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
568 ret
= write(sip
->fd
, buf
, writelen
);
572 if (ret
< 0 && errno
== EAGAIN
)
574 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
579 if (ret
< writelen
) {
580 if (!sip
->tx_handler
){
582 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
585 sip
->tx_handler
= purple_input_add(sip
->fd
,
586 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
591 /* XXX: is it OK to do this? You might get part of a request sent
592 with part of another. */
593 if (sip
->txbuf
->bufused
> 0)
594 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
596 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
602 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
604 sendout_pkt(gc
, buf
);
608 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
610 GSList
*tmp
= msg
->headers
;
613 GString
*outstr
= g_string_new("");
614 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
616 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
617 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
618 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
619 tmp
= g_slist_next(tmp
);
621 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
622 sendout_pkt(sip
->gc
, outstr
->str
);
623 g_string_free(outstr
, TRUE
);
626 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
630 if (sip
->registrar
.type
== AUTH_TYPE_UNSET
) {
634 if (sip
->registrar
.gssapi_context
) {
635 struct sipmsg_breakdown msgbd
;
636 gchar
*signature_input_str
;
638 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
639 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
640 sip
->registrar
.ntlm_num
++;
641 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
642 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
643 if (signature_input_str
!= NULL
) {
644 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
645 msg
->signature
= signature_hex
;
646 msg
->rand
= g_strdup(msgbd
.rand
);
647 msg
->num
= g_strdup(msgbd
.num
);
648 g_free(signature_input_str
);
650 sipmsg_breakdown_free(&msgbd
);
653 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
654 buf
= auth_header(sip
, &sip
->registrar
, msg
);
655 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
657 } 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")) {
658 sip
->registrar
.nc
= 3;
660 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
662 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
665 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
670 buf
= auth_header(sip
, &sip
->registrar
, msg
);
671 sipmsg_add_header_now_pos(msg
, "Proxy-Authorization", buf
, 5);
674 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
680 static char *get_contact_service(struct sipe_account_data *sip)
682 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()));
683 //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);
687 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
688 const char *text
, const char *body
)
692 GString
*outstr
= g_string_new("");
693 struct sipe_account_data
*sip
= gc
->proto_data
;
696 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
698 contact
= get_contact(sip
);
699 sipmsg_add_header(msg
, "Contact", contact
);
704 sprintf(len
, "%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
705 sipmsg_add_header(msg
, "Content-Length", len
);
707 sipmsg_add_header(msg
, "Content-Length", "0");
710 msg
->response
= code
;
712 sipmsg_strip_headers(msg
, keepers
);
713 sipmsg_merge_new_headers(msg
);
714 sign_outgoing_message(msg
, sip
, msg
->method
);
716 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
719 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
720 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
722 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
723 tmp
= g_slist_next(tmp
);
725 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
726 sendout_pkt(gc
, outstr
->str
);
727 g_string_free(outstr
, TRUE
);
730 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
732 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
733 if (trans
->msg
) sipmsg_free(trans
->msg
);
738 static struct transaction
*
739 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
741 gchar
*call_id
= NULL
;
743 struct transaction
*trans
= g_new0(struct transaction
, 1);
745 trans
->time
= time(NULL
);
746 trans
->msg
= (struct sipmsg
*)msg
;
747 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
748 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
749 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
750 trans
->callback
= callback
;
751 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
755 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
757 struct transaction
*trans
;
758 GSList
*transactions
= sip
->transactions
;
759 gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
760 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
761 gchar
*key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
763 while (transactions
) {
764 trans
= transactions
->data
;
765 if (!g_strcasecmp(trans
->key
, key
)) {
769 transactions
= transactions
->next
;
777 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
778 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
779 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
781 struct sipe_account_data
*sip
= gc
->proto_data
;
782 const char *addh
= "";
785 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
786 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
787 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
788 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
789 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
790 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
791 gchar
*route
= strdup("");
792 gchar
*epid
= get_epid(sip
); // TODO generate one per account/login
793 int cseq
= dialog
? ++dialog
->cseq
:
794 /* This breaks OCS2007: own presence, contact search, ?
795 1 .* as Call-Id is new in this case */
797 struct transaction
*trans
;
799 if (dialog
&& dialog
->routes
)
801 GSList
*iter
= dialog
->routes
;
806 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
808 iter
= g_slist_next(iter
);
812 if (!ourtag
&& !dialog
) {
816 if (!strcmp(method
, "REGISTER")) {
817 if (sip
->regcallid
) {
819 callid
= g_strdup(sip
->regcallid
);
821 sip
->regcallid
= g_strdup(callid
);
825 if (addheaders
) addh
= addheaders
;
827 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
828 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
829 "From: <sip:%s>%s%s;epid=%s\r\n"
830 "To: <%s>%s%s%s%s\r\n"
831 "Max-Forwards: 70\r\n"
836 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
838 dialog
&& dialog
->request
? dialog
->request
: url
,
839 TRANSPORT_DESCRIPTOR
,
840 purple_network_get_my_ip(-1),
842 branch
? ";branch=" : "",
843 branch
? branch
: "",
845 ourtag
? ";tag=" : "",
846 ourtag
? ourtag
: "",
849 theirtag
? ";tag=" : "",
850 theirtag
? theirtag
: "",
851 theirepid
? ";epid=" : "",
852 theirepid
? theirepid
: "",
859 body
? (gsize
) strlen(body
) : 0,
863 //printf ("parsing msg buf:\n%s\n\n", buf);
864 msg
= sipmsg_parse_msg(buf
);
875 sign_outgoing_message (msg
, sip
, method
);
877 buf
= sipmsg_to_string (msg
);
879 /* add to ongoing transactions */
880 trans
= transactions_add_buf(sip
, msg
, tc
);
881 sendout_pkt(gc
, buf
);
887 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
889 gchar
*from
= sip_uri_self(sip
);
890 gchar
*contact
= get_contact(sip
);
891 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
892 "Content-Type: application/SOAP+xml\r\n",contact
);
894 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
895 tr
->payload
= payload
;
902 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
904 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
907 static char *get_contact_register(struct sipe_account_data
*sip
)
909 char *epid
= get_epid(sip
);
910 char *uuid
= generateUUIDfromEPID(epid
);
911 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
);
917 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
919 char *expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
920 char *uri
= sip_uri_from_name(sip
->sipdomain
);
921 char *to
= sip_uri_self(sip
);
922 char *contact
= get_contact_register(sip
);
923 char *hdr
= g_strdup_printf("Contact: %s\r\n"
924 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
925 "Event: registration\r\n"
926 "Allow-Events: presence\r\n"
927 "ms-keep-alive: UAC;hop-hop=yes\r\n"
928 "%s", contact
, expires
);
932 sip
->registerstatus
= 1;
934 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
935 process_register_response
);
942 static void do_register_cb(struct sipe_account_data
*sip
,
943 SIPE_UNUSED_PARAMETER
void *unused
)
945 do_register_exp(sip
, -1);
946 sip
->reregister_set
= FALSE
;
949 static void do_register(struct sipe_account_data
*sip
)
951 do_register_exp(sip
, -1);
955 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
957 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
958 send_soap_request(sip
, body
);
963 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
966 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
968 purple_debug_info("sipe", "Blocking contact %s\n", who
);
971 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
975 void sipe_auth_user_cb(void * data
)
977 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
980 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
985 void sipe_deny_user_cb(void * data
)
987 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
990 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
995 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
997 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
998 sipe_contact_allow_deny(sip
, name
, TRUE
);
1002 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1004 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1005 sipe_contact_allow_deny(sip
, name
, FALSE
);
1009 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1011 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1012 sipe_contact_set_acl(sip, name, "");
1016 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1020 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1021 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1023 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1025 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1026 if (!watchers
) return;
1028 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1029 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1030 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1031 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1033 // TODO pull out optional displayName to pass as alias
1035 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1036 job
->who
= remote_user
;
1038 purple_account_request_authorization(
1052 xmlnode_free(watchers
);
1057 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1059 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1060 if (!purple_group
) {
1061 purple_group
= purple_group_new(group
->name
);
1062 purple_blist_add_group(purple_group
, NULL
);
1066 group
->purple_group
= purple_group
;
1067 sip
->groups
= g_slist_append(sip
->groups
, group
);
1068 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1070 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1074 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1076 struct sipe_group
*group
;
1082 entry
= sip
->groups
;
1084 group
= entry
->data
;
1085 if (group
->id
== id
) {
1088 entry
= entry
->next
;
1093 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1095 struct sipe_group
*group
;
1101 entry
= sip
->groups
;
1103 group
= entry
->data
;
1104 if (!strcmp(group
->name
, name
)) {
1107 entry
= entry
->next
;
1113 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1116 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1117 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1118 send_soap_request(sip
, body
);
1120 g_free(group
->name
);
1121 group
->name
= g_strdup(name
);
1125 * Only appends if no such value already stored.
1128 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1129 GSList
* res
= list
;
1130 if (!g_slist_find_custom(list
, data
, func
)) {
1131 res
= g_slist_insert_sorted(list
, data
, func
);
1137 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1138 return group1
->id
- group2
->id
;
1142 * Returns string like "2 4 7 8" - group ids buddy belong to.
1145 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1148 //creating array from GList, converting int to gchar*
1149 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1150 GSList
*entry
= buddy
->groups
;
1152 struct sipe_group
* group
= entry
->data
;
1153 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1154 entry
= entry
->next
;
1158 res
= g_strjoinv(" ", ids_arr
);
1159 g_strfreev(ids_arr
);
1164 * Sends buddy update to server
1167 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1169 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1170 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1172 if (buddy
&& purple_buddy
) {
1173 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1175 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1176 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1178 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1179 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1181 send_soap_request(sip
, body
);
1187 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1189 if (msg
->response
== 200) {
1190 struct sipe_group
*group
;
1191 struct group_user_context
*ctx
= (struct group_user_context
*)tc
->payload
;
1195 struct sipe_buddy
*buddy
;
1197 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1203 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1210 group_id
= xmlnode_get_data(node
);
1217 group
= g_new0(struct sipe_group
, 1);
1218 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1220 group
->name
= ctx
->group_name
;
1222 sipe_group_add(sip
, group
);
1224 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1226 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1229 sipe_group_set_user(sip
, ctx
->user_name
);
1238 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1240 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1242 ctx
->group_name
= g_strdup(name
);
1243 ctx
->user_name
= g_strdup(who
);
1245 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1246 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1251 * Data structure for scheduled actions
1253 typedef void (*Action
) (struct sipe_account_data
*, void *);
1255 struct scheduled_action
{
1258 * Format is <Event>[<Data>...]
1259 * Example: <presence><sip:user@domain.com> or <registration>
1262 guint timeout_handler
;
1263 gboolean repetitive
;
1265 GDestroyNotify destroy
;
1266 struct sipe_account_data
*sip
;
1272 * Should return FALSE if repetitive action is not needed
1274 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1277 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1278 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1279 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1280 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1281 ret
= sched_action
->repetitive
;
1282 if (sched_action
->destroy
) {
1283 (*sched_action
->destroy
)(sched_action
->payload
);
1285 g_free(sched_action
->name
);
1286 g_free(sched_action
);
1291 * Kills action timer effectively cancelling
1294 * @param name of action
1296 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1300 if (!sip
->timeouts
|| !name
) return;
1302 entry
= sip
->timeouts
;
1304 struct scheduled_action
*sched_action
= entry
->data
;
1305 if(!strcmp(sched_action
->name
, name
)) {
1306 GSList
*to_delete
= entry
;
1307 entry
= entry
->next
;
1308 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1309 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1310 purple_timeout_remove(sched_action
->timeout_handler
);
1311 if (sched_action
->destroy
) {
1312 (*sched_action
->destroy
)(sched_action
->payload
);
1314 g_free(sched_action
->name
);
1315 g_free(sched_action
);
1317 entry
= entry
->next
;
1323 sipe_schedule_action0(const gchar
*name
,
1327 GDestroyNotify destroy
,
1328 struct sipe_account_data
*sip
,
1331 struct scheduled_action
*sched_action
;
1333 /* Make sure each action only exists once */
1334 sipe_cancel_scheduled_action(sip
, name
);
1336 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1337 sched_action
= g_new0(struct scheduled_action
, 1);
1338 sched_action
->repetitive
= FALSE
;
1339 sched_action
->name
= g_strdup(name
);
1340 sched_action
->action
= action
;
1341 sched_action
->destroy
= destroy
;
1342 sched_action
->sip
= sip
;
1343 sched_action
->payload
= payload
;
1344 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1345 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1346 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1347 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1351 * Do schedule action for execution in the future.
1352 * Non repetitive execution.
1354 * @param name of action (will be copied)
1355 * @param timeout in seconds
1356 * @action callback function
1357 * @payload callback data (can be NULL, otherwise caller must allocate memory)
1360 sipe_schedule_action(const gchar
*name
,
1363 GDestroyNotify destroy
,
1364 struct sipe_account_data
*sip
,
1367 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1371 * Same as sipe_schedule_action() but timeout is in milliseconds.
1374 sipe_schedule_action_msec(const gchar
*name
,
1377 GDestroyNotify destroy
,
1378 struct sipe_account_data
*sip
,
1381 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1385 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1387 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
1388 SIPE_UNUSED_PARAMETER
struct transaction
*tc
)
1390 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1392 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1397 static void sipe_subscribe_resource_uri(const char *name
,
1398 SIPE_UNUSED_PARAMETER gpointer value
,
1399 gchar
**resources_uri
)
1401 gchar
*tmp
= *resources_uri
;
1402 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1406 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1408 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1409 if (sbuddy
&& !sbuddy
->resubscribed
) { // Only not resubscribed contacts; the first time everybody are included
1410 gchar
*tmp
= *resources_uri
;
1411 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"><context/></resource>\n", tmp
, name
);
1417 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1418 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1419 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1420 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1421 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1424 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1426 gchar
*contact
= get_contact(sip
);
1429 gchar
*require
= "";
1431 gchar
*autoextend
= "";
1432 gchar
*content_type
;
1434 if (sip
->msrtc_event_categories
) {
1435 require
= ", categoryList";
1436 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1437 content_type
= "application/msrtc-adrl-categorylist+xml";
1438 content
= g_strdup_printf(
1439 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1440 "<action name=\"subscribe\" id=\"63792024\">\n"
1441 "<adhocList>\n%s</adhocList>\n"
1442 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1443 "<category name=\"note\"/>\n"
1444 "<category name=\"state\"/>\n"
1447 "</batchSub>", sip
->username
, resources_uri
);
1449 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1450 content_type
= "application/adrl+xml";
1451 content
= g_strdup_printf(
1452 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1453 "<create xmlns=\"\">\n%s</create>\n"
1454 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1456 g_free(resources_uri
);
1458 request
= g_strdup_printf(
1459 "Require: adhoclist%s\r\n"
1460 "Supported: eventlist\r\n"
1461 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1462 "Supported: ms-piggyback-first-notify\r\n"
1463 "%sSupported: ms-benotify\r\n"
1464 "Proxy-Require: ms-benotify\r\n"
1465 "Event: presence\r\n"
1466 "Content-Type: %s\r\n"
1467 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1470 /* subscribe to buddy presence */
1471 //send_sip_request(sip->gc, "SUBSCRIBE", resource_uri, resource_uri, request, content, NULL, process_subscribe_response);
1472 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1479 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
,
1480 SIPE_UNUSED_PARAMETER
void *unused
)
1482 gchar
*to
= sip_uri_self(sip
);
1483 gchar
*resources_uri
= g_strdup("");
1484 if (sip
->msrtc_event_categories
) {
1485 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1487 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1489 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1492 struct presence_batched_routed
{
1497 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1499 struct presence_batched_routed
*data
= payload
;
1500 GSList
*buddies
= data
->buddies
;
1502 g_free(buddies
->data
);
1503 buddies
= buddies
->next
;
1505 g_slist_free(data
->buddies
);
1510 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
1512 struct presence_batched_routed
*data
= payload
;
1513 GSList
*buddies
= data
->buddies
;
1514 gchar
*resources_uri
= g_strdup("");
1516 gchar
*tmp
= resources_uri
;
1517 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
1519 buddies
= buddies
->next
;
1521 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
1522 g_strdup(data
->host
));
1526 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1527 * The user sends a single SUBSCRIBE request to the subscribed contact.
1528 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1532 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
1534 gchar
*to
= sip_uri((char *)buddy_name
);
1535 gchar
*tmp
= get_contact(sip
);
1538 gchar
*autoextend
= "";
1540 if (!sip
->msrtc_event_categories
)
1541 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1543 request
= g_strdup_printf(
1544 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1545 "Supported: ms-piggyback-first-notify\r\n"
1546 "%sSupported: ms-benotify\r\n"
1547 "Proxy-Require: ms-benotify\r\n"
1548 "Event: presence\r\n"
1549 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1550 "Contact: %s\r\n", autoextend
,tmp
);
1552 content
= g_strdup_printf(
1553 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1554 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1555 "<resource uri=\"%s\"/>\n"
1557 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1558 "<category name=\"note\"/>\n"
1559 "<category name=\"state\"/>\n"
1562 "</batchSub>", sip
->username
, to
1567 /* subscribe to buddy presence */
1568 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1575 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1577 if (!purple_status_is_active(status
))
1581 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
1584 g_free(sip
->status
);
1585 sip
->status
= g_strdup(purple_status_get_id(status
));
1586 send_presence_status(sip
);
1592 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
,
1593 SIPE_UNUSED_PARAMETER
const char *alias
)
1595 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1596 sipe_group_set_user(sip
, name
);
1600 sipe_group_buddy(PurpleConnection
*gc
,
1602 const char *old_group_name
,
1603 const char *new_group_name
)
1605 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1606 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1607 struct sipe_group
* old_group
= NULL
;
1608 struct sipe_group
* new_group
;
1610 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1611 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1613 if(!buddy
) { // buddy not in roaming list
1617 if (old_group_name
) {
1618 old_group
= sipe_group_find_by_name(sip
, g_strdup(old_group_name
));
1620 new_group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1623 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1624 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1628 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1630 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1631 sipe_group_set_user(sip
, who
);
1635 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1637 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1638 struct sipe_buddy
*b
;
1640 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1642 // Prepend sip: if needed
1643 if (strncmp("sip:", buddy
->name
, 4)) {
1644 gchar
*buf
= sip_uri_from_name(buddy
->name
);
1645 purple_blist_rename_buddy(buddy
, buf
);
1649 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1650 b
= g_new0(struct sipe_buddy
, 1);
1651 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1652 b
->name
= g_strdup(buddy
->name
);
1653 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1654 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1655 sipe_subscribe_presence_single(sip
, b
->name
); //@TODO should go to callback
1657 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1661 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1665 * We are calling g_hash_table_foreach_steal(). That means that no
1666 * key/value deallocation functions are called. Therefore the glib
1667 * hash code does not touch the key (buddy->name) or value (buddy)
1668 * of the to-be-deleted hash node at all. It follows that we
1670 * - MUST free the memory for the key ourselves and
1671 * - ARE allowed to do it in this function
1673 * Conclusion: glib must be broken on the Windows platform if sipe
1674 * crashes with SIGTRAP when closing. You'll have to live
1675 * with the memory leak until this is fixed.
1677 g_free(buddy
->name
);
1679 g_free(buddy
->annotation
);
1680 g_free(buddy
->device_name
);
1681 g_slist_free(buddy
->groups
);
1686 * Unassociates buddy from group first.
1687 * Then see if no groups left, removes buddy completely.
1688 * Otherwise updates buddy groups on server.
1690 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1692 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1693 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1694 struct sipe_group
*g
= NULL
;
1696 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1701 g
= sipe_group_find_by_name(sip
, group
->name
);
1705 b
->groups
= g_slist_remove(b
->groups
, g
);
1706 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
1709 if (g_slist_length(b
->groups
) < 1) {
1710 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
1711 sipe_cancel_scheduled_action(sip
, action_name
);
1712 g_free(action_name
);
1714 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1717 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1718 send_soap_request(sip
, body
);
1724 //updates groups on server
1725 sipe_group_set_user(sip
, b
->name
);
1731 sipe_rename_group(PurpleConnection
*gc
,
1732 const char *old_name
,
1734 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
1736 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1737 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1739 sipe_group_rename(sip
, s_group
, group
->name
);
1741 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1746 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1748 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1749 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1752 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1753 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1754 send_soap_request(sip
, body
);
1757 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1758 g_free(s_group
->name
);
1761 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1765 static GList
*sipe_status_types(SIPE_UNUSED_PARAMETER PurpleAccount
*acc
)
1767 PurpleStatusType
*type
;
1768 GList
*types
= NULL
;
1771 type
= purple_status_type_new_with_attrs(
1772 PURPLE_STATUS_AVAILABLE
, NULL
, _("Online"), TRUE
, TRUE
, FALSE
,
1773 // Translators: noun
1774 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1776 types
= g_list_append(types
, type
);
1779 type
= purple_status_type_new_with_attrs(
1780 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_BUSY
, _("Busy"), TRUE
, TRUE
, FALSE
,
1781 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1783 types
= g_list_append(types
, type
);
1785 // Do Not Disturb (not user settable)
1786 type
= purple_status_type_new_with_attrs(
1787 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_DND
, _("Do Not Disturb"), TRUE
, FALSE
, FALSE
,
1788 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1790 types
= g_list_append(types
, type
);
1793 type
= purple_status_type_new_with_attrs(
1794 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_BRB
, _("Be Right Back"), TRUE
, TRUE
, FALSE
,
1795 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1797 types
= g_list_append(types
, type
);
1800 type
= purple_status_type_new_with_attrs(
1801 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1802 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1804 types
= g_list_append(types
, type
);
1807 type
= purple_status_type_new_with_attrs(
1808 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_ONPHONE
, _("On The Phone"), TRUE
, TRUE
, FALSE
,
1809 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1811 types
= g_list_append(types
, type
);
1814 type
= purple_status_type_new_with_attrs(
1815 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_LUNCH
, _("Out To Lunch"), TRUE
, TRUE
, FALSE
,
1816 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1818 types
= g_list_append(types
, type
);
1821 type
= purple_status_type_new_full(
1822 PURPLE_STATUS_INVISIBLE
, NULL
, _("Appear Offline"), TRUE
, TRUE
, FALSE
);
1823 types
= g_list_append(types
, type
);
1826 type
= purple_status_type_new_full(
1827 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1828 types
= g_list_append(types
, type
);
1834 * A callback for g_hash_table_foreach
1836 static void sipe_buddy_subscribe_cb(SIPE_UNUSED_PARAMETER
char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1838 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
1839 int time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1840 int timeout
= (time_range
* rand()) / RAND_MAX
; /* random period within the range */
1841 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy
->name
));
1845 * Removes entries from purple buddy list
1846 * that does not correspond ones in the roaming contact list.
1848 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
1849 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1850 GSList
*entry
= buddies
;
1851 struct sipe_buddy
*buddy
;
1855 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
1856 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
1859 g
= purple_buddy_get_group(b
);
1860 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
1862 gboolean in_sipe_groups
= FALSE
;
1863 GSList
*entry2
= buddy
->groups
;
1865 struct sipe_group
*group
= entry2
->data
;
1866 if (!strcmp(group
->name
, g
->name
)) {
1867 in_sipe_groups
= TRUE
;
1870 entry2
= entry2
->next
;
1872 if(!in_sipe_groups
) {
1873 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
1874 purple_blist_remove_buddy(b
);
1877 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
1878 purple_blist_remove_buddy(b
);
1880 entry
= entry
->next
;
1884 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1886 int len
= msg
->bodylen
;
1888 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1891 const gchar
*contacts_delta
;
1892 xmlnode
*group_node
;
1893 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1897 /* Convert the contact from XML to Purple Buddies */
1898 isc
= xmlnode_from_str(msg
->body
, len
);
1903 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
1904 if (contacts_delta
) {
1905 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1908 if (!strcmp(isc
->name
, "contactList")) {
1911 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1912 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1913 const char *name
= xmlnode_get_attrib(group_node
, "name");
1915 if (!strncmp(name
, "~", 1)) {
1916 name
= _("Other Contacts");
1918 group
->name
= g_strdup(name
);
1919 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1921 sipe_group_add(sip
, group
);
1924 // Make sure we have at least one group
1925 if (g_slist_length(sip
->groups
) == 0) {
1926 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1927 PurpleGroup
*purple_group
;
1928 group
->name
= g_strdup(_("Other Contacts"));
1930 purple_group
= purple_group_new(group
->name
);
1931 purple_blist_add_group(purple_group
, NULL
);
1932 sip
->groups
= g_slist_append(sip
->groups
, group
);
1935 /* Parse contacts */
1936 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1937 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
1938 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
1939 gchar
* groups
= g_strdup(xmlnode_get_attrib(item
, "groups"));
1940 gchar
* buddy_name
= sip_uri_from_name(uri
);
1941 gchar
**item_groups
;
1942 struct sipe_group
*group
= NULL
;
1943 struct sipe_buddy
*buddy
= NULL
;
1946 // assign to group Other Contacts if nothing else received
1947 if(!groups
|| !strcmp("", groups
) ) {
1948 group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
1949 groups
= group
? g_strdup_printf("%d", group
->id
) : "1";
1952 item_groups
= g_strsplit(groups
, " ", 0);
1954 while (item_groups
[i
]) {
1955 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
1957 // If couldn't find the right group for this contact, just put them in the first group we have
1958 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1959 group
= sip
->groups
->data
;
1962 if (group
!= NULL
) {
1963 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1965 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1966 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1969 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
1970 if (name
!= NULL
&& strlen(name
) != 0) {
1971 purple_blist_alias_buddy(b
, name
);
1976 buddy
= g_new0(struct sipe_buddy
, 1);
1977 buddy
->name
= g_strdup(b
->name
);
1978 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
1981 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1983 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
1985 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
1990 } // while, contact groups
1991 g_strfreev(item_groups
);
1999 sipe_cleanup_local_blist(sip
);
2003 //subscribe to buddies
2004 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2005 if(sip
->batched_support
){
2006 sipe_subscribe_presence_batched(sip
, NULL
);
2009 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2011 sip
->subscribed_buddies
= TRUE
;
2018 * Subscribe roaming contacts
2020 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
)
2022 gchar
*to
= sip_uri_self(sip
);
2023 gchar
*tmp
= get_contact(sip
);
2024 gchar
*hdr
= g_strdup_printf(
2025 "Event: vnd-microsoft-roaming-contacts\r\n"
2026 "Accept: application/vnd-microsoft-roaming-contacts+xml\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
);
2039 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
,
2040 SIPE_UNUSED_PARAMETER
void *unused
)
2042 gchar
*to
= sip_uri_self(sip
);
2043 gchar
*tmp
= get_contact(sip
);
2044 gchar
*hdr
= g_strdup_printf(
2045 "Event: presence.wpending\r\n"
2046 "Accept: text/xml+msrtc.wpending\r\n"
2047 "Supported: com.microsoft.autoextend\r\n"
2048 "Supported: ms-benotify\r\n"
2049 "Proxy-Require: ms-benotify\r\n"
2050 "Supported: ms-piggyback-first-notify\r\n"
2051 "Contact: %s\r\n", tmp
);
2054 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2060 * Fires on deregistration event initiated by server.
2061 * [MS-SIPREGE] SIP extension.
2066 // Content-Type: text/registration-event
2067 // subscription-state: terminated;expires=0
2068 // ms-diagnostics-public: 4141;reason="User disabled"
2070 // deregistered;event=rejected
2072 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2074 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2075 gchar
*event
= NULL
;
2076 gchar
*reason
= NULL
;
2077 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2079 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2080 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2082 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2083 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2084 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2085 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2087 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2091 if (warning
!= NULL
) {
2092 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2093 } else { // for LCS2005
2095 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2096 error_id
= 4140; // [MS-SIPREGE]
2097 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2098 reason
= g_strdup(_("You have been signed off because you've signed in at another location"));
2099 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2101 reason
= g_strdup(_("User disabled")); // [MS-OCER]
2102 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2104 reason
= g_strdup(_("User moved")); // [MS-OCER]
2108 warning
= g_strdup_printf(_("Unregistered by Server: %s."), reason
? reason
: _("no reason given"));
2111 sip
->gc
->wants_to_die
= TRUE
;
2112 purple_connection_error(sip
->gc
, warning
);
2117 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2119 xmlnode
*xn_provision_group_list
;
2122 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2124 /* provisionGroup */
2125 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2126 if (!strcmp("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2127 g_free(sip
->focus_factory_uri
);
2128 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2129 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2130 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2134 xmlnode_free(xn_provision_group_list
);
2137 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2139 const gchar
*contacts_delta
;
2142 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2148 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2151 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2158 free_container(struct sipe_container
*container
)
2162 if (!container
) return;
2164 entry
= container
->members
;
2166 g_free(entry
->data
);
2167 entry
= g_slist_remove(entry
, entry
->data
);
2173 * Finds locally stored MS-PRES container member
2175 static struct sipe_container_member
*
2176 sipe_find_container_member(struct sipe_container
*container
,
2180 struct sipe_container_member
*member
;
2183 if (container
== NULL
|| type
== NULL
) {
2187 entry
= container
->members
;
2189 member
= entry
->data
;
2190 if (!g_strcasecmp(member
->type
, type
)
2191 && ((!member
->value
&& !value
)
2192 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2196 entry
= entry
->next
;
2202 * Finds locally stored MS-PRES container by id
2204 static struct sipe_container
*
2205 sipe_find_container(struct sipe_account_data
*sip
,
2208 struct sipe_container
*container
;
2215 entry
= sip
->containers
;
2217 container
= entry
->data
;
2218 if (id
== container
->id
) {
2221 entry
= entry
->next
;
2235 sipe_find_access_level(struct sipe_account_data
*sip
,
2239 guint containers
[] = {32000, 400, 300, 200, 100};
2242 for (i
= 0; i
< 5; i
++) {
2243 struct sipe_container_member
*member
;
2244 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2245 if (!container
) continue;
2247 member
= sipe_find_container_member(container
, type
, value
);
2249 return containers
[i
];
2257 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2259 guint container_version
,
2260 const gchar
* action
,
2264 gchar
*self
= sip_uri_self(sip
);
2265 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2268 gchar
*body
= g_strdup_printf(
2269 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2270 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2271 "</setContainerMembers>",
2279 contact
= get_contact(sip
);
2280 hdr
= g_strdup_printf("Contact: %s\r\n"
2281 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2284 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2293 * When we receive some self (BE) NOTIFY with a new subscriber
2294 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2297 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2304 char *display_name
= NULL
;
2305 PurpleBuddy
*pbuddy
;
2310 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2312 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2315 contact
= get_contact(sip
);
2316 to
= sip_uri_self(sip
);
2319 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2320 guint id
= atoi(xmlnode_get_attrib(node
, "id"));
2321 struct sipe_container
*container
= sipe_find_container(sip
, id
);
2324 sip
->containers
= g_slist_remove(sip
->containers
, container
);
2325 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
2326 free_container(container
);
2328 container
= g_new0(struct sipe_container
, 1);
2330 container
->version
= atoi(xmlnode_get_attrib(node
, "version"));
2331 sip
->containers
= g_slist_append(sip
->containers
, container
);
2332 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
2334 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
2335 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
2336 member
->type
= xmlnode_get_attrib(node2
, "type");
2337 member
->value
= xmlnode_get_attrib(node2
, "value");
2338 container
->members
= g_slist_append(container
->members
, member
);
2339 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
2340 member
->type
, member
->value
? member
->value
: "");
2344 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
2345 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
2346 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
2347 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
2348 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
2349 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
2350 /* initial set-up to let counterparties see your status */
2351 if (sameEnterpriseAL
< 0) {
2352 struct sipe_container
*container
= sipe_find_container(sip
, 200);
2353 guint version
= container
? container
->version
: 0;
2354 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
2356 if (federatedAL
< 0) {
2357 struct sipe_container
*container
= sipe_find_container(sip
, 100);
2358 guint version
= container
? container
->version
: 0;
2359 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
2361 sip
->access_level_set
= TRUE
;
2365 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2367 const char *acknowledged
;
2371 user
= xmlnode_get_attrib(node
, "user");
2372 if (!user
) continue;
2373 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
2374 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
2375 uri_user
= sip_uri_from_name(user
);
2376 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri_user
);
2378 alias
= purple_buddy_get_local_alias(pbuddy
);
2379 uri_alias
= sip_uri_from_name(alias
);
2380 if (display_name
&& !g_ascii_strcasecmp(uri_user
, uri_alias
)) { // 'bad' alias
2381 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri_user
, display_name
);
2382 purple_blist_alias_buddy(pbuddy
, display_name
);
2387 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
2388 if(!g_ascii_strcasecmp(acknowledged
,"false")){
2389 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
2390 if (!purple_find_buddy(sip
->account
, uri_user
)) {
2391 purple_account_request_add(sip
->account
, uri_user
, _("you"), display_name
, NULL
);
2394 hdr
= g_strdup_printf(
2396 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2398 body
= g_strdup_printf(
2399 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2400 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2401 "</setSubscribers>", user
);
2403 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
2407 g_free(display_name
);
2416 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
)
2418 gchar
*to
= sip_uri_self(sip
);
2419 gchar
*tmp
= get_contact(sip
);
2420 gchar
*hdr
= g_strdup_printf(
2421 "Event: vnd-microsoft-roaming-ACL\r\n"
2422 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
2423 "Supported: com.microsoft.autoextend\r\n"
2424 "Supported: ms-benotify\r\n"
2425 "Proxy-Require: ms-benotify\r\n"
2426 "Supported: ms-piggyback-first-notify\r\n"
2427 "Contact: %s\r\n", tmp
);
2430 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2436 * To request for presence information about the user, access level settings that have already been configured by the user
2437 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
2438 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
2441 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
)
2443 gchar
*to
= sip_uri_self(sip
);
2444 gchar
*tmp
= get_contact(sip
);
2445 gchar
*hdr
= g_strdup_printf(
2446 "Event: vnd-microsoft-roaming-self\r\n"
2447 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
2448 "Supported: ms-benotify\r\n"
2449 "Proxy-Require: ms-benotify\r\n"
2450 "Supported: ms-piggyback-first-notify\r\n"
2452 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
2454 gchar
*body
=g_strdup(
2455 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
2456 "<roaming type=\"categories\"/>"
2457 "<roaming type=\"containers\"/>"
2458 "<roaming type=\"subscribers\"/></roamingList>");
2461 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2470 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
)
2472 gchar
*to
= sip_uri_self(sip
);
2473 gchar
*tmp
= get_contact(sip
);
2474 gchar
*hdr
= g_strdup_printf(
2475 "Event: vnd-microsoft-provisioning\r\n"
2476 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
2477 "Supported: com.microsoft.autoextend\r\n"
2478 "Supported: ms-benotify\r\n"
2479 "Proxy-Require: ms-benotify\r\n"
2480 "Supported: ms-piggyback-first-notify\r\n"
2482 "Contact: %s\r\n", tmp
);
2485 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
2490 /** Subscription for provisioning information to help with initial
2491 * configuration. This subscription is a one-time query (denoted by the Expires header,
2492 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
2493 * configuration, meeting policies, and policy settings that Communicator must enforce.
2494 * TODO: for what we need this information.
2497 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
)
2499 gchar
*to
= sip_uri_self(sip
);
2500 gchar
*tmp
= get_contact(sip
);
2501 gchar
*hdr
= g_strdup_printf(
2502 "Event: vnd-microsoft-provisioning-v2\r\n"
2503 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
2504 "Supported: com.microsoft.autoextend\r\n"
2505 "Supported: ms-benotify\r\n"
2506 "Proxy-Require: ms-benotify\r\n"
2507 "Supported: ms-piggyback-first-notify\r\n"
2510 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
2511 gchar
*body
= g_strdup(
2512 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
2513 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
2514 "<provisioningGroup name=\"ucPolicy\"/>"
2515 "</provisioningGroupList>");
2518 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2524 /* IM Session (INVITE and MESSAGE methods) */
2526 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
2528 get_end_points (struct sipe_account_data
*sip
,
2529 struct sip_session
*session
)
2533 if (session
== NULL
) {
2537 res
= g_strdup_printf("<sip:%s>", sip
->username
);
2539 SIPE_DIALOG_FOREACH
{
2541 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
2544 if (dialog
->theirepid
) {
2546 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
2549 } SIPE_DIALOG_FOREACH_END
;
2555 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_account_data
*sip
,
2557 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2559 gboolean ret
= TRUE
;
2561 if (msg
->response
!= 200) {
2562 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
2566 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
2572 * Asks UA/proxy about its capabilities.
2574 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
2576 gchar
*to
= sip_uri(who
);
2577 gchar
*contact
= get_contact(sip
);
2578 gchar
*request
= g_strdup_printf(
2579 "Accept: application/sdp\r\n"
2580 "Contact: %s\r\n", contact
);
2583 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
2590 sipe_notify_user(struct sipe_account_data
*sip
,
2591 struct sip_session
*session
,
2592 PurpleMessageFlags flags
,
2593 const gchar
*message
)
2595 PurpleConversation
*conv
;
2597 if (!session
->conv
) {
2598 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
2600 conv
= session
->conv
;
2602 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
2606 sipe_present_info(struct sipe_account_data
*sip
,
2607 struct sip_session
*session
,
2608 const gchar
*message
)
2610 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
2614 sipe_present_err(struct sipe_account_data
*sip
,
2615 struct sip_session
*session
,
2616 const gchar
*message
)
2618 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_ERROR
, message
);
2622 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
2623 struct sip_session
*session
,
2625 const gchar
*message
)
2627 char *msg
, *msg_tmp
;
2629 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
2630 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2632 msg_tmp
= g_strdup_printf( _("This message was not delivered to %s because one or more recipients are offline:\n%s") ,
2633 who
? who
: "", msg
? msg
: "");
2634 sipe_present_err(sip
, session
, msg_tmp
);
2640 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
);
2643 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
2644 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2646 gboolean ret
= TRUE
;
2647 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2648 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
2649 struct sip_dialog
*dialog
;
2655 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2660 dialog
= sipe_dialog_find(session
, with
);
2662 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2667 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2668 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
2670 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2672 if (msg
->response
>= 400) {
2673 PurpleBuddy
*pbuddy
;
2674 gchar
*alias
= with
;
2676 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
2678 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
2679 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
2682 sipe_present_message_undelivered_err(sip
, session
, alias
, message
);
2685 gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
2687 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
));
2688 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
2689 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
2692 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2693 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
2694 key
, g_hash_table_size(session
->unconfirmed_messages
));
2700 if (ret
) sipe_im_process_queue(sip
, session
);
2705 sipe_is_election_finished(struct sip_session
*session
);
2708 sipe_election_result(struct sipe_account_data
*sip
,
2712 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
2713 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2715 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2716 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2717 struct sip_dialog
*dialog
;
2718 struct sip_session
*session
;
2720 session
= sipe_session_find_chat_by_callid(sip
, callid
);
2722 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
2726 if (msg
->response
== 200 && !strncmp(contenttype
, "application/x-ms-mim", 20)) {
2727 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2728 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
2729 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
2731 if (xn_request_rm_response
) {
2732 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
2733 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
2735 dialog
= sipe_dialog_find(session
, with
);
2737 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
2741 if (allow
&& !g_strcasecmp(allow
, "true")) {
2742 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
2743 dialog
->election_vote
= 1;
2744 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
2745 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
2746 dialog
->election_vote
= -1;
2749 if (sipe_is_election_finished(session
)) {
2750 sipe_election_result(sip
, session
);
2753 } else if (xn_set_rm_response
) {
2756 xmlnode_free(xn_action
);
2763 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
)
2772 sipe_parse_html(msg
, &msgformat
, &msgtext
);
2773 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
2775 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2778 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2781 msgr
= g_strdup("");
2784 tmp
= get_contact(sip
);
2785 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
2786 //hdr = g_strdup("Content-Type: text/rtf\r\n");
2787 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
2788 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n", tmp
, msgr
);
2792 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
2799 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
2801 GSList
*entry2
= session
->outgoing_message_queue
;
2803 char *queued_msg
= entry2
->data
;
2805 /* for multiparty chat or conference */
2806 if (session
->is_multiparty
|| session
->focus_uri
) {
2807 gchar
*who
= sip_uri_self(sip
);
2808 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
2809 PURPLE_MESSAGE_SEND
, queued_msg
, time(NULL
));
2813 SIPE_DIALOG_FOREACH
{
2816 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
2818 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
2819 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
2820 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
2821 key
, g_hash_table_size(session
->unconfirmed_messages
));
2824 sipe_send_message(sip
, dialog
, queued_msg
);
2825 } SIPE_DIALOG_FOREACH_END
;
2827 entry2
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2833 sipe_refer_notify(struct sipe_account_data
*sip
,
2834 struct sip_session
*session
,
2841 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
2843 hdr
= g_strdup_printf(
2845 "Subscription-State: %s\r\n"
2846 "Content-Type: message/sipfrag\r\n",
2847 status
>= 200 ? "terminated" : "active");
2849 body
= g_strdup_printf(
2850 "SIP/2.0 %d %s\r\n",
2853 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "NOTIFY",
2854 who
, who
, hdr
, body
, dialog
, NULL
);
2861 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2863 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2864 struct sip_session
*session
;
2865 struct sip_dialog
*dialog
;
2869 struct sipmsg
*request_msg
= trans
->msg
;
2871 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2874 session
= sipe_session_find_chat_by_callid(sip
, callid
);
2876 session
= sipe_session_find_im(sip
, with
);
2879 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2884 dialog
= sipe_dialog_find(session
, with
);
2886 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2891 sipe_dialog_parse(dialog
, msg
, TRUE
);
2893 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2894 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
2896 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2898 if (msg
->response
!= 200) {
2899 PurpleBuddy
*pbuddy
;
2900 gchar
*alias
= with
;
2902 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2904 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
2905 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
2909 sipe_present_message_undelivered_err(sip
, session
, alias
, message
);
2911 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
2912 sipe_present_err(sip
, session
, tmp_msg
);
2916 sipe_dialog_remove(session
, with
);
2924 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
2925 dialog
->outgoing_invite
= NULL
;
2926 dialog
->is_established
= TRUE
;
2928 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
2930 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
2931 g_free(referred_by
);
2934 /* add user to chat if it is a multiparty session */
2935 if (session
->is_multiparty
) {
2936 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
2938 PURPLE_CBFLAGS_NONE
, TRUE
);
2941 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
2942 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
2943 if (session
->outgoing_message_queue
) {
2944 char *queued_msg
= session
->outgoing_message_queue
->data
;
2945 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2950 sipe_im_process_queue(sip
, session
);
2952 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2953 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
2954 key
, g_hash_table_size(session
->unconfirmed_messages
));
2963 sipe_invite(struct sipe_account_data
*sip
,
2964 struct sip_session
*session
,
2966 const gchar
*msg_body
,
2967 const gchar
*referred_by
,
2968 const gboolean is_triggered
)
2975 char *ms_text_format
= g_strdup("");
2976 gchar
*roster_manager
;
2978 gchar
*referred_by_str
;
2979 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
2981 if (dialog
&& dialog
->is_established
) {
2982 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
2987 dialog
= sipe_dialog_add(session
);
2988 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
2989 dialog
->with
= g_strdup(who
);
2992 if (!(dialog
->ourtag
)) {
2993 dialog
->ourtag
= gentag();
3006 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
3007 purple_debug_info("sipe", "sipe_invite: msgformat=%s", msgformat
);
3009 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3013 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
3017 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
3018 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
3023 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
3024 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
3025 purple_debug_info("sipe", "sipe_im_send: added message %s to unconfirmed_messages(count=%d)\n",
3026 key
, g_hash_table_size(session
->unconfirmed_messages
));
3030 contact
= get_contact(sip
);
3031 end_points
= get_end_points(sip
, session
);
3032 self
= sip_uri_self(sip
);
3033 roster_manager
= g_strdup_printf(
3034 "Roster-Manager: %s\r\n"
3035 "EndPoints: %s\r\n",
3038 referred_by_str
= referred_by
?
3040 "Referred-By: %s\r\n",
3043 hdr
= g_strdup_printf(
3044 "Supported: ms-sender\r\n"
3050 "Content-Type: application/sdp\r\n",
3051 (session
->roster_manager
&& !strcmp(session
->roster_manager
, self
)) ? roster_manager
: "",
3053 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
3054 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
3057 g_free(ms_text_format
);
3060 body
= g_strdup_printf(
3062 "o=- 0 0 IN IP4 %s\r\n"
3066 "m=message %d sip null\r\n"
3067 "a=accept-types:text/plain text/html image/gif "
3068 "multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3069 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
3071 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
3072 to
, to
, hdr
, body
, dialog
, process_invite_response
);
3075 g_free(roster_manager
);
3077 g_free(referred_by_str
);
3084 sipe_refer(struct sipe_account_data
*sip
,
3085 struct sip_session
*session
,
3090 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
3091 session
->roster_manager
);
3093 contact
= get_contact(sip
);
3094 hdr
= g_strdup_printf(
3096 "Refer-to: <%s>\r\n"
3097 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
3098 "Require: com.microsoft.rtc-multiparty\r\n",
3102 dialog
->ourtag
? ";tag=" : "",
3103 dialog
->ourtag
? dialog
->ourtag
: "",
3106 send_sip_request(sip
->gc
, "REFER",
3107 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
3114 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
3115 struct sip_dialog
*dialog
,
3118 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
3120 gchar
*body
= g_strdup_printf(
3121 "<?xml version=\"1.0\"?>\r\n"
3122 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3123 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
3124 sip
->username
, bid
);
3126 send_sip_request(sip
->gc
, "INFO",
3127 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
3133 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
3134 struct sip_dialog
*dialog
)
3136 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
3138 gchar
*body
= g_strdup_printf(
3139 "<?xml version=\"1.0\"?>\r\n"
3140 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3141 "<SetRM uri=\"sip:%s\"/></action>\r\n",
3144 send_sip_request(sip
->gc
, "INFO",
3145 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
3151 sipe_session_close(struct sipe_account_data
*sip
,
3152 struct sip_session
* session
)
3154 if (session
&& session
->focus_uri
) {
3155 conf_session_close(sip
, session
);
3159 SIPE_DIALOG_FOREACH
{
3160 /* @TODO slow down BYE message sending rate */
3161 /* @see single subscription code */
3162 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
3163 } SIPE_DIALOG_FOREACH_END
;
3165 sipe_session_remove(sip
, session
);
3170 sipe_session_close_all(struct sipe_account_data
*sip
)
3173 while ((entry
= sip
->sessions
) != NULL
) {
3174 sipe_session_close(sip
, entry
->data
);
3179 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
3181 struct sipe_account_data
*sip
= gc
->proto_data
;
3183 purple_debug_info("sipe", "conversation with %s closed\n", who
);
3184 sipe_session_close(sip
, sipe_session_find_im(sip
, who
));
3188 sipe_chat_leave (PurpleConnection
*gc
, int id
)
3190 struct sipe_account_data
*sip
= gc
->proto_data
;
3191 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
3193 sipe_session_close(sip
, session
);
3196 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
3197 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3199 struct sipe_account_data
*sip
= gc
->proto_data
;
3200 struct sip_session
*session
;
3201 struct sip_dialog
*dialog
;
3203 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
3205 session
= sipe_session_find_or_add_im(sip
, who
);
3206 dialog
= sipe_dialog_find(session
, who
);
3208 // Queue the message
3209 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
3211 if (dialog
&& dialog
->callid
) {
3212 sipe_im_process_queue(sip
, session
);
3213 } else if (!dialog
|| !dialog
->outgoing_invite
) {
3214 // Need to send the INVITE to get the outgoing dialog setup
3215 sipe_invite(sip
, session
, who
, what
, NULL
, FALSE
);
3221 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
3222 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3224 struct sipe_account_data
*sip
= gc
->proto_data
;
3225 struct sip_session
*session
;
3227 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
3229 session
= sipe_session_find_chat_by_id(sip
, id
);
3231 // Queue the message
3232 if (session
&& session
->dialogs
) {
3233 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
3235 sipe_im_process_queue(sip
, session
);
3241 /* End IM Session (INVITE and MESSAGE methods) */
3243 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3245 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
3246 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3247 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3249 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
3251 session
= sipe_session_find_im(sip
, from
);
3258 if (!strncmp(contenttype
, "application/x-ms-mim", 20)) {
3259 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3260 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
3261 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
3263 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
3265 if (xn_request_rm
) {
3266 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
3267 int bid
= atoi(xmlnode_get_attrib(xn_request_rm
, "bid"));
3268 gchar
*body
= g_strdup_printf(
3269 "<?xml version=\"1.0\"?>\r\n"
3270 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3271 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
3273 session
->bid
< bid
? "true" : "false");
3274 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3276 } else if (xn_set_rm
) {
3278 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
3279 g_free(session
->roster_manager
);
3280 session
->roster_manager
= g_strdup(rm
);
3282 body
= g_strdup_printf(
3283 "<?xml version=\"1.0\"?>\r\n"
3284 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3285 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
3287 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3290 xmlnode_free(xn_action
);
3293 /* looks like purple lacks typing notification for chat */
3294 if (!session
->is_multiparty
&& !session
->focus_uri
) {
3295 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
3298 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3303 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3305 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3306 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3307 struct sip_session
*session
;
3309 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3311 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3313 session
= sipe_session_find_im(sip
, from
);
3320 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
3321 g_free(session
->roster_manager
);
3322 session
->roster_manager
= NULL
;
3325 /* This what BYE is essentially for - terminating dialog */
3326 sipe_dialog_remove(session
, from
);
3327 if (session
->focus_uri
&& !g_strcasecmp(from
, session
->im_mcu_uri
)) {
3328 sipe_conf_immcu_closed(sip
, session
);
3329 } else if (session
->is_multiparty
) {
3330 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
3336 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3338 gchar
*self
= sip_uri_self(sip
);
3339 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3340 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3341 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
3342 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
3343 struct sip_session
*session
;
3344 struct sip_dialog
*dialog
;
3346 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3347 dialog
= sipe_dialog_find(session
, from
);
3349 if (!session
|| !dialog
|| !session
->roster_manager
|| strcmp(session
->roster_manager
, self
)) {
3350 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
3352 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
3354 sipe_invite(sip
, session
, refer_to
, NULL
, referred_by
, FALSE
);
3360 g_free(referred_by
);
3364 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
3366 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
3367 struct sip_session
*session
;
3368 struct sip_dialog
*dialog
;
3370 if (state
== PURPLE_NOT_TYPING
)
3373 session
= sipe_session_find_im(sip
, who
);
3374 dialog
= sipe_dialog_find(session
, who
);
3376 if (session
&& dialog
&& dialog
->is_established
) {
3377 send_sip_request(gc
, "INFO", who
, who
,
3378 "Content-Type: application/xml\r\n",
3379 SIPE_SEND_TYPING
, dialog
, NULL
);
3381 return SIPE_TYPING_SEND_TIMEOUT
;
3384 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
3386 GSList
*tmp
= sip
->transactions
;
3387 time_t currtime
= time(NULL
);
3389 struct transaction
*trans
= tmp
->data
;
3391 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
3392 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
3395 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
3397 sendout_sipmsg(sip
, trans
->msg
);
3404 static void do_reauthenticate_cb(struct sipe_account_data
*sip
,
3405 SIPE_UNUSED_PARAMETER
void *unused
)
3407 /* register again when security token expires */
3408 /* we have to start a new authentication as the security token
3409 * is almost expired by sending a not signed REGISTER message */
3410 purple_debug_info("sipe", "do a full reauthentication\n");
3411 sipe_auth_free(&sip
->registrar
);
3412 sipe_auth_free(&sip
->proxy
);
3413 sip
->registerstatus
= 0;
3415 sip
->reauthenticate_set
= FALSE
;
3418 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3422 gboolean found
= FALSE
;
3424 from
= parse_from(sipmsg_find_header(msg
, "From"));
3428 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
3430 contenttype
= sipmsg_find_header(msg
, "Content-Type");
3431 if (!strncmp(contenttype
, "text/plain", 10)
3432 || !strncmp(contenttype
, "text/html", 9)
3433 || !strncmp(contenttype
, "multipart/related", 21))
3435 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3436 gchar
*html
= get_html_message(contenttype
, msg
->body
);
3438 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
3440 session
= sipe_session_find_im(sip
, from
);
3443 if (session
&& session
->focus_uri
) { /* a conference */
3444 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
3445 gchar
*sender
= parse_from(tmp
);
3447 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
3448 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3450 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
3451 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
3452 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3454 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3457 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3460 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
3461 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3466 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
3470 state
= xmlnode_get_child(isc
, "state");
3473 purple_debug_info("sipe", "process_incoming_message: no state found\n");
3478 statedata
= xmlnode_get_data(state
);
3480 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
3481 else serv_got_typing_stopped(sip
->gc
, from
);
3486 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3490 purple_debug_info("sipe", "got unknown mime-type");
3491 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
3496 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3498 gchar
*ms_text_format
;
3503 gboolean is_multiparty
= FALSE
;
3504 gboolean is_triggered
= FALSE
;
3505 gboolean was_multiparty
= TRUE
;
3506 gboolean just_joined
= FALSE
;
3508 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3509 gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
3510 gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
3511 gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
3512 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
3513 GSList
*end_points
= NULL
;
3514 struct sip_session
*session
;
3516 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? msg
->body
: "");
3518 /* Invitation to join conference */
3519 if (!strncmp(content_type
, "application/ms-conf-invite+xml", 30)) {
3520 process_incoming_invite_conf(sip
, msg
);
3524 /* Only accept text invitations */
3525 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
3526 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3530 // TODO There *must* be a better way to clean up the To header to add a tag...
3531 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
3532 oldHeader
= sipmsg_find_header(msg
, "To");
3534 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
3535 sipmsg_remove_header_now(msg
, "To");
3536 sipmsg_add_header_now(msg
, "To", newHeader
);
3539 if (end_points_hdr
) {
3540 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
3542 if (g_slist_length(end_points
) > 2) {
3543 is_multiparty
= TRUE
;
3546 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
3547 is_triggered
= TRUE
;
3548 is_multiparty
= TRUE
;
3551 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3552 /* Convert to multiparty */
3553 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
3554 g_free(session
->with
);
3555 session
->with
= NULL
;
3556 was_multiparty
= FALSE
;
3557 session
->is_multiparty
= TRUE
;
3558 session
->chat_id
= rand();
3561 if (!session
&& is_multiparty
) {
3562 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
3565 from
= parse_from(sipmsg_find_header(msg
, "From"));
3567 session
= sipe_session_find_or_add_im(sip
, from
);
3570 g_free(session
->callid
);
3571 session
->callid
= g_strdup(callid
);
3573 session
->is_multiparty
= is_multiparty
;
3574 if (roster_manager
) {
3575 session
->roster_manager
= g_strdup(roster_manager
);
3578 if (is_multiparty
&& end_points
) {
3579 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
3580 GSList
*entry
= end_points
;
3582 struct sip_dialog
*dialog
;
3583 struct sipendpoint
*end_point
= entry
->data
;
3584 entry
= entry
->next
;
3586 if (!g_strcasecmp(from
, end_point
->contact
) ||
3587 !g_strcasecmp(to
, end_point
->contact
))
3590 dialog
= sipe_dialog_find(session
, end_point
->contact
);
3592 g_free(dialog
->theirepid
);
3593 dialog
->theirepid
= end_point
->epid
;
3594 end_point
->epid
= NULL
;
3596 dialog
= sipe_dialog_add(session
);
3598 dialog
->callid
= g_strdup(session
->callid
);
3599 dialog
->with
= end_point
->contact
;
3600 end_point
->contact
= NULL
;
3601 dialog
->theirepid
= end_point
->epid
;
3602 end_point
->epid
= NULL
;
3606 /* send triggered INVITE */
3607 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, TRUE
);
3614 GSList
*entry
= end_points
;
3616 struct sipendpoint
*end_point
= entry
->data
;
3617 entry
= entry
->next
;
3618 g_free(end_point
->contact
);
3619 g_free(end_point
->epid
);
3622 g_slist_free(end_points
);
3626 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
3628 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
3630 dialog
= sipe_dialog_add(session
);
3632 dialog
->callid
= g_strdup(session
->callid
);
3633 dialog
->with
= g_strdup(from
);
3634 sipe_dialog_parse(dialog
, msg
, FALSE
);
3636 if (!dialog
->ourtag
) {
3637 dialog
->ourtag
= newTag
;
3644 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
3648 if (is_multiparty
&& !session
->conv
) {
3649 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++sip
->chat_seq
);
3650 gchar
*self
= sip_uri_self(sip
);
3651 /* create prpl chat */
3652 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_name
);
3653 session
->chat_name
= g_strdup(chat_name
);
3654 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
3656 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3658 PURPLE_CBFLAGS_NONE
, FALSE
);
3663 if (is_multiparty
&& !was_multiparty
) {
3664 /* add current IM counterparty to chat */
3665 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3666 sipe_dialog_first(session
)->with
, NULL
,
3667 PURPLE_CBFLAGS_NONE
, FALSE
);
3670 /* add inviting party */
3672 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3674 PURPLE_CBFLAGS_NONE
, TRUE
);
3677 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
3678 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
3679 if (ms_text_format
) {
3680 if (!strncmp(ms_text_format
, "text/plain", 10) || !strncmp(ms_text_format
, "text/html", 9)) {
3682 gchar
*html
= get_html_message(ms_text_format
, NULL
);
3684 if (is_multiparty
) {
3685 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
3686 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3688 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3691 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message received
3697 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
3698 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3699 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
3701 body
= g_strdup_printf(
3703 "o=- 0 0 IN IP4 %s\r\n"
3707 "m=message %d sip sip:%s\r\n"
3708 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3709 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
3710 sip
->realport
, sip
->username
);
3711 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3715 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3719 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
3720 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3721 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
3723 body
= g_strdup_printf(
3725 "o=- 0 0 IN IP4 0.0.0.0\r\n"
3727 "c=IN IP4 0.0.0.0\r\n"
3729 "m=message %d sip sip:%s\r\n"
3730 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3731 sip
->realport
, sip
->username
);
3732 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3736 static void sipe_connection_cleanup(struct sipe_account_data
*);
3737 static void create_connection(struct sipe_account_data
*, gchar
*, int);
3739 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3742 const gchar
*expires_header
;
3744 GSList
*hdr
= msg
->headers
;
3745 struct siphdrelement
*elem
;
3747 expires_header
= sipmsg_find_header(msg
, "Expires");
3748 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
3749 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
3751 switch (msg
->response
) {
3754 sip
->registerstatus
= 0;
3756 gchar
*contact_hdr
= NULL
;
3762 if (!sip
->reregister_set
) {
3763 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
3764 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
3765 g_free(action_name
);
3766 sip
->reregister_set
= TRUE
;
3769 sip
->registerstatus
= 3;
3772 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3774 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3777 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3781 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3782 fill_auth(tmp
, &sip
->registrar
);
3785 if (!sip
->reauthenticate_set
) {
3786 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
3787 guint reauth_timeout
;
3788 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
3789 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
3790 reauth_timeout
= sip
->registrar
.expires
- 300;
3792 /* NTLM: we have to reauthenticate as our security token expires
3793 after eight hours (be five minutes early) */
3794 reauth_timeout
= (8 * 3600) - 300;
3796 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
3797 g_free(action_name
);
3798 sip
->reauthenticate_set
= TRUE
;
3801 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
3803 epid
= get_epid(sip
);
3804 uuid
= generateUUIDfromEPID(epid
);
3807 // There can be multiple Contact headers (one per location where the user is logged in) so
3808 // make sure to only get the one for this uuid
3809 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
3810 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
3811 if (valid_contact
) {
3812 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
3813 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
3814 g_free(valid_contact
);
3817 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
3822 g_free(sip
->contact
);
3824 sip
->contact
= g_strdup_printf("<%s>", gruu
);
3827 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
3828 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
);
3830 sip
->msrtc_event_categories
= FALSE
;
3831 sip
->batched_support
= FALSE
;
3836 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
3837 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
3838 sip
->msrtc_event_categories
= TRUE
;
3839 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->msrtc_event_categories
);
3841 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
3842 sip
->batched_support
= TRUE
;
3843 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->batched_support
);
3846 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
3847 gchar
**caps
= g_strsplit(elem
->value
,",",0);
3850 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
3851 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
3856 hdr
= g_slist_next(hdr
);
3860 if (!sip
->subscribed
) { //do it just once, not every re-register
3862 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
3863 (GCompareFunc
)g_ascii_strcasecmp
)) {
3864 sipe_subscribe_roaming_contacts(sip
);
3867 /* For 2007+ it does not make sence to subscribe to:
3868 * vnd-microsoft-roaming-ACL
3869 * vnd-microsoft-provisioning (not v2)
3871 * These are for backward compatibility.
3873 if (sip
->msrtc_event_categories
)
3875 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
3876 (GCompareFunc
)g_ascii_strcasecmp
)) {
3877 sipe_subscribe_roaming_self(sip
);
3879 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
3880 (GCompareFunc
)g_ascii_strcasecmp
)) {
3881 sipe_subscribe_roaming_provisioning_v2(sip
);
3884 /* For 2005- servers */
3887 //sipe_options_request(sip, sip->sipdomain);
3889 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
3890 (GCompareFunc
)g_ascii_strcasecmp
)) {
3891 sipe_subscribe_roaming_acl(sip
);
3893 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
3894 (GCompareFunc
)g_ascii_strcasecmp
)) {
3895 sipe_subscribe_roaming_provisioning(sip
);
3897 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
3898 (GCompareFunc
)g_ascii_strcasecmp
)) {
3899 sipe_subscribe_presence_wpending(sip
, msg
);
3902 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
3903 sip
->subscribed
= TRUE
;
3906 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
3907 "timeout=", ";", NULL
);
3908 if (timeout
!= NULL
) {
3909 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
3910 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
3911 sip
->keepalive_timeout
);
3915 // Should we remove the transaction here?
3916 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
3917 transactions_remove(sip
, tc
);
3922 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
3924 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
3925 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
3929 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
3932 tmp
= g_strsplit(parts
[0], ":", 0);
3933 hostname
= g_strdup(tmp
[0]);
3934 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
3938 tmp
= g_strsplit(parts
[i
], "=", 0);
3940 if (g_strcasecmp("transport", tmp
[0]) == 0) {
3941 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
3942 transport
= SIPE_TRANSPORT_TCP
;
3943 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
3944 transport
= SIPE_TRANSPORT_UDP
;
3953 /* Close old connection */
3954 sipe_connection_cleanup(sip
);
3956 /* Create new connection */
3957 sip
->transport
= transport
;
3958 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
3959 hostname
, port
, TRANSPORT_DESCRIPTOR
);
3960 create_connection(sip
, hostname
, port
);
3966 if (sip
->registerstatus
!= 2) {
3967 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
3968 if (sip
->registrar
.retries
> 3) {
3969 sip
->gc
->wants_to_die
= TRUE
;
3970 purple_connection_error(sip
->gc
, _("Wrong Password"));
3974 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3976 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3979 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3982 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3983 fill_auth(tmp
, &sip
->registrar
);
3984 sip
->registerstatus
= 2;
3985 if (sip
->account
->disconnecting
) {
3986 do_register_exp(sip
, 0);
3994 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
3995 if (warning
!= NULL
) {
3997 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
3999 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
4000 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
4003 warning
= g_strdup(_("You have been rejected by the server"));
4006 sip
->gc
->wants_to_die
= TRUE
;
4007 purple_connection_error(sip
->gc
, warning
);
4014 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
4015 if (warning
!= NULL
) {
4016 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
4017 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
4020 warning
= g_strdup(_("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator"));
4023 sip
->gc
->wants_to_die
= TRUE
;
4024 purple_connection_error(sip
->gc
, warning
);
4031 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
4032 if (warning
!= NULL
) {
4033 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
4034 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
4037 warning
= g_strdup(_("Service unavailable: no reason given"));
4040 sip
->gc
->wants_to_die
= TRUE
;
4041 purple_connection_error(sip
->gc
, warning
);
4050 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4053 xmlnode
*xn_categories
;
4054 xmlnode
*xn_category
;
4056 const char *activity
= NULL
;
4058 xn_categories
= xmlnode_from_str(data
, len
);
4059 uri
= xmlnode_get_attrib(xn_categories
, "uri");
4061 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
4063 xn_category
= xmlnode_get_next_twin(xn_category
) )
4065 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
4067 if (!strcmp(attrVar
, "note"))
4070 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4075 xn_node
= xmlnode_get_child(xn_category
, "note");
4076 if (!xn_node
) continue;
4077 xn_node
= xmlnode_get_child(xn_node
, "body");
4078 if (!xn_node
) continue;
4079 note
= xmlnode_get_data(xn_node
);
4080 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s),note(%s)\n",uri
,note
? note
: "");
4081 g_free(sbuddy
->annotation
);
4082 sbuddy
->annotation
= NULL
;
4083 if (note
) sbuddy
->annotation
= g_strdup(note
);
4089 else if(!strcmp(attrVar
, "state"))
4093 xn_node
= xmlnode_get_child(xn_category
, "state");
4094 if (!xn_node
) continue;
4095 xn_node
= xmlnode_get_child(xn_node
, "availability");
4096 if (!xn_node
) continue;
4098 data
= xmlnode_get_data(xn_node
);
4103 activity
= SIPE_STATUS_ID_UNKNOWN
;
4104 else if (avail
< 4500)
4105 activity
= SIPE_STATUS_ID_AVAILABLE
;
4106 else if (avail
< 6000)
4107 activity
= SIPE_STATUS_ID_BRB
;
4108 else if (avail
< 7500)
4109 activity
= SIPE_STATUS_ID_ONPHONE
;
4110 else if (avail
< 9000)
4111 activity
= SIPE_STATUS_ID_BUSY
;
4112 else if (avail
< 12000)
4113 activity
= SIPE_STATUS_ID_DND
;
4114 else if (avail
< 18000)
4115 activity
= SIPE_STATUS_ID_AWAY
;
4117 activity
= SIPE_STATUS_ID_OFFLINE
;
4121 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", activity
);
4122 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
4125 xmlnode_free(xn_categories
);
4128 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
4130 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4131 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
4132 payload
->host
= g_strdup(host
);
4133 payload
->buddies
= server
;
4134 sipe_subscribe_presence_batched_routed(sip
, payload
);
4135 sipe_subscribe_presence_batched_routed_free(payload
);
4138 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4141 xmlnode
*xn_resource
;
4142 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
4147 xn_list
= xmlnode_from_str(data
, len
);
4149 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
4151 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
4153 const char *uri
, *state
;
4154 xmlnode
*xn_instance
;
4156 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
4157 if (!xn_instance
) continue;
4159 uri
= xmlnode_get_attrib(xn_resource
, "uri");
4160 state
= xmlnode_get_attrib(xn_instance
, "state");
4161 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
4163 if (strstr(state
, "resubscribe")) {
4164 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
4165 struct sipe_buddy
*sbuddy
;
4166 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
4167 gchar
*user
= g_strdup(uri
);
4168 host
= g_strdup(poolFqdn
);
4169 server
= g_hash_table_lookup(servers
, host
);
4170 server
= g_slist_append(server
, user
);
4171 g_hash_table_insert(servers
, host
, server
);
4173 sipe_subscribe_presence_single(sip
, (void *) uri
);
4175 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4177 sbuddy
->resubscribed
= TRUE
;
4182 /* Send out any deferred poolFqdn subscriptions */
4183 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
4184 g_hash_table_destroy(servers
);
4186 xmlnode_free(xn_list
);
4189 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4193 gchar
*activity
= NULL
;
4195 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
4196 gboolean isonline
= FALSE
;
4197 xmlnode
*display_name_node
;
4199 pidf
= xmlnode_from_str(data
, len
);
4201 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data
);
4205 uri
= xmlnode_get_attrib(pidf
, "entity");
4207 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
4209 if ((status
= xmlnode_get_child(tuple
, "status"))) {
4210 basicstatus
= xmlnode_get_child(status
, "basic");
4215 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
4220 getbasic
= xmlnode_get_data(basicstatus
);
4222 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
4227 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
4228 if (strstr(getbasic
, "open")) {
4233 display_name_node
= xmlnode_get_child(pidf
, "display-name");
4234 // updating display name if alias was just URI
4235 if (display_name_node
) {
4236 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
4237 GSList
*entry
= buddies
;
4238 PurpleBuddy
*p_buddy
;
4239 char * display_name
= xmlnode_get_data(display_name_node
);
4242 const char *server_alias
;
4245 p_buddy
= entry
->data
;
4247 alias
= (char *)purple_buddy_get_alias(p_buddy
);
4248 alias
= alias
? sip_uri_from_name(alias
) : NULL
;
4249 if (!alias
|| !g_ascii_strcasecmp(uri
, alias
)) {
4250 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
4251 purple_blist_alias_buddy(p_buddy
, display_name
);
4255 server_alias
= purple_buddy_get_server_alias(p_buddy
);
4257 ( (server_alias
&& strcmp(display_name
, server_alias
))
4258 || !server_alias
|| strlen(server_alias
) == 0 )
4260 purple_blist_server_alias_buddy(p_buddy
, display_name
);
4263 entry
= entry
->next
;
4265 g_free(display_name
);
4268 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
4269 if ((status
= xmlnode_get_child(tuple
, "status"))) {
4270 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
4271 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
4272 activity
= xmlnode_get_data(basicstatus
);
4273 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
4280 const gchar
* status_id
= NULL
;
4282 if (strstr(activity
, "busy")) {
4283 status_id
= SIPE_STATUS_ID_BUSY
;
4284 } else if (strstr(activity
, "away")) {
4285 status_id
= SIPE_STATUS_ID_AWAY
;
4290 status_id
= SIPE_STATUS_ID_AVAILABLE
;
4293 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
4294 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
4296 purple_prpl_got_user_status(sip
->account
, uri
, SIPE_STATUS_ID_OFFLINE
, NULL
);
4303 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4305 const char *availability
;
4306 const char *activity
;
4307 const char *display_name
= NULL
;
4308 const char *activity_name
= NULL
;
4313 struct sipe_buddy
*sbuddy
;
4315 xmlnode
*xn_presentity
= xmlnode_from_str(data
, len
);
4317 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
4318 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
4319 xmlnode
*xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
4320 xmlnode
*xn_email
= xmlnode_get_child(xn_presentity
, "email");
4321 const char *email
= xn_email
? xmlnode_get_attrib(xn_email
, "email") : NULL
;
4322 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
4323 xmlnode
*xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
):NULL
;
4324 const char *avail
= xn_state
? xmlnode_get_attrib(xn_state
, "avail") : NULL
;
4326 xmlnode
*xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
4327 char *note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
4328 xmlnode
*xn_devices
= xmlnode_get_child(xn_presentity
, "devices");
4329 xmlnode
*xn_device_presence
= xn_devices
? xmlnode_get_child(xn_devices
, "devicePresence") : NULL
;
4330 xmlnode
*xn_device_name
= xn_device_presence
? xmlnode_get_child(xn_device_presence
, "deviceName") : NULL
;
4331 const char *device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
4333 name
= xmlnode_get_attrib(xn_presentity
, "uri");
4334 uri
= sip_uri_from_name(name
);
4335 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
4336 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
4338 // updating display name if alias was just URI
4339 if (xn_display_name
) {
4340 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
4341 GSList
*entry
= buddies
;
4342 PurpleBuddy
*p_buddy
;
4343 display_name
= xmlnode_get_attrib(xn_display_name
, "displayName");
4346 const char *email_str
, *server_alias
;
4348 p_buddy
= entry
->data
;
4350 if (!g_ascii_strcasecmp(name
, purple_buddy_get_alias(p_buddy
))) {
4351 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
4352 purple_blist_alias_buddy(p_buddy
, display_name
);
4355 server_alias
= purple_buddy_get_server_alias(p_buddy
);
4357 ( (server_alias
&& strcmp(display_name
, server_alias
))
4358 || !server_alias
|| strlen(server_alias
) == 0 )
4360 purple_blist_server_alias_buddy(p_buddy
, display_name
);
4364 email_str
= purple_blist_node_get_string((PurpleBlistNode
*)p_buddy
, "email");
4365 if (!email_str
|| g_ascii_strcasecmp(email_str
, email
)) {
4366 purple_blist_node_set_string((PurpleBlistNode
*)p_buddy
, "email", email
);
4370 entry
= entry
->next
;
4374 avl
= atoi(availability
);
4375 act
= atoi(activity
);
4377 if(sip
->msrtc_event_categories
){
4378 if (act
== 100 && avl
== 0)
4379 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4380 else if (act
== 100 && avl
== 300)
4381 activity_name
= SIPE_STATUS_ID_AWAY
;
4382 else if (act
== 300 && avl
== 300)
4383 activity_name
= SIPE_STATUS_ID_BRB
;
4384 else if (act
== 400 && avl
== 300)
4385 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4386 else if (act
== 500 && act
== 300)
4387 activity_name
= SIPE_STATUS_ID_ONPHONE
;
4388 else if (act
== 600 && avl
== 300)
4389 activity_name
= SIPE_STATUS_ID_BUSY
;
4390 else if (act
== 0 && avl
== 0){ //MSRTC elements are zero
4391 if(avail
){ //Check for LegacyInterop elements
4394 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4395 else if (avl
== 3500)
4396 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4397 else if (avl
== 15500)
4398 activity_name
= SIPE_STATUS_ID_AWAY
;
4399 else if (avl
== 6500)
4400 activity_name
= SIPE_STATUS_ID_BUSY
;
4401 else if (avl
== 12500)
4402 activity_name
= SIPE_STATUS_ID_BRB
;
4407 if(activity_name
== NULL
){
4409 activity_name
= SIPE_STATUS_ID_AWAY
;
4410 else if (act
<= 150)
4411 activity_name
= SIPE_STATUS_ID_LUNCH
;
4412 else if (act
<= 300)
4413 activity_name
= SIPE_STATUS_ID_BRB
;
4414 else if (act
<= 400)
4415 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4416 else if (act
<= 500)
4417 activity_name
= SIPE_STATUS_ID_ONPHONE
;
4418 else if (act
<= 600)
4419 activity_name
= SIPE_STATUS_ID_BUSY
;
4421 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4424 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4427 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4430 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
4431 sbuddy
->annotation
= NULL
;
4432 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
4434 if (sbuddy
->device_name
) { g_free(sbuddy
->device_name
); }
4435 sbuddy
->device_name
= NULL
;
4436 if (device_name
) { sbuddy
->device_name
= g_strdup(device_name
); }
4439 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
4440 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
4442 xmlnode_free(xn_presentity
);
4446 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4448 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4450 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
4452 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
4453 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
4455 const char *content
= msg
->body
;
4456 unsigned length
= msg
->bodylen
;
4457 PurpleMimeDocument
*mime
= NULL
;
4459 if (strstr(ctype
, "multipart"))
4461 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
4462 const char *content_type
;
4464 mime
= purple_mime_document_parse(doc
);
4465 parts
= purple_mime_document_get_parts(mime
);
4467 content
= purple_mime_part_get_data(parts
->data
);
4468 length
= purple_mime_part_get_length(parts
->data
);
4469 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
4470 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
4472 process_incoming_notify_rlmi_resub(sip
, content
, length
);
4474 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
4476 process_incoming_notify_msrtc(sip
, content
, length
);
4480 process_incoming_notify_rlmi(sip
, content
, length
);
4482 parts
= parts
->next
;
4488 purple_mime_document_free(mime
);
4491 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
4493 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
4495 else if(strstr(ctype
, "application/rlmi+xml"))
4497 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
4500 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
4502 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
4506 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
4510 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
4512 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4513 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
4515 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
4518 strstr(ctype
, "multipart") &&
4519 (strstr(ctype
, "application/rlmi+xml") ||
4520 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
4521 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
4522 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
4523 GList
*parts
= purple_mime_document_get_parts(mime
);
4524 GSList
*buddies
= NULL
;
4525 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4528 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
4529 purple_mime_part_get_length(parts
->data
));
4530 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
4532 buddies
= g_slist_append(buddies
, uri
);
4535 parts
= parts
->next
;
4538 if (mime
) purple_mime_document_free(mime
);
4540 payload
->host
= who
;
4541 payload
->buddies
= buddies
;
4542 sipe_schedule_action(action_name
, timeout
,
4543 sipe_subscribe_presence_batched_routed
,
4544 sipe_subscribe_presence_batched_routed_free
,
4546 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
4549 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, NULL
, sip
, who
);
4550 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
4552 g_free(action_name
);
4556 * Dispatcher for all incoming subscription information
4557 * whether it comes from NOTIFY, BENOTIFY requests or
4558 * piggy-backed to subscription's OK responce.
4560 * @param request whether initiated from BE/NOTIFY request or OK-response message.
4561 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
4563 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
4565 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4566 gchar
*event
= sipmsg_find_header(msg
, "Event");
4567 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
4570 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event
? event
: "", msg
->body
);
4571 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n\n", subscription_state
? subscription_state
: "");
4573 /* implicit subscriptions */
4574 if (content_type
&& purple_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
4575 sipe_process_imdn(sip
, msg
);
4580 const gchar
*expires_header
;
4581 expires_header
= sipmsg_find_header(msg
, "Expires");
4582 timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
4583 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n\n", timeout
);
4584 timeout
= (timeout
- 60) > 60 ? (timeout
- 60) : timeout
; // 1 min ahead of expiration
4587 /* for one off subscriptions (send with Expire: 0) */
4588 if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning-v2"))
4590 sipe_process_provisioning_v2(sip
, msg
);
4593 if (!subscription_state
|| strstr(subscription_state
, "active"))
4595 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
4597 sipe_process_presence(sip
, msg
);
4599 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
4601 sipe_process_roaming_contacts(sip
, msg
);
4603 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self"))
4605 sipe_process_roaming_self(sip
, msg
);
4607 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
4609 sipe_process_roaming_acl(sip
, msg
);
4611 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
4613 sipe_process_presence_wpending(sip
, msg
);
4615 else if (event
&& !g_ascii_strcasecmp(event
, "conference"))
4617 sipe_process_conference(sip
, msg
);
4621 //The server sends a (BE)NOTIFY with the status 'terminated'
4622 if (request
&& subscription_state
&& strstr(subscription_state
, "terminated") ) {
4623 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4624 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from
);
4628 if (timeout
&& event
) {// For LSC 2005 and OCS 2007
4629 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
4630 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
4632 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
4633 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_contacts, NULL, sip, msg);
4634 g_free(action_name);
4636 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
4637 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
4639 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
4640 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_acl, NULL, sip, msg);
4641 g_free(action_name);
4644 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
4645 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
4647 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
4648 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
4649 g_free(action_name
);
4651 else if (!g_ascii_strcasecmp(event
, "presence") &&
4652 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
4654 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
4655 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
4656 if(sip
->batched_support
) {
4657 gchar
*my_self
= sip_uri_self(sip
);
4658 if(!g_ascii_strcasecmp(who
, my_self
)){
4659 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_batched
, NULL
, sip
, NULL
);
4660 purple_debug_info("sipe", "Resubscription full batched list in %d\n",timeout
);
4661 g_free(who
); /* unused */
4664 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
4669 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, who
);
4670 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
,timeout
);
4672 g_free(action_name
);
4673 /* "who" will be freed by the action we just scheduled */
4677 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
4679 sipe_process_registration_notify(sip
, msg
);
4682 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
4683 if (request
&& !benotify
)
4685 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4692 static gchar* gen_xpidf(struct sipe_account_data *sip)
4694 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4696 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
4697 "<display name=\"sip:%s\"/>\r\n"
4698 "<atom id=\"1234\">\r\n"
4699 "<address uri=\"sip:%s\">\r\n"
4700 "<status status=\"%s\"/>\r\n"
4713 static gchar* gen_pidf(struct sipe_account_data *sip)
4715 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4716 "<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"
4717 "<tuple id=\"0\">\r\n"
4719 "<basic>open</basic>\r\n"
4720 "<ep:activities>\r\n"
4721 " <ep:activity>%s</ep:activity>\r\n"
4725 "<ci:display-name>%s</ci:display-name>\r\n"
4734 static void send_presence_soap(struct sipe_account_data
*sip
, const char * note
)
4736 int availability
= 300; // online
4737 int activity
= 400; // Available
4740 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
)) {
4742 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4744 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4746 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4748 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4750 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4752 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_INVISIBLE
) ||
4753 !strcmp(sip
->status
, SIPE_STATUS_ID_OFFLINE
)) {
4754 availability
= 0; // offline
4757 activity
= 400; // available
4760 name
= g_strdup_printf("sip: sip:%s", sip
->username
);
4761 //@TODO: send user data - state; add hostname in upper case
4762 body
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
4763 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
4769 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4770 SIPE_UNUSED_PARAMETER
struct transaction
*tc
)
4772 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4773 if (msg
->response
== 200) {
4774 sip
->status_version
= 0;
4775 send_presence_status(sip
);
4781 process_send_presence_category_publish_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4782 SIPE_UNUSED_PARAMETER
struct transaction
*tc
)
4784 if (msg
->response
== 409) {
4785 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4786 // TODO need to parse the version #'s?
4787 gchar
*uri
= sip_uri_self(sip
);
4788 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
4792 purple_debug_info("sipe", "process_send_presence_category_publish_response = %s\n", msg
->body
);
4794 tmp
= get_contact(sip
);
4795 hdr
= g_strdup_printf("Contact: %s\r\n"
4796 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4798 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
4808 static void send_presence_category_publish(struct sipe_account_data
*sip
, const char * note
)
4815 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
) ||
4816 !strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4818 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_DND
)) {
4820 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4822 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4824 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4826 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4828 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_UNKNOWN
)) {
4831 // Offline or invisible
4835 uri
= sip_uri_self(sip
);
4836 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
4837 sip
->status_version
, code
,
4838 sip
->status_version
, code
,
4839 sip
->status_version
, note
? note
: "",
4840 sip
->status_version
, note
? note
: "",
4841 sip
->status_version
, note
? note
: ""
4843 sip
->status_version
++;
4845 tmp
= get_contact(sip
);
4846 hdr
= g_strdup_printf("Contact: %s\r\n"
4847 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4849 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
4857 static void send_presence_status(struct sipe_account_data
*sip
)
4859 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
4861 if (!status
) return;
4863 note
= purple_status_get_attr_string(status
, "message");
4865 if(sip
->msrtc_event_categories
){
4866 send_presence_category_publish(sip
, note
);
4868 send_presence_soap(sip
, note
);
4872 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
4874 gboolean found
= FALSE
;
4875 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
4876 if (msg
->response
== 0) { /* request */
4877 if (!strcmp(msg
->method
, "MESSAGE")) {
4878 process_incoming_message(sip
, msg
);
4880 } else if (!strcmp(msg
->method
, "NOTIFY")) {
4881 purple_debug_info("sipe","send->process_incoming_notify\n");
4882 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
4884 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
4885 purple_debug_info("sipe","send->process_incoming_benotify\n");
4886 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
4888 } else if (!strcmp(msg
->method
, "INVITE")) {
4889 process_incoming_invite(sip
, msg
);
4891 } else if (!strcmp(msg
->method
, "REFER")) {
4892 process_incoming_refer(sip
, msg
);
4894 } else if (!strcmp(msg
->method
, "OPTIONS")) {
4895 process_incoming_options(sip
, msg
);
4897 } else if (!strcmp(msg
->method
, "INFO")) {
4898 process_incoming_info(sip
, msg
);
4900 } else if (!strcmp(msg
->method
, "ACK")) {
4901 // ACK's don't need any response
4903 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
4904 // LCS 2005 sends us these - just respond 200 OK
4906 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4907 } else if (!strcmp(msg
->method
, "BYE")) {
4908 process_incoming_bye(sip
, msg
);
4911 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
4913 } else { /* response */
4914 struct transaction
*trans
= transactions_find(sip
, msg
);
4916 if (msg
->response
== 407) {
4917 gchar
*resend
, *auth
, *ptmp
;
4919 if (sip
->proxy
.retries
> 30) return;
4920 sip
->proxy
.retries
++;
4921 /* do proxy authentication */
4923 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
4925 fill_auth(ptmp
, &sip
->proxy
);
4926 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
4927 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
4928 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
4930 resend
= sipmsg_to_string(trans
->msg
);
4931 /* resend request */
4932 sendout_pkt(sip
->gc
, resend
);
4935 if (msg
->response
== 100 || msg
->response
== 180) {
4936 /* ignore provisional response */
4937 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
4939 sip
->proxy
.retries
= 0;
4940 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
4941 if (msg
->response
== 401)
4943 sip
->registrar
.retries
++;
4947 sip
->registrar
.retries
= 0;
4949 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
4951 if (msg
->response
== 401) {
4952 gchar
*resend
, *auth
, *ptmp
;
4954 if (sip
->registrar
.retries
> 4) return;
4955 sip
->registrar
.retries
++;
4958 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4960 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
4963 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4967 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
4969 fill_auth(ptmp
, &sip
->registrar
);
4970 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
4971 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
4972 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
4974 //sipmsg_remove_header_now(trans->msg, "Authorization");
4975 //sipmsg_add_header(trans->msg, "Authorization", auth);
4977 resend
= sipmsg_to_string(trans
->msg
);
4978 /* resend request */
4979 sendout_pkt(sip
->gc
, resend
);
4984 if (trans
->callback
) {
4985 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
4986 /* call the callback to process response*/
4987 (trans
->callback
)(sip
, msg
, trans
);
4989 /* Not sure if this is needed or what needs to be done
4990 but transactions seem to be removed prematurely so
4991 this only removes them if the response is 200 OK */
4992 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
4993 /*Has a bug and it's unneccesary*/
4994 /*transactions_remove(sip, trans);*/
5000 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
5004 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
5008 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
5016 /* according to the RFC remove CRLF at the beginning */
5017 while (*cur
== '\r' || *cur
== '\n') {
5020 if (cur
!= conn
->inbuf
) {
5021 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
5022 conn
->inbufused
= strlen(conn
->inbuf
);
5025 /* Received a full Header? */
5026 sip
->processing_input
= TRUE
;
5027 while (sip
->processing_input
&&
5028 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
5029 time_t currtime
= time(NULL
);
5032 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
5033 msg
= sipmsg_parse_header(conn
->inbuf
);
5036 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
5037 if (restlen
>= msg
->bodylen
) {
5038 dummy
= g_malloc(msg
->bodylen
+ 1);
5039 memcpy(dummy
, cur
, msg
->bodylen
);
5040 dummy
[msg
->bodylen
] = '\0';
5042 cur
+= msg
->bodylen
;
5043 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
5044 conn
->inbufused
= strlen(conn
->inbuf
);
5046 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
5047 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
5053 purple_debug_info("sipe", "body:\n%s", msg->body);
5056 // Verify the signature before processing it
5057 if (sip
->registrar
.gssapi_context
) {
5058 struct sipmsg_breakdown msgbd
;
5059 gchar
*signature_input_str
;
5062 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
5063 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
5065 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
5067 if (rspauth
!= NULL
) {
5068 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
5069 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
5070 process_input_message(sip
, msg
);
5072 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
5073 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
5074 sip
->gc
->wants_to_die
= TRUE
;
5076 } else if (msg
->response
== 401) {
5077 purple_connection_error(sip
->gc
, _("Wrong Password"));
5078 sip
->gc
->wants_to_die
= TRUE
;
5080 g_free(signature_input_str
);
5083 sipmsg_breakdown_free(&msgbd
);
5085 process_input_message(sip
, msg
);
5092 static void sipe_udp_process(gpointer data
, gint source
,
5093 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
5095 PurpleConnection
*gc
= data
;
5096 struct sipe_account_data
*sip
= gc
->proto_data
;
5101 static char buffer
[65536];
5102 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
5104 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
5105 msg
= sipmsg_parse_msg(buffer
);
5106 if (msg
) process_input_message(sip
, msg
);
5110 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
5112 struct sipe_account_data
*sip
= gc
->proto_data
;
5113 PurpleSslConnection
*gsc
= sip
->gsc
;
5115 purple_debug_error("sipe", "%s",debug
);
5116 purple_connection_error(gc
, msg
);
5118 /* Invalidate this connection. Next send will open a new one */
5120 connection_remove(sip
, gsc
->fd
);
5121 purple_ssl_close(gsc
);
5127 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
5128 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
5130 PurpleConnection
*gc
= data
;
5131 struct sipe_account_data
*sip
;
5132 struct sip_connection
*conn
;
5134 gboolean firstread
= TRUE
;
5136 /* NOTE: This check *IS* necessary */
5137 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
5138 purple_ssl_close(gsc
);
5142 sip
= gc
->proto_data
;
5143 conn
= connection_find(sip
, gsc
->fd
);
5145 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
5146 gc
->wants_to_die
= TRUE
;
5147 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
5151 /* Read all available data from the SSL connection */
5153 /* Increase input buffer size as needed */
5154 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
5155 conn
->inbuflen
+= SIMPLE_BUF_INC
;
5156 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
5157 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
5160 /* Try to read as much as there is space left in the buffer */
5161 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
5162 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
5164 if (len
< 0 && errno
== EAGAIN
) {
5165 /* Try again later */
5167 } else if (len
< 0) {
5168 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
5170 } else if (firstread
&& (len
== 0)) {
5171 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
5175 conn
->inbufused
+= len
;
5178 /* Equivalence indicates that there is possibly more data to read */
5179 } while (len
== readlen
);
5181 conn
->inbuf
[conn
->inbufused
] = '\0';
5182 process_input(sip
, conn
);
5186 static void sipe_input_cb(gpointer data
, gint source
,
5187 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
5189 PurpleConnection
*gc
= data
;
5190 struct sipe_account_data
*sip
= gc
->proto_data
;
5192 struct sip_connection
*conn
= connection_find(sip
, source
);
5194 purple_debug_error("sipe", "Connection not found!\n");
5198 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
5199 conn
->inbuflen
+= SIMPLE_BUF_INC
;
5200 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
5203 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
5205 if (len
< 0 && errno
== EAGAIN
)
5207 else if (len
<= 0) {
5208 purple_debug_info("sipe", "sipe_input_cb: read error\n");
5209 connection_remove(sip
, source
);
5210 if (sip
->fd
== source
) sip
->fd
= -1;
5214 conn
->inbufused
+= len
;
5215 conn
->inbuf
[conn
->inbufused
] = '\0';
5217 process_input(sip
, conn
);
5220 /* Callback for new connections on incoming TCP port */
5221 static void sipe_newconn_cb(gpointer data
, gint source
,
5222 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
5224 PurpleConnection
*gc
= data
;
5225 struct sipe_account_data
*sip
= gc
->proto_data
;
5226 struct sip_connection
*conn
;
5228 int newfd
= accept(source
, NULL
, NULL
);
5230 conn
= connection_create(sip
, newfd
);
5232 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
5235 static void login_cb(gpointer data
, gint source
,
5236 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
5238 PurpleConnection
*gc
= data
;
5239 struct sipe_account_data
*sip
;
5240 struct sip_connection
*conn
;
5242 if (!PURPLE_CONNECTION_IS_VALID(gc
))
5250 purple_connection_error(gc
, _("Could not connect"));
5254 sip
= gc
->proto_data
;
5256 sip
->last_keepalive
= time(NULL
);
5258 conn
= connection_create(sip
, source
);
5262 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
5265 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
5266 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
5268 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
5269 if (sip
== NULL
) return;
5274 static guint
sipe_ht_hash_nick(const char *nick
)
5276 char *lc
= g_utf8_strdown(nick
, -1);
5277 guint bucket
= g_str_hash(lc
);
5283 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
5285 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
5288 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
5290 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5292 sip
->listen_data
= NULL
;
5294 if (listenfd
== -1) {
5295 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5301 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
5302 sip
->listenfd
= sip
->fd
;
5304 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
5306 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
5310 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
5311 SIPE_UNUSED_PARAMETER
const char *error_message
)
5313 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5315 sip
->query_data
= NULL
;
5317 if (!hosts
|| !hosts
->data
) {
5318 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
5322 hosts
= g_slist_remove(hosts
, hosts
->data
);
5323 g_free(sip
->serveraddr
);
5324 sip
->serveraddr
= hosts
->data
;
5325 hosts
= g_slist_remove(hosts
, hosts
->data
);
5327 hosts
= g_slist_remove(hosts
, hosts
->data
);
5328 g_free(hosts
->data
);
5329 hosts
= g_slist_remove(hosts
, hosts
->data
);
5332 /* create socket for incoming connections */
5333 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
5334 sipe_udp_host_resolved_listen_cb
, sip
);
5335 if (sip
->listen_data
== NULL
) {
5336 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5341 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
5342 PurpleSslErrorType error
,
5345 PurpleConnection
*gc
= data
;
5346 struct sipe_account_data
*sip
;
5348 /* If the connection is already disconnected, we don't need to do anything else */
5349 if (!PURPLE_CONNECTION_IS_VALID(gc
))
5352 sip
= gc
->proto_data
;
5357 case PURPLE_SSL_CONNECT_FAILED
:
5358 purple_connection_error(gc
, _("Connection Failed"));
5360 case PURPLE_SSL_HANDSHAKE_FAILED
:
5361 purple_connection_error(gc
, _("SSL Handshake Failed"));
5363 case PURPLE_SSL_CERTIFICATE_INVALID
:
5364 purple_connection_error(gc
, _("SSL Certificate Invalid"));
5370 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
5372 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5373 PurpleProxyConnectData
*connect_data
;
5375 sip
->listen_data
= NULL
;
5377 sip
->listenfd
= listenfd
;
5378 if (sip
->listenfd
== -1) {
5379 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5383 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
5384 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
5385 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
5386 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
5387 sipe_newconn_cb
, sip
->gc
);
5388 purple_debug_info("sipe", "connecting to %s port %d\n",
5389 sip
->realhostname
, sip
->realport
);
5390 /* open tcp connection to the server */
5391 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
5392 sip
->realport
, login_cb
, sip
->gc
);
5394 if (connect_data
== NULL
) {
5395 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
5400 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
5402 PurpleAccount
*account
= sip
->account
;
5403 PurpleConnection
*gc
= sip
->gc
;
5405 if (purple_account_get_bool(account
, "useport", FALSE
)) {
5406 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
5407 port
= purple_account_get_int(account
, "port", 0);
5409 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
5412 sip
->realhostname
= hostname
;
5413 sip
->realport
= port
;
5415 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
5418 /* TODO: is there a good default grow size? */
5419 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
5420 sip
->txbuf
= purple_circ_buffer_new(0);
5422 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
5424 if (!purple_ssl_is_supported()) {
5425 gc
->wants_to_die
= TRUE
;
5426 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
5430 purple_debug_info("sipe", "using SSL\n");
5432 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
5433 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
5434 if (sip
->gsc
== NULL
) {
5435 purple_connection_error(gc
, _("Could not create SSL context"));
5438 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
5440 purple_debug_info("sipe", "using UDP\n");
5442 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
5443 if (sip
->query_data
== NULL
) {
5444 purple_connection_error(gc
, _("Could not resolve hostname"));
5448 purple_debug_info("sipe", "using TCP\n");
5449 /* create socket for incoming connections */
5450 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
5451 sipe_tcp_connect_listen_cb
, sip
);
5452 if (sip
->listen_data
== NULL
) {
5453 purple_connection_error(gc
, _("Could not create listen socket"));
5459 /* Service list for autodection */
5460 static const struct sipe_service_data service_autodetect
[] = {
5461 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
5462 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
5463 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
5464 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
5468 /* Service list for SSL/TLS */
5469 static const struct sipe_service_data service_tls
[] = {
5470 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
5471 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
5475 /* Service list for TCP */
5476 static const struct sipe_service_data service_tcp
[] = {
5477 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
5478 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
5482 /* Service list for UDP */
5483 static const struct sipe_service_data service_udp
[] = {
5484 { "sip", "udp", SIPE_TRANSPORT_UDP
},
5488 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
5489 static void resolve_next_service(struct sipe_account_data
*sip
,
5490 const struct sipe_service_data
*start
)
5493 sip
->service_data
= start
;
5495 sip
->service_data
++;
5496 if (sip
->service_data
->service
== NULL
) {
5498 /* Try connecting to the SIP hostname directly */
5499 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
5500 if (sip
->auto_transport
) {
5501 // If SSL is supported, default to using it; OCS servers aren't configured
5502 // by default to accept TCP
5503 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
5504 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
5505 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
5508 hostname
= g_strdup(sip
->sipdomain
);
5509 create_connection(sip
, hostname
, 0);
5514 /* Try to resolve next service */
5515 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
5516 sip
->service_data
->transport
,
5521 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
5523 struct sipe_account_data
*sip
= data
;
5525 sip
->srv_query_data
= NULL
;
5527 /* find the host to connect to */
5529 gchar
*hostname
= g_strdup(resp
->hostname
);
5530 int port
= resp
->port
;
5531 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
5535 sip
->transport
= sip
->service_data
->type
;
5537 create_connection(sip
, hostname
, port
);
5539 resolve_next_service(sip
, NULL
);
5543 static void sipe_login(PurpleAccount
*account
)
5545 PurpleConnection
*gc
;
5546 struct sipe_account_data
*sip
;
5547 gchar
**signinname_login
, **userserver
, **domain_user
;
5548 const char *transport
;
5550 const char *username
= purple_account_get_username(account
);
5551 gc
= purple_account_get_connection(account
);
5553 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
5554 gc
->wants_to_die
= TRUE
;
5555 purple_connection_error(gc
, _("SIP Exchange username contains invalid characters"));
5559 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
5560 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
5561 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
5563 sip
->account
= account
;
5564 sip
->reregister_set
= FALSE
;
5565 sip
->reauthenticate_set
= FALSE
;
5566 sip
->subscribed
= FALSE
;
5567 sip
->subscribed_buddies
= FALSE
;
5569 signinname_login
= g_strsplit(username
, ",", 2);
5571 userserver
= g_strsplit(signinname_login
[0], "@", 2);
5572 purple_connection_set_display_name(gc
, userserver
[0]);
5573 sip
->username
= g_strjoin("@", userserver
[0], userserver
[1], NULL
);
5574 sip
->sipdomain
= g_strdup(userserver
[1]);
5576 if (strpbrk(sip
->username
, " \t\v\r\n") != NULL
) {
5577 gc
->wants_to_die
= TRUE
;
5578 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
5582 domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
5583 sip
->authdomain
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[0]) : NULL
;
5584 sip
->authuser
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[1]) : (signinname_login
? g_strdup(signinname_login
[1]) : NULL
);
5586 sip
->password
= g_strdup(purple_connection_get_password(gc
));
5588 g_strfreev(userserver
);
5589 g_strfreev(domain_user
);
5590 g_strfreev(signinname_login
);
5592 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
5594 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
5596 /* TODO: Set the status correctly. */
5597 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
5599 transport
= purple_account_get_string(account
, "transport", "auto");
5600 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
5601 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
5604 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
5605 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
5606 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
5607 } else if (strcmp(transport
, "auto") == 0) {
5608 sip
->auto_transport
= TRUE
;
5609 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
5610 } else if (strcmp(transport
, "tls") == 0) {
5611 resolve_next_service(sip
, service_tls
);
5612 } else if (strcmp(transport
, "tcp") == 0) {
5613 resolve_next_service(sip
, service_tcp
);
5615 resolve_next_service(sip
, service_udp
);
5619 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
5621 connection_free_all(sip
);
5626 if (sip
->query_data
!= NULL
)
5627 purple_dnsquery_destroy(sip
->query_data
);
5628 sip
->query_data
= NULL
;
5630 if (sip
->srv_query_data
!= NULL
)
5631 purple_srv_cancel(sip
->srv_query_data
);
5632 sip
->srv_query_data
= NULL
;
5634 if (sip
->listen_data
!= NULL
)
5635 purple_network_listen_cancel(sip
->listen_data
);
5636 sip
->listen_data
= NULL
;
5638 if (sip
->gsc
!= NULL
)
5639 purple_ssl_close(sip
->gsc
);
5642 sipe_auth_free(&sip
->registrar
);
5643 sipe_auth_free(&sip
->proxy
);
5646 purple_circ_buffer_destroy(sip
->txbuf
);
5649 g_free(sip
->realhostname
);
5650 sip
->realhostname
= NULL
;
5653 purple_input_remove(sip
->listenpa
);
5655 if (sip
->tx_handler
)
5656 purple_input_remove(sip
->tx_handler
);
5657 sip
->tx_handler
= 0;
5658 if (sip
->resendtimeout
)
5659 purple_timeout_remove(sip
->resendtimeout
);
5660 sip
->resendtimeout
= 0;
5661 if (sip
->timeouts
) {
5662 GSList
*entry
= sip
->timeouts
;
5664 struct scheduled_action
*sched_action
= entry
->data
;
5665 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
5666 purple_timeout_remove(sched_action
->timeout_handler
);
5667 if (sched_action
->destroy
) {
5668 (*sched_action
->destroy
)(sched_action
->payload
);
5670 g_free(sched_action
->name
);
5671 g_free(sched_action
);
5672 entry
= entry
->next
;
5675 g_slist_free(sip
->timeouts
);
5677 if (sip
->allow_events
) {
5678 GSList
*entry
= sip
->allow_events
;
5680 g_free(entry
->data
);
5681 entry
= entry
->next
;
5684 g_slist_free(sip
->allow_events
);
5686 if (sip
->containers
) {
5687 GSList
*entry
= sip
->containers
;
5689 free_container((struct sipe_container
*)entry
->data
);
5690 entry
= entry
->next
;
5693 g_slist_free(sip
->containers
);
5696 g_free(sip
->contact
);
5697 sip
->contact
= NULL
;
5699 g_free(sip
->regcallid
);
5700 sip
->regcallid
= NULL
;
5702 if (sip
->serveraddr
)
5703 g_free(sip
->serveraddr
);
5704 sip
->serveraddr
= NULL
;
5706 if (sip
->focus_factory_uri
)
5707 g_free(sip
->focus_factory_uri
);
5708 sip
->focus_factory_uri
= NULL
;
5711 sip
->processing_input
= FALSE
;
5715 * A callback for g_hash_table_foreach_remove
5717 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
5718 SIPE_UNUSED_PARAMETER gpointer user_data
)
5720 sipe_free_buddy((struct sipe_buddy
*) buddy
);
5722 /* We must return TRUE as the key/value have already been deleted */
5726 static void sipe_close(PurpleConnection
*gc
)
5728 struct sipe_account_data
*sip
= gc
->proto_data
;
5731 /* leave all conversations */
5732 sipe_session_close_all(sip
);
5733 sipe_session_remove_all(sip
);
5736 do_register_exp(sip
, 0);
5738 sipe_connection_cleanup(sip
);
5739 g_free(sip
->sipdomain
);
5740 g_free(sip
->username
);
5741 g_free(sip
->password
);
5742 g_free(sip
->authdomain
);
5743 g_free(sip
->authuser
);
5744 g_free(sip
->status
);
5746 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
5747 g_hash_table_destroy(sip
->buddies
);
5749 g_free(gc
->proto_data
);
5750 gc
->proto_data
= NULL
;
5753 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
5754 SIPE_UNUSED_PARAMETER
void *user_data
)
5756 PurpleAccount
*acct
= purple_connection_get_account(gc
);
5757 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
5758 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
5760 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
5761 purple_conversation_present(conv
);
5765 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
5766 SIPE_UNUSED_PARAMETER
void *user_data
)
5769 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
5770 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
5773 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
5774 SIPE_UNUSED_PARAMETER
struct transaction
*tc
)
5776 PurpleNotifySearchResults
*results
;
5777 PurpleNotifySearchColumn
*column
;
5778 xmlnode
*searchResults
;
5780 int match_count
= 0;
5781 gboolean more
= FALSE
;
5784 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
5786 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5787 if (!searchResults
) {
5788 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
5792 results
= purple_notify_searchresults_new();
5794 if (results
== NULL
) {
5795 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
5796 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
5798 xmlnode_free(searchResults
);
5802 column
= purple_notify_searchresults_column_new(_("User Name"));
5803 purple_notify_searchresults_column_add(results
, column
);
5805 column
= purple_notify_searchresults_column_new(_("Name"));
5806 purple_notify_searchresults_column_add(results
, column
);
5808 column
= purple_notify_searchresults_column_new(_("Company"));
5809 purple_notify_searchresults_column_add(results
, column
);
5811 column
= purple_notify_searchresults_column_new(_("Country"));
5812 purple_notify_searchresults_column_add(results
, column
);
5814 column
= purple_notify_searchresults_column_new(_("Email"));
5815 purple_notify_searchresults_column_add(results
, column
);
5817 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
5820 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
5821 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
5822 g_strfreev(uri_parts
);
5824 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
5825 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
5826 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
5827 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
5829 purple_notify_searchresults_row_add(results
, row
);
5833 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
5834 char *data
= xmlnode_get_data_unescaped(mrow
);
5835 more
= (g_strcasecmp(data
, "true") == 0);
5839 secondary
= g_strdup_printf(
5840 dngettext(GETTEXT_PACKAGE
,
5841 "Found %d contact%s:",
5842 "Found %d contacts%s:", match_count
),
5843 match_count
, more
? _(" (more matched your query)") : "");
5845 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
5846 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
5847 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
5850 xmlnode_free(searchResults
);
5854 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
5856 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
5857 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
5861 PurpleRequestField
*field
= entries
->data
;
5862 const char *id
= purple_request_field_get_id(field
);
5863 const char *value
= purple_request_field_string_get_value(field
);
5865 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
5867 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
5868 } while ((entries
= g_list_next(entries
)) != NULL
);
5872 gchar
*query
= g_strjoinv(NULL
, attrs
);
5873 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
5874 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
5875 send_soap_request_with_cb(gc
->proto_data
, body
,
5876 (TransCallback
) process_search_contact_response
, NULL
);
5884 static void sipe_show_find_contact(PurplePluginAction
*action
)
5886 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
5887 PurpleRequestFields
*fields
;
5888 PurpleRequestFieldGroup
*group
;
5889 PurpleRequestField
*field
;
5891 fields
= purple_request_fields_new();
5892 group
= purple_request_field_group_new(NULL
);
5893 purple_request_fields_add_group(fields
, group
);
5895 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
5896 purple_request_field_group_add_field(group
, field
);
5897 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
5898 purple_request_field_group_add_field(group
, field
);
5899 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
5900 purple_request_field_group_add_field(group
, field
);
5901 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
5902 purple_request_field_group_add_field(group
, field
);
5904 purple_request_fields(gc
,
5906 _("Search for a Contact"),
5907 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
5909 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
5911 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
5914 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
5915 SIPE_UNUSED_PARAMETER gpointer context
)
5918 PurplePluginAction
*act
;
5920 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
5921 menu
= g_list_prepend(menu
, act
);
5923 menu
= g_list_reverse(menu
);
5928 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
5932 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
5938 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
5944 static char *sipe_status_text(PurpleBuddy
*buddy
)
5946 struct sipe_account_data
*sip
;
5947 struct sipe_buddy
*sbuddy
;
5950 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
5951 if (sip
) //happens on pidgin exit
5953 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
5954 if (sbuddy
&& sbuddy
->annotation
)
5956 text
= g_strdup(sbuddy
->annotation
);
5963 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
5965 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
5966 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
5967 struct sipe_account_data
*sip
;
5968 struct sipe_buddy
*sbuddy
;
5969 char *annotation
= NULL
;
5971 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
5972 if (sip
) //happens on pidgin exit
5974 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
5977 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
5982 if (purple_presence_is_online(presence
))
5984 purple_notify_user_info_add_pair(user_info
, _("Status"), purple_status_get_name(status
));
5989 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
5996 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
5999 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
6000 g_hash_table_insert(table
, "login_label", (gpointer
)_("Sign-In Name..."));
6004 static PurpleBuddy
*
6005 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
6008 const gchar
*server_alias
, *email
;
6009 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
6011 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
6013 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
6015 server_alias
= g_strdup(purple_buddy_get_server_alias(buddy
));
6017 purple_blist_server_alias_buddy(clone
, server_alias
);
6020 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
6022 purple_blist_node_set_string((PurpleBlistNode
*)clone
, "email", email
);
6025 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
6027 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
6032 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
6034 PurpleBuddy
*buddy
, *b
;
6035 PurpleConnection
*gc
;
6036 PurpleGroup
* group
= purple_find_group(group_name
);
6038 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
6040 buddy
= (PurpleBuddy
*)node
;
6042 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
6043 gc
= purple_account_get_connection(buddy
->account
);
6045 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
6047 b
= purple_blist_add_buddy_clone(group
, buddy
);
6050 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
6054 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
6056 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6058 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: buddy->name=%s\n", buddy
->name
);
6060 /* 2007+ conference */
6061 if (sip
->msrtc_event_categories
)
6063 sipe_conf_add(sip
, buddy
->name
);
6065 else /* 2005- multiparty chat */
6067 gchar
*self
= sip_uri_self(sip
);
6068 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++sip
->chat_seq
);
6069 struct sip_session
*session
;
6071 session
= sipe_session_add_chat(sip
);
6072 session
->roster_manager
= g_strdup(self
);
6074 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, g_strdup(chat_name
));
6075 session
->chat_name
= g_strdup(chat_name
);
6076 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
6077 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
6078 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, FALSE
);
6086 sipe_is_election_finished(struct sip_session
*session
)
6088 gboolean res
= TRUE
;
6090 SIPE_DIALOG_FOREACH
{
6091 if (dialog
->election_vote
== 0) {
6095 } SIPE_DIALOG_FOREACH_END
;
6098 session
->is_voting_in_progress
= FALSE
;
6104 sipe_election_start(struct sipe_account_data
*sip
,
6105 struct sip_session
*session
)
6107 int election_timeout
;
6109 if (session
->is_voting_in_progress
) {
6110 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
6113 session
->is_voting_in_progress
= TRUE
;
6115 session
->bid
= rand();
6117 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
6119 SIPE_DIALOG_FOREACH
{
6120 /* reset election_vote for each chat participant */
6121 dialog
->election_vote
= 0;
6123 /* send RequestRM to each chat participant*/
6124 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
6125 } SIPE_DIALOG_FOREACH_END
;
6127 election_timeout
= 15; /* sec */
6128 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
6132 * @param who a URI to whom to invite to chat
6135 sipe_invite_to_chat(struct sipe_account_data
*sip
,
6136 struct sip_session
*session
,
6140 if (session
->focus_uri
)
6142 sipe_invite_conf(sip
, session
, who
);
6144 else /* a multi-party chat */
6146 gchar
*self
= sip_uri_self(sip
);
6147 if (session
->roster_manager
) {
6148 if (!strcmp(session
->roster_manager
, self
)) {
6149 sipe_invite(sip
, session
, who
, NULL
, NULL
, FALSE
);
6151 sipe_refer(sip
, session
, who
);
6154 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: no RM available\n");
6156 session
->pending_invite_queue
= slist_insert_unique_sorted(
6157 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
6159 sipe_election_start(sip
, session
);
6166 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
6167 struct sip_session
*session
)
6170 GSList
*entry
= session
->pending_invite_queue
;
6173 invitee
= entry
->data
;
6174 sipe_invite_to_chat(sip
, session
, invitee
);
6175 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
6181 sipe_election_result(struct sipe_account_data
*sip
,
6184 struct sip_session
*session
= (struct sip_session
*)sess
;
6186 gboolean has_won
= TRUE
;
6188 if (session
->roster_manager
) {
6189 purple_debug_info("sipe",
6190 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
6194 session
->is_voting_in_progress
= FALSE
;
6196 SIPE_DIALOG_FOREACH
{
6197 if (dialog
->election_vote
< 0) {
6199 rival
= dialog
->with
;
6202 } SIPE_DIALOG_FOREACH_END
;
6205 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
6207 session
->roster_manager
= sip_uri_self(sip
);
6209 SIPE_DIALOG_FOREACH
{
6210 /* send SetRM to each chat participant*/
6211 sipe_send_election_set_rm(sip
, dialog
);
6212 } SIPE_DIALOG_FOREACH_END
;
6214 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
6218 sipe_process_pending_invite_queue(sip
, session
);
6222 * For 2007+ conference only.
6225 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_name
)
6227 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6228 struct sip_session
*session
;
6230 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
6231 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_name=%s\n", chat_name
);
6233 session
= sipe_session_find_chat_by_name(sip
, chat_name
);
6235 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
6239 * For 2007+ conference only.
6242 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_name
)
6244 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6245 struct sip_session
*session
;
6247 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
6248 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_name=%s\n", chat_name
);
6250 session
= sipe_session_find_chat_by_name(sip
, chat_name
);
6252 sipe_conf_delete_user(sip
, session
, buddy
->name
);
6256 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, const char *chat_name
)
6258 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6259 struct sip_session
*session
;
6261 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: buddy->name=%s\n", buddy
->name
);
6262 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: chat_name=%s\n", chat_name
);
6264 session
= sipe_session_find_chat_by_name(sip
, chat_name
);
6266 sipe_invite_to_chat(sip
, session
, buddy
->name
);
6270 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
6273 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
6275 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
6278 char *mailto
= g_strdup_printf("mailto:%s", email
);
6279 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
6283 char *const parmList
[] = {mailto
, NULL
};
6284 if ((pid
= fork()) == -1)
6286 purple_debug_info("sipe", "fork() error\n");
6290 execvp("xdg-email", parmList
);
6291 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
6299 //@TODO resolve env variable %WINDIR% first
6300 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
6303 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
6312 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
6317 * A menu which appear when right-clicking on buddy in contact list.
6320 sipe_buddy_menu(PurpleBuddy
*buddy
)
6322 PurpleBlistNode
*g_node
;
6323 PurpleGroup
*group
, *gr_parent
;
6324 PurpleMenuAction
*act
;
6326 GList
*menu_groups
= NULL
;
6327 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6328 gchar
*self
= sip_uri_self(sip
);
6330 SIPE_SESSION_FOREACH
{
6331 if (strcmp(self
, buddy
->name
) && session
->chat_name
&& session
->conv
)
6333 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
6335 PurpleConvChatBuddyFlags flags
;
6336 PurpleConvChatBuddyFlags flags_us
;
6338 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
6339 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
6340 if (session
->focus_uri
6341 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
6342 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
6344 gchar
*label
= g_strdup_printf(_("Make Leader of '%s'"), session
->chat_name
);
6345 act
= purple_menu_action_new(label
,
6346 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
6347 g_strdup(session
->chat_name
), NULL
);
6349 menu
= g_list_prepend(menu
, act
);
6352 if (session
->focus_uri
6353 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
6355 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_name
);
6356 act
= purple_menu_action_new(label
,
6357 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
6358 g_strdup(session
->chat_name
), NULL
);
6360 menu
= g_list_prepend(menu
, act
);
6365 if (!session
->focus_uri
6366 || (session
->focus_uri
&& !session
->locked
))
6368 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_name
);
6369 act
= purple_menu_action_new(label
,
6370 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
6371 g_strdup(session
->chat_name
), NULL
);
6373 menu
= g_list_prepend(menu
, act
);
6377 } SIPE_SESSION_FOREACH_END
;
6379 act
= purple_menu_action_new(_("New Chat"),
6380 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
6382 menu
= g_list_prepend(menu
, act
);
6384 act
= purple_menu_action_new(_("Send Email..."),
6385 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
6387 menu
= g_list_prepend(menu
, act
);
6389 gr_parent
= purple_buddy_get_group(buddy
);
6390 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
6391 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
6394 group
= (PurpleGroup
*)g_node
;
6395 if (group
== gr_parent
)
6398 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
6401 act
= purple_menu_action_new(purple_group_get_name(group
),
6402 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
6404 menu_groups
= g_list_prepend(menu_groups
, act
);
6406 menu_groups
= g_list_reverse(menu_groups
);
6408 act
= purple_menu_action_new(_("Copy to"),
6411 menu
= g_list_prepend(menu
, act
);
6412 menu
= g_list_reverse(menu
);
6419 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
6421 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
6422 struct sip_session
*session
;
6424 session
= sipe_session_find_chat_by_name(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
6425 sipe_conf_modify_conference_lock(sip
, session
, locked
);
6429 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
6431 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
6432 sipe_conf_modify_lock(chat
, FALSE
);
6436 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
6438 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
6439 sipe_conf_modify_lock(chat
, TRUE
);
6443 sipe_chat_menu(PurpleChat
*chat
)
6445 PurpleMenuAction
*act
;
6446 PurpleConvChatBuddyFlags flags_us
;
6448 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
6449 struct sip_session
*session
;
6450 gchar
*self
= sip_uri_self(sip
);
6452 session
= sipe_session_find_chat_by_name(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
6453 if (!session
) return NULL
;
6455 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
6457 if (session
->focus_uri
6458 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
6460 if (session
->locked
) {
6461 act
= purple_menu_action_new(_("Unlock Conversation"),
6462 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
6464 menu
= g_list_prepend(menu
, act
);
6466 act
= purple_menu_action_new(_("Lock Conversation"),
6467 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
6469 menu
= g_list_prepend(menu
, act
);
6473 menu
= g_list_reverse(menu
);
6480 sipe_blist_node_menu(PurpleBlistNode
*node
)
6482 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
6483 return sipe_buddy_menu((PurpleBuddy
*) node
);
6484 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
6485 return sipe_chat_menu((PurpleChat
*)node
);
6492 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
6494 gboolean ret
= TRUE
;
6495 char *username
= (char *)trans
->payload
;
6497 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
6498 PurpleBuddy
*pbuddy
;
6499 struct sipe_buddy
*sbuddy
;
6501 char *server_alias
= NULL
;
6503 const char *device_name
= NULL
;
6505 purple_debug_info("sipe", "Fetching %s's user info for %s\n", username
, sip
->username
);
6507 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, username
);
6508 alias
= purple_buddy_get_local_alias(pbuddy
);
6512 //will query buddy UA's capabilities and send answer to log
6513 sipe_options_request(sip
, username
);
6515 sbuddy
= g_hash_table_lookup(sip
->buddies
, username
);
6518 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
6522 if (msg
->response
!= 200) {
6523 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
6525 xmlnode
*searchResults
;
6528 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
6529 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
6530 if (!searchResults
) {
6531 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
6532 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
6533 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
6534 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
6535 purple_notify_user_info_add_pair(info
, _("Job Title"), g_strdup(xmlnode_get_attrib(mrow
, "title")));
6536 purple_notify_user_info_add_pair(info
, _("Office"), g_strdup(xmlnode_get_attrib(mrow
, "office")));
6537 purple_notify_user_info_add_pair(info
, _("Business Phone"), g_strdup(xmlnode_get_attrib(mrow
, "phone")));
6538 purple_notify_user_info_add_pair(info
, _("Company"), g_strdup(xmlnode_get_attrib(mrow
, "company")));
6539 purple_notify_user_info_add_pair(info
, _("City"), g_strdup(xmlnode_get_attrib(mrow
, "city")));
6540 purple_notify_user_info_add_pair(info
, _("State"), g_strdup(xmlnode_get_attrib(mrow
, "state")));
6541 purple_notify_user_info_add_pair(info
, _("Country"), g_strdup(xmlnode_get_attrib(mrow
, "country")));
6542 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
6543 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
6544 if (!email
|| strcmp("", email
)) {
6545 if (!purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email")) {
6546 purple_blist_node_set_string((PurpleBlistNode
*)pbuddy
, "email", email
);
6550 xmlnode_free(searchResults
);
6553 purple_notify_user_info_add_section_break(info
);
6555 if (!server_alias
|| !strcmp("", server_alias
)) {
6556 g_free(server_alias
);
6557 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
6559 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
6563 // same as server alias, do not present
6564 alias
= (alias
&& server_alias
&& !strcmp(alias
, server_alias
)) ? NULL
: alias
;
6567 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
6570 if (!email
|| !strcmp("", email
)) {
6572 email
= g_strdup(purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email"));
6574 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
6580 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
6583 /* show a buddy's user info in a nice dialog box */
6584 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
6585 username
, /* buddy's username */
6587 NULL
, /* callback called when dialog closed */
6588 NULL
); /* userdata for callback */
6594 * AD search first, LDAP based
6596 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
6598 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
6599 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
6601 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
6602 send_soap_request_with_cb((struct sipe_account_data
*)gc
->proto_data
, body
,
6603 (TransCallback
) process_get_info_response
, (gpointer
)g_strdup(username
));
6608 static PurplePlugin
*my_protocol
= NULL
;
6610 static PurplePluginProtocolInfo prpl_info
=
6612 OPT_PROTO_CHAT_TOPIC
,
6613 NULL
, /* user_splits */
6614 NULL
, /* protocol_options */
6615 NO_BUDDY_ICONS
, /* icon_spec */
6616 sipe_list_icon
, /* list_icon */
6617 NULL
, /* list_emblems */
6618 sipe_status_text
, /* status_text */
6619 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
6620 sipe_status_types
, /* away_states */
6621 sipe_blist_node_menu
, /* blist_node_menu */
6622 NULL
, /* chat_info */
6623 NULL
, /* chat_info_defaults */
6624 sipe_login
, /* login */
6625 sipe_close
, /* close */
6626 sipe_im_send
, /* send_im */
6627 NULL
, /* set_info */ // TODO maybe
6628 sipe_send_typing
, /* send_typing */
6629 sipe_get_info
, /* get_info */
6630 sipe_set_status
, /* set_status */
6631 NULL
, /* set_idle */
6632 NULL
, /* change_passwd */
6633 sipe_add_buddy
, /* add_buddy */
6634 NULL
, /* add_buddies */
6635 sipe_remove_buddy
, /* remove_buddy */
6636 NULL
, /* remove_buddies */
6637 sipe_add_permit
, /* add_permit */
6638 sipe_add_deny
, /* add_deny */
6639 sipe_add_deny
, /* rem_permit */
6640 sipe_add_permit
, /* rem_deny */
6641 dummy_permit_deny
, /* set_permit_deny */
6642 NULL
, /* join_chat */
6643 NULL
, /* reject_chat */
6644 NULL
, /* get_chat_name */
6645 sipe_chat_invite
, /* chat_invite */
6646 sipe_chat_leave
, /* chat_leave */
6647 NULL
, /* chat_whisper */
6648 sipe_chat_send
, /* chat_send */
6649 sipe_keep_alive
, /* keepalive */
6650 NULL
, /* register_user */
6651 NULL
, /* get_cb_info */ // deprecated
6652 NULL
, /* get_cb_away */ // deprecated
6653 sipe_alias_buddy
, /* alias_buddy */
6654 sipe_group_buddy
, /* group_buddy */
6655 sipe_rename_group
, /* rename_group */
6656 NULL
, /* buddy_free */
6657 sipe_convo_closed
, /* convo_closed */
6658 purple_normalize_nocase
, /* normalize */
6659 NULL
, /* set_buddy_icon */
6660 sipe_remove_group
, /* remove_group */
6661 NULL
, /* get_cb_real_name */ // TODO?
6662 NULL
, /* set_chat_topic */
6663 NULL
, /* find_blist_chat */
6664 NULL
, /* roomlist_get_list */
6665 NULL
, /* roomlist_cancel */
6666 NULL
, /* roomlist_expand_category */
6667 NULL
, /* can_receive_file */
6668 NULL
, /* send_file */
6669 NULL
, /* new_xfer */
6670 NULL
, /* offline_message */
6671 NULL
, /* whiteboard_prpl_ops */
6672 sipe_send_raw
, /* send_raw */
6673 NULL
, /* roomlist_room_serialize */
6674 NULL
, /* unregister_user */
6675 NULL
, /* send_attention */
6676 NULL
, /* get_attention_types */
6678 sizeof(PurplePluginProtocolInfo
), /* struct_size */
6679 sipe_get_account_text_table
, /* get_account_text_table */
6683 static PurplePluginInfo info
= {
6684 PURPLE_PLUGIN_MAGIC
,
6685 PURPLE_MAJOR_VERSION
,
6686 PURPLE_MINOR_VERSION
,
6687 PURPLE_PLUGIN_PROTOCOL
, /**< type */
6688 NULL
, /**< ui_requirement */
6690 NULL
, /**< dependencies */
6691 PURPLE_PRIORITY_DEFAULT
, /**< priority */
6692 "prpl-sipe", /**< id */
6693 "Microsoft LCS/OCS", /**< name */
6694 VERSION
, /**< version */
6695 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
6696 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
6697 "Anibal Avelar <avelar@gmail.com>, " /**< author */
6698 "Gabriel Burt <gburt@novell.com>", /**< author */
6699 PURPLE_WEBSITE
, /**< homepage */
6700 sipe_plugin_load
, /**< load */
6701 sipe_plugin_unload
, /**< unload */
6702 sipe_plugin_destroy
, /**< destroy */
6703 NULL
, /**< ui_info */
6704 &prpl_info
, /**< extra_info */
6713 static void sipe_plugin_destroy(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
6717 entry
= prpl_info
.protocol_options
;
6719 purple_account_option_destroy(entry
->data
);
6720 entry
= g_list_delete_link(entry
, entry
);
6722 prpl_info
.protocol_options
= NULL
;
6724 entry
= prpl_info
.user_splits
;
6726 purple_account_user_split_destroy(entry
->data
);
6727 entry
= g_list_delete_link(entry
, entry
);
6729 prpl_info
.user_splits
= NULL
;
6732 static void init_plugin(PurplePlugin
*plugin
)
6734 PurpleAccountUserSplit
*split
;
6735 PurpleAccountOption
*option
;
6740 purple_debug_info(PACKAGE
, "bindtextdomain = %s\n", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
6741 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s\n",
6742 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
6743 textdomain(GETTEXT_PACKAGE
);
6746 purple_plugin_register(plugin
);
6748 split
= purple_account_user_split_new(_("Login \n domain\\user or\n someone@linux.com "), NULL
, ',');
6749 purple_account_user_split_set_reverse(split
, FALSE
);
6750 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
6752 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
6753 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6754 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
6755 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6757 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
6758 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6759 // Translators: noun (networking port)
6760 option
= purple_account_option_int_new(_("Port"), "port", 5061);
6761 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6763 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
6764 purple_account_option_add_list_item(option
, _("Auto"), "auto");
6765 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
6766 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
6767 purple_account_option_add_list_item(option
, _("UDP"), "udp");
6768 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6770 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
6771 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
6773 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
6774 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6777 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
6778 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6780 /* Suitable for sspi/NTLM, sspi/Kerberos and krb5 security mechanisms
6781 * No login/password is taken into account if this option present,
6782 * instead used default credentials stored in OS.
6784 option
= purple_account_option_bool_new(_("Use Single Sign-On"), "sso", TRUE
);
6785 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6787 my_protocol
= plugin
;
6790 /* I had to redefined the function for it load, but works */
6791 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
6792 plugin
->info
= &(info
);
6793 init_plugin((plugin
));
6794 sipe_plugin_load((plugin
));
6795 return purple_plugin_register(plugin
);