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>"
107 /* Our publication type keys. OCS 2007+
108 * Format: SIPE_PUB_{Category}[_{SubSategory}]
110 #define SIPE_PUB_DEVICE "000"
111 #define SIPE_PUB_STATE_MACHINE "100"
112 #define SIPE_PUB_NOTE "200"
114 static char *genbranch()
116 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
117 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
118 rand() & 0xFFFF, rand() & 0xFFFF);
121 static const char *sipe_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount
*a
,
122 SIPE_UNUSED_PARAMETER PurpleBuddy
*b
)
127 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
129 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
131 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
132 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
135 static void sipe_close(PurpleConnection
*gc
);
137 static void send_presence_status(struct sipe_account_data
*sip
);
139 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
141 static void sipe_keep_alive(PurpleConnection
*gc
)
143 struct sipe_account_data
*sip
= gc
->proto_data
;
144 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
145 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
146 gchar buf
[2] = {0, 0};
147 purple_debug_info("sipe", "sending keep alive\n");
148 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
150 time_t now
= time(NULL
);
151 if ((sip
->keepalive_timeout
> 0) &&
152 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
153 #if PURPLE_VERSION_CHECK(2,4,0)
154 && ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
157 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
158 sendout_pkt(gc
, "\r\n\r\n");
159 sip
->last_keepalive
= now
;
164 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
166 struct sip_connection
*ret
= NULL
;
167 GSList
*entry
= sip
->openconns
;
170 if (ret
->fd
== fd
) return ret
;
176 static void sipe_auth_free(struct sip_auth
*auth
)
178 g_free(auth
->opaque
);
182 g_free(auth
->target
);
184 auth
->type
= AUTH_TYPE_UNSET
;
187 g_free(auth
->gssapi_data
);
188 auth
->gssapi_data
= NULL
;
189 sip_sec_destroy_context(auth
->gssapi_context
);
190 auth
->gssapi_context
= NULL
;
193 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
195 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
197 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
201 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
203 struct sip_connection
*conn
= connection_find(sip
, fd
);
205 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
206 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
212 static void connection_free_all(struct sipe_account_data
*sip
)
214 struct sip_connection
*ret
= NULL
;
215 GSList
*entry
= sip
->openconns
;
218 connection_remove(sip
, ret
->fd
);
219 entry
= sip
->openconns
;
223 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
226 const char *authuser
= sip
->authuser
;
230 if (!authuser
|| strlen(authuser
) < 1) {
231 authuser
= sip
->username
;
234 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
235 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
237 // If we have a signature for the message, include that
238 if (msg
->signature
) {
239 return g_strdup_printf("%s qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", crand=\"%s\", cnum=\"%s\", response=\"%s\"", auth_protocol
, auth
->opaque
, auth
->realm
, auth
->target
, msg
->rand
, msg
->num
, msg
->signature
);
242 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
243 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
247 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
250 purple_account_get_bool(sip
->account
, "sso", TRUE
),
251 sip
->authdomain
? sip
->authdomain
: "",
256 if (!gssapi_data
|| !auth
->gssapi_context
) {
257 sip
->gc
->wants_to_die
= TRUE
;
258 purple_connection_error(sip
->gc
, _("Failed to authenticate to server"));
262 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
263 ret
= g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth_protocol
, opaque
, auth
->realm
, auth
->target
, gssapi_data
);
269 return g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth_protocol
, auth
->realm
, auth
->target
);
271 } else { /* Digest */
273 /* Calculate new session key */
275 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
276 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
277 authuser
, auth
->realm
, sip
->password
,
278 auth
->gssapi_data
, NULL
);
281 sprintf(noncecount
, "%08d", auth
->nc
++);
282 response
= purple_cipher_http_digest_calculate_response("md5",
283 msg
->method
, msg
->target
, NULL
, NULL
,
284 auth
->gssapi_data
, noncecount
, NULL
,
286 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
288 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
);
294 static char *parse_attribute(const char *attrname
, const char *source
)
296 const char *tmp
, *tmp2
;
298 int len
= strlen(attrname
);
300 if (!strncmp(source
, attrname
, len
)) {
302 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
304 retval
= g_strndup(tmp
, tmp2
- tmp
);
306 retval
= g_strdup(tmp
);
312 static void fill_auth(gchar
*hdr
, struct sip_auth
*auth
)
318 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
322 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
323 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
324 auth
->type
= AUTH_TYPE_NTLM
;
327 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
328 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
329 auth
->type
= AUTH_TYPE_KERBEROS
;
333 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
334 auth
->type
= AUTH_TYPE_DIGEST
;
338 parts
= g_strsplit(hdr
, "\", ", 0);
339 for (i
= 0; parts
[i
]; i
++) {
342 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
344 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
345 g_free(auth
->gssapi_data
);
346 auth
->gssapi_data
= tmp
;
348 if (auth
->type
== AUTH_TYPE_NTLM
) {
349 /* NTLM module extracts nonce from gssapi-data */
353 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
354 /* Only used with AUTH_TYPE_DIGEST */
355 g_free(auth
->gssapi_data
);
356 auth
->gssapi_data
= tmp
;
357 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
358 g_free(auth
->opaque
);
360 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
364 if (auth
->type
== AUTH_TYPE_DIGEST
) {
365 /* Throw away old session key */
366 g_free(auth
->opaque
);
371 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
372 g_free(auth
->target
);
381 static void sipe_canwrite_cb(gpointer data
,
382 SIPE_UNUSED_PARAMETER gint source
,
383 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
385 PurpleConnection
*gc
= data
;
386 struct sipe_account_data
*sip
= gc
->proto_data
;
390 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
392 if (max_write
== 0) {
393 if (sip
->tx_handler
!= 0){
394 purple_input_remove(sip
->tx_handler
);
400 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
402 if (written
< 0 && errno
== EAGAIN
)
404 else if (written
<= 0) {
405 /*TODO: do we really want to disconnect on a failure to write?*/
406 purple_connection_error(gc
, _("Could not write"));
410 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
413 static void sipe_canwrite_cb_ssl(gpointer data
,
414 SIPE_UNUSED_PARAMETER gint src
,
415 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
417 PurpleConnection
*gc
= data
;
418 struct sipe_account_data
*sip
= gc
->proto_data
;
422 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
424 if (max_write
== 0) {
425 if (sip
->tx_handler
!= 0) {
426 purple_input_remove(sip
->tx_handler
);
432 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
434 if (written
< 0 && errno
== EAGAIN
)
436 else if (written
<= 0) {
437 /*TODO: do we really want to disconnect on a failure to write?*/
438 purple_connection_error(gc
, _("Could not write"));
442 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
445 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
447 static void send_later_cb(gpointer data
, gint source
,
448 SIPE_UNUSED_PARAMETER
const gchar
*error
)
450 PurpleConnection
*gc
= data
;
451 struct sipe_account_data
*sip
;
452 struct sip_connection
*conn
;
454 if (!PURPLE_CONNECTION_IS_VALID(gc
))
462 purple_connection_error(gc
, _("Could not connect"));
466 sip
= gc
->proto_data
;
468 sip
->connecting
= FALSE
;
469 sip
->last_keepalive
= time(NULL
);
471 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
473 /* If there is more to write now, we need to register a handler */
474 if (sip
->txbuf
->bufused
> 0)
475 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
477 conn
= connection_create(sip
, source
);
478 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
481 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
483 struct sipe_account_data
*sip
;
484 struct sip_connection
*conn
;
486 if (!PURPLE_CONNECTION_IS_VALID(gc
))
488 if (gsc
) purple_ssl_close(gsc
);
492 sip
= gc
->proto_data
;
495 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
496 sip
->connecting
= FALSE
;
497 sip
->last_keepalive
= time(NULL
);
499 conn
= connection_create(sip
, gsc
->fd
);
501 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
506 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
507 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
509 PurpleConnection
*gc
= data
;
510 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
511 if (sip
== NULL
) return;
513 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
515 /* If there is more to write now */
516 if (sip
->txbuf
->bufused
> 0) {
517 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
522 static void sendlater(PurpleConnection
*gc
, const char *buf
)
524 struct sipe_account_data
*sip
= gc
->proto_data
;
526 if (!sip
->connecting
) {
527 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
528 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
529 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
531 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
532 purple_connection_error(gc
, _("Couldn't create socket"));
535 sip
->connecting
= TRUE
;
538 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
539 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
541 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
544 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
546 struct sipe_account_data
*sip
= gc
->proto_data
;
547 time_t currtime
= time(NULL
);
548 int writelen
= strlen(buf
);
550 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
551 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
552 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
553 purple_debug_info("sipe", "could not send packet\n");
562 if (sip
->tx_handler
) {
567 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
569 ret
= write(sip
->fd
, buf
, writelen
);
573 if (ret
< 0 && errno
== EAGAIN
)
575 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
580 if (ret
< writelen
) {
581 if (!sip
->tx_handler
){
583 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
586 sip
->tx_handler
= purple_input_add(sip
->fd
,
587 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
592 /* XXX: is it OK to do this? You might get part of a request sent
593 with part of another. */
594 if (sip
->txbuf
->bufused
> 0)
595 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
597 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
603 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
605 sendout_pkt(gc
, buf
);
609 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
611 GSList
*tmp
= msg
->headers
;
614 GString
*outstr
= g_string_new("");
615 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
617 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
618 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
619 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
620 tmp
= g_slist_next(tmp
);
622 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
623 sendout_pkt(sip
->gc
, outstr
->str
);
624 g_string_free(outstr
, TRUE
);
627 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
631 if (sip
->registrar
.type
== AUTH_TYPE_UNSET
) {
635 if (sip
->registrar
.gssapi_context
) {
636 struct sipmsg_breakdown msgbd
;
637 gchar
*signature_input_str
;
639 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
640 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
641 sip
->registrar
.ntlm_num
++;
642 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
643 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
644 if (signature_input_str
!= NULL
) {
645 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
646 msg
->signature
= signature_hex
;
647 msg
->rand
= g_strdup(msgbd
.rand
);
648 msg
->num
= g_strdup(msgbd
.num
);
649 g_free(signature_input_str
);
651 sipmsg_breakdown_free(&msgbd
);
654 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
655 buf
= auth_header(sip
, &sip
->registrar
, msg
);
657 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
660 } 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")) {
661 sip
->registrar
.nc
= 3;
663 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
665 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
668 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
673 buf
= auth_header(sip
, &sip
->registrar
, msg
);
674 sipmsg_add_header_now_pos(msg
, "Proxy-Authorization", buf
, 5);
677 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
683 static char *get_contact_service(struct sipe_account_data *sip)
685 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()));
686 //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);
690 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
691 const char *text
, const char *body
)
695 GString
*outstr
= g_string_new("");
696 struct sipe_account_data
*sip
= gc
->proto_data
;
699 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
701 contact
= get_contact(sip
);
702 sipmsg_add_header(msg
, "Contact", contact
);
707 sprintf(len
, "%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
708 sipmsg_add_header(msg
, "Content-Length", len
);
710 sipmsg_add_header(msg
, "Content-Length", "0");
713 msg
->response
= code
;
715 sipmsg_strip_headers(msg
, keepers
);
716 sipmsg_merge_new_headers(msg
);
717 sign_outgoing_message(msg
, sip
, msg
->method
);
719 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
722 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
723 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
725 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
726 tmp
= g_slist_next(tmp
);
728 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
729 sendout_pkt(gc
, outstr
->str
);
730 g_string_free(outstr
, TRUE
);
733 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
735 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
736 if (trans
->msg
) sipmsg_free(trans
->msg
);
741 static struct transaction
*
742 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
744 gchar
*call_id
= NULL
;
746 struct transaction
*trans
= g_new0(struct transaction
, 1);
748 trans
->time
= time(NULL
);
749 trans
->msg
= (struct sipmsg
*)msg
;
750 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
751 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
752 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
753 trans
->callback
= callback
;
754 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
758 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
760 struct transaction
*trans
;
761 GSList
*transactions
= sip
->transactions
;
762 gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
763 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
764 gchar
*key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
766 while (transactions
) {
767 trans
= transactions
->data
;
768 if (!g_strcasecmp(trans
->key
, key
)) {
772 transactions
= transactions
->next
;
780 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
781 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
782 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
784 struct sipe_account_data
*sip
= gc
->proto_data
;
785 const char *addh
= "";
788 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
789 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
790 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
791 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
792 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
793 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
794 gchar
*route
= strdup("");
795 gchar
*epid
= get_epid(sip
); // TODO generate one per account/login
796 int cseq
= dialog
? ++dialog
->cseq
:
797 /* This breaks OCS2007: own presence, contact search, ?
798 1 .* as Call-Id is new in this case */
800 struct transaction
*trans
;
802 if (dialog
&& dialog
->routes
)
804 GSList
*iter
= dialog
->routes
;
809 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
811 iter
= g_slist_next(iter
);
815 if (!ourtag
&& !dialog
) {
819 if (!strcmp(method
, "REGISTER")) {
820 if (sip
->regcallid
) {
822 callid
= g_strdup(sip
->regcallid
);
824 sip
->regcallid
= g_strdup(callid
);
828 if (addheaders
) addh
= addheaders
;
830 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
831 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
832 "From: <sip:%s>%s%s;epid=%s\r\n"
833 "To: <%s>%s%s%s%s\r\n"
834 "Max-Forwards: 70\r\n"
839 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
841 dialog
&& dialog
->request
? dialog
->request
: url
,
842 TRANSPORT_DESCRIPTOR
,
843 purple_network_get_my_ip(-1),
845 branch
? ";branch=" : "",
846 branch
? branch
: "",
848 ourtag
? ";tag=" : "",
849 ourtag
? ourtag
: "",
852 theirtag
? ";tag=" : "",
853 theirtag
? theirtag
: "",
854 theirepid
? ";epid=" : "",
855 theirepid
? theirepid
: "",
862 body
? (gsize
) strlen(body
) : 0,
866 //printf ("parsing msg buf:\n%s\n\n", buf);
867 msg
= sipmsg_parse_msg(buf
);
878 sign_outgoing_message (msg
, sip
, method
);
880 buf
= sipmsg_to_string (msg
);
882 /* add to ongoing transactions */
883 trans
= transactions_add_buf(sip
, msg
, tc
);
884 sendout_pkt(gc
, buf
);
890 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
892 gchar
*from
= sip_uri_self(sip
);
893 gchar
*contact
= get_contact(sip
);
894 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
895 "Content-Type: application/SOAP+xml\r\n",contact
);
897 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
898 tr
->payload
= payload
;
905 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
907 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
910 static char *get_contact_register(struct sipe_account_data
*sip
)
912 char *epid
= get_epid(sip
);
913 char *uuid
= generateUUIDfromEPID(epid
);
914 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
);
920 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
928 if (!sip
->sipdomain
) return;
930 uri
= sip_uri_from_name(sip
->sipdomain
);
931 expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
932 to
= sip_uri_self(sip
);
933 contact
= get_contact_register(sip
);
934 hdr
= g_strdup_printf("Contact: %s\r\n"
935 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
936 "Event: registration\r\n"
937 "Allow-Events: presence\r\n"
938 "ms-keep-alive: UAC;hop-hop=yes\r\n"
939 "%s", contact
, expires
);
943 sip
->registerstatus
= 1;
945 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
946 process_register_response
);
953 static void do_register_cb(struct sipe_account_data
*sip
,
954 SIPE_UNUSED_PARAMETER
void *unused
)
956 do_register_exp(sip
, -1);
957 sip
->reregister_set
= FALSE
;
960 static void do_register(struct sipe_account_data
*sip
)
962 do_register_exp(sip
, -1);
966 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
968 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
969 send_soap_request(sip
, body
);
974 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
977 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
979 purple_debug_info("sipe", "Blocking contact %s\n", who
);
982 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
986 void sipe_auth_user_cb(void * data
)
988 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
991 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
996 void sipe_deny_user_cb(void * data
)
998 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1001 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1006 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1008 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1009 sipe_contact_allow_deny(sip
, name
, TRUE
);
1013 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1015 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1016 sipe_contact_allow_deny(sip
, name
, FALSE
);
1020 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1022 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1023 sipe_contact_set_acl(sip, name, "");
1027 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1031 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1032 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1034 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1036 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1037 if (!watchers
) return;
1039 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1040 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1041 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1042 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1044 // TODO pull out optional displayName to pass as alias
1046 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1047 job
->who
= remote_user
;
1049 purple_account_request_authorization(
1063 xmlnode_free(watchers
);
1068 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1070 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1071 if (!purple_group
) {
1072 purple_group
= purple_group_new(group
->name
);
1073 purple_blist_add_group(purple_group
, NULL
);
1077 group
->purple_group
= purple_group
;
1078 sip
->groups
= g_slist_append(sip
->groups
, group
);
1079 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1081 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1085 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1087 struct sipe_group
*group
;
1093 entry
= sip
->groups
;
1095 group
= entry
->data
;
1096 if (group
->id
== id
) {
1099 entry
= entry
->next
;
1104 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1106 struct sipe_group
*group
;
1112 entry
= sip
->groups
;
1114 group
= entry
->data
;
1115 if (!strcmp(group
->name
, name
)) {
1118 entry
= entry
->next
;
1124 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1127 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1128 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1129 send_soap_request(sip
, body
);
1131 g_free(group
->name
);
1132 group
->name
= g_strdup(name
);
1136 * Only appends if no such value already stored.
1139 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1140 GSList
* res
= list
;
1141 if (!g_slist_find_custom(list
, data
, func
)) {
1142 res
= g_slist_insert_sorted(list
, data
, func
);
1148 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1149 return group1
->id
- group2
->id
;
1153 * Returns string like "2 4 7 8" - group ids buddy belong to.
1156 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1159 //creating array from GList, converting int to gchar*
1160 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1161 GSList
*entry
= buddy
->groups
;
1163 struct sipe_group
* group
= entry
->data
;
1164 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1165 entry
= entry
->next
;
1169 res
= g_strjoinv(" ", ids_arr
);
1170 g_strfreev(ids_arr
);
1175 * Sends buddy update to server
1178 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1180 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1181 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1183 if (buddy
&& purple_buddy
) {
1184 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1186 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1187 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1189 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1190 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1192 send_soap_request(sip
, body
);
1198 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1200 if (msg
->response
== 200) {
1201 struct sipe_group
*group
;
1202 struct group_user_context
*ctx
= (struct group_user_context
*)tc
->payload
;
1206 struct sipe_buddy
*buddy
;
1208 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1214 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1221 group_id
= xmlnode_get_data(node
);
1228 group
= g_new0(struct sipe_group
, 1);
1229 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1231 group
->name
= ctx
->group_name
;
1233 sipe_group_add(sip
, group
);
1235 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1237 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1240 sipe_group_set_user(sip
, ctx
->user_name
);
1249 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1251 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1253 ctx
->group_name
= g_strdup(name
);
1254 ctx
->user_name
= g_strdup(who
);
1256 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1257 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1262 * Data structure for scheduled actions
1264 typedef void (*Action
) (struct sipe_account_data
*, void *);
1266 struct scheduled_action
{
1269 * Format is <Event>[<Data>...]
1270 * Example: <presence><sip:user@domain.com> or <registration>
1273 guint timeout_handler
;
1274 gboolean repetitive
;
1276 GDestroyNotify destroy
;
1277 struct sipe_account_data
*sip
;
1283 * Should return FALSE if repetitive action is not needed
1285 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1288 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1289 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1290 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1291 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1292 ret
= sched_action
->repetitive
;
1293 if (sched_action
->destroy
) {
1294 (*sched_action
->destroy
)(sched_action
->payload
);
1296 g_free(sched_action
->name
);
1297 g_free(sched_action
);
1302 * Kills action timer effectively cancelling
1305 * @param name of action
1307 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1311 if (!sip
->timeouts
|| !name
) return;
1313 entry
= sip
->timeouts
;
1315 struct scheduled_action
*sched_action
= entry
->data
;
1316 if(!strcmp(sched_action
->name
, name
)) {
1317 GSList
*to_delete
= entry
;
1318 entry
= entry
->next
;
1319 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1320 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1321 purple_timeout_remove(sched_action
->timeout_handler
);
1322 if (sched_action
->destroy
) {
1323 (*sched_action
->destroy
)(sched_action
->payload
);
1325 g_free(sched_action
->name
);
1326 g_free(sched_action
);
1328 entry
= entry
->next
;
1334 sipe_schedule_action0(const gchar
*name
,
1338 GDestroyNotify destroy
,
1339 struct sipe_account_data
*sip
,
1342 struct scheduled_action
*sched_action
;
1344 /* Make sure each action only exists once */
1345 sipe_cancel_scheduled_action(sip
, name
);
1347 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1348 sched_action
= g_new0(struct scheduled_action
, 1);
1349 sched_action
->repetitive
= FALSE
;
1350 sched_action
->name
= g_strdup(name
);
1351 sched_action
->action
= action
;
1352 sched_action
->destroy
= destroy
;
1353 sched_action
->sip
= sip
;
1354 sched_action
->payload
= payload
;
1355 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1356 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1357 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1358 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1362 * Do schedule action for execution in the future.
1363 * Non repetitive execution.
1365 * @param name of action (will be copied)
1366 * @param timeout in seconds
1367 * @action callback function
1368 * @payload callback data (can be NULL, otherwise caller must allocate memory)
1371 sipe_schedule_action(const gchar
*name
,
1374 GDestroyNotify destroy
,
1375 struct sipe_account_data
*sip
,
1378 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1382 * Same as sipe_schedule_action() but timeout is in milliseconds.
1385 sipe_schedule_action_msec(const gchar
*name
,
1388 GDestroyNotify destroy
,
1389 struct sipe_account_data
*sip
,
1392 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1396 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1398 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
1399 SIPE_UNUSED_PARAMETER
struct transaction
*tc
)
1401 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1403 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1408 static void sipe_subscribe_resource_uri(const char *name
,
1409 SIPE_UNUSED_PARAMETER gpointer value
,
1410 gchar
**resources_uri
)
1412 gchar
*tmp
= *resources_uri
;
1413 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1417 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1419 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1420 if (sbuddy
&& !sbuddy
->resubscribed
) { // Only not resubscribed contacts; the first time everybody are included
1421 gchar
*tmp
= *resources_uri
;
1422 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"><context/></resource>\n", tmp
, name
);
1428 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1429 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1430 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1431 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1432 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1435 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1437 gchar
*contact
= get_contact(sip
);
1440 gchar
*require
= "";
1442 gchar
*autoextend
= "";
1443 gchar
*content_type
;
1445 if (sip
->msrtc_event_categories
) {
1446 require
= ", categoryList";
1447 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1448 content_type
= "application/msrtc-adrl-categorylist+xml";
1449 content
= g_strdup_printf(
1450 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1451 "<action name=\"subscribe\" id=\"63792024\">\n"
1452 "<adhocList>\n%s</adhocList>\n"
1453 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1454 "<category name=\"note\"/>\n"
1455 "<category name=\"state\"/>\n"
1458 "</batchSub>", sip
->username
, resources_uri
);
1460 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1461 content_type
= "application/adrl+xml";
1462 content
= g_strdup_printf(
1463 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1464 "<create xmlns=\"\">\n%s</create>\n"
1465 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1467 g_free(resources_uri
);
1469 request
= g_strdup_printf(
1470 "Require: adhoclist%s\r\n"
1471 "Supported: eventlist\r\n"
1472 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1473 "Supported: ms-piggyback-first-notify\r\n"
1474 "%sSupported: ms-benotify\r\n"
1475 "Proxy-Require: ms-benotify\r\n"
1476 "Event: presence\r\n"
1477 "Content-Type: %s\r\n"
1478 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1481 /* subscribe to buddy presence */
1482 //send_sip_request(sip->gc, "SUBSCRIBE", resource_uri, resource_uri, request, content, NULL, process_subscribe_response);
1483 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1490 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
,
1491 SIPE_UNUSED_PARAMETER
void *unused
)
1493 gchar
*to
= sip_uri_self(sip
);
1494 gchar
*resources_uri
= g_strdup("");
1495 if (sip
->msrtc_event_categories
) {
1496 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1498 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1500 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1503 struct presence_batched_routed
{
1508 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1510 struct presence_batched_routed
*data
= payload
;
1511 GSList
*buddies
= data
->buddies
;
1513 g_free(buddies
->data
);
1514 buddies
= buddies
->next
;
1516 g_slist_free(data
->buddies
);
1521 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
1523 struct presence_batched_routed
*data
= payload
;
1524 GSList
*buddies
= data
->buddies
;
1525 gchar
*resources_uri
= g_strdup("");
1527 gchar
*tmp
= resources_uri
;
1528 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
1530 buddies
= buddies
->next
;
1532 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
1533 g_strdup(data
->host
));
1537 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1538 * The user sends a single SUBSCRIBE request to the subscribed contact.
1539 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1543 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
1545 gchar
*to
= sip_uri((char *)buddy_name
);
1546 gchar
*tmp
= get_contact(sip
);
1549 gchar
*autoextend
= "";
1551 if (!sip
->msrtc_event_categories
)
1552 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1554 request
= g_strdup_printf(
1555 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1556 "Supported: ms-piggyback-first-notify\r\n"
1557 "%sSupported: ms-benotify\r\n"
1558 "Proxy-Require: ms-benotify\r\n"
1559 "Event: presence\r\n"
1560 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1561 "Contact: %s\r\n", autoextend
,tmp
);
1563 content
= g_strdup_printf(
1564 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1565 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1566 "<resource uri=\"%s\"/>\n"
1568 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1569 "<category name=\"note\"/>\n"
1570 "<category name=\"state\"/>\n"
1573 "</batchSub>", sip
->username
, to
1578 /* subscribe to buddy presence */
1579 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1586 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1588 if (!purple_status_is_active(status
))
1592 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
1595 g_free(sip
->status
);
1596 sip
->status
= g_strdup(purple_status_get_id(status
));
1597 send_presence_status(sip
);
1603 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
,
1604 SIPE_UNUSED_PARAMETER
const char *alias
)
1606 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1607 sipe_group_set_user(sip
, name
);
1611 sipe_group_buddy(PurpleConnection
*gc
,
1613 const char *old_group_name
,
1614 const char *new_group_name
)
1616 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1617 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1618 struct sipe_group
* old_group
= NULL
;
1619 struct sipe_group
* new_group
;
1621 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1622 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1624 if(!buddy
) { // buddy not in roaming list
1628 if (old_group_name
) {
1629 old_group
= sipe_group_find_by_name(sip
, g_strdup(old_group_name
));
1631 new_group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1634 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1635 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1639 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1641 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1642 sipe_group_set_user(sip
, who
);
1646 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1648 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1649 struct sipe_buddy
*b
;
1651 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1653 // Prepend sip: if needed
1654 if (strncmp("sip:", buddy
->name
, 4)) {
1655 gchar
*buf
= sip_uri_from_name(buddy
->name
);
1656 purple_blist_rename_buddy(buddy
, buf
);
1660 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1661 b
= g_new0(struct sipe_buddy
, 1);
1662 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1663 b
->name
= g_strdup(buddy
->name
);
1664 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1665 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1666 sipe_subscribe_presence_single(sip
, b
->name
); //@TODO should go to callback
1668 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1672 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1676 * We are calling g_hash_table_foreach_steal(). That means that no
1677 * key/value deallocation functions are called. Therefore the glib
1678 * hash code does not touch the key (buddy->name) or value (buddy)
1679 * of the to-be-deleted hash node at all. It follows that we
1681 * - MUST free the memory for the key ourselves and
1682 * - ARE allowed to do it in this function
1684 * Conclusion: glib must be broken on the Windows platform if sipe
1685 * crashes with SIGTRAP when closing. You'll have to live
1686 * with the memory leak until this is fixed.
1688 g_free(buddy
->name
);
1690 g_free(buddy
->annotation
);
1691 g_free(buddy
->device_name
);
1692 g_slist_free(buddy
->groups
);
1697 * Unassociates buddy from group first.
1698 * Then see if no groups left, removes buddy completely.
1699 * Otherwise updates buddy groups on server.
1701 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1703 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1704 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1705 struct sipe_group
*g
= NULL
;
1707 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1712 g
= sipe_group_find_by_name(sip
, group
->name
);
1716 b
->groups
= g_slist_remove(b
->groups
, g
);
1717 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
1720 if (g_slist_length(b
->groups
) < 1) {
1721 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
1722 sipe_cancel_scheduled_action(sip
, action_name
);
1723 g_free(action_name
);
1725 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1728 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1729 send_soap_request(sip
, body
);
1735 //updates groups on server
1736 sipe_group_set_user(sip
, b
->name
);
1742 sipe_rename_group(PurpleConnection
*gc
,
1743 const char *old_name
,
1745 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
1747 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1748 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1750 sipe_group_rename(sip
, s_group
, group
->name
);
1752 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1757 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1759 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1760 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1763 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1764 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1765 send_soap_request(sip
, body
);
1768 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1769 g_free(s_group
->name
);
1772 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1776 static GList
*sipe_status_types(SIPE_UNUSED_PARAMETER PurpleAccount
*acc
)
1778 PurpleStatusType
*type
;
1779 GList
*types
= NULL
;
1782 type
= purple_status_type_new_with_attrs(
1783 PURPLE_STATUS_AVAILABLE
, NULL
, _("Online"), TRUE
, TRUE
, FALSE
,
1784 // Translators: noun
1785 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1787 types
= g_list_append(types
, type
);
1790 type
= purple_status_type_new_with_attrs(
1791 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_BUSY
, _("Busy"), TRUE
, TRUE
, FALSE
,
1792 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1794 types
= g_list_append(types
, type
);
1796 // Do Not Disturb (not user settable)
1797 type
= purple_status_type_new_with_attrs(
1798 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_DND
, _("Do Not Disturb"), TRUE
, FALSE
, FALSE
,
1799 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1801 types
= g_list_append(types
, type
);
1804 type
= purple_status_type_new_with_attrs(
1805 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_BRB
, _("Be Right Back"), TRUE
, TRUE
, FALSE
,
1806 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1808 types
= g_list_append(types
, type
);
1811 type
= purple_status_type_new_with_attrs(
1812 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1813 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1815 types
= g_list_append(types
, type
);
1818 type
= purple_status_type_new_with_attrs(
1819 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_ONPHONE
, _("On The Phone"), TRUE
, TRUE
, FALSE
,
1820 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1822 types
= g_list_append(types
, type
);
1825 type
= purple_status_type_new_with_attrs(
1826 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_LUNCH
, _("Out To Lunch"), TRUE
, TRUE
, FALSE
,
1827 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1829 types
= g_list_append(types
, type
);
1832 type
= purple_status_type_new_full(
1833 PURPLE_STATUS_INVISIBLE
, NULL
, _("Appear Offline"), TRUE
, TRUE
, FALSE
);
1834 types
= g_list_append(types
, type
);
1837 type
= purple_status_type_new_full(
1838 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1839 types
= g_list_append(types
, type
);
1845 * A callback for g_hash_table_foreach
1847 static void sipe_buddy_subscribe_cb(SIPE_UNUSED_PARAMETER
char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1849 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
1850 int time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1851 int timeout
= (time_range
* rand()) / RAND_MAX
; /* random period within the range */
1852 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy
->name
));
1856 * Removes entries from purple buddy list
1857 * that does not correspond ones in the roaming contact list.
1859 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
1860 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1861 GSList
*entry
= buddies
;
1862 struct sipe_buddy
*buddy
;
1866 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
1867 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
1870 g
= purple_buddy_get_group(b
);
1871 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
1873 gboolean in_sipe_groups
= FALSE
;
1874 GSList
*entry2
= buddy
->groups
;
1876 struct sipe_group
*group
= entry2
->data
;
1877 if (!strcmp(group
->name
, g
->name
)) {
1878 in_sipe_groups
= TRUE
;
1881 entry2
= entry2
->next
;
1883 if(!in_sipe_groups
) {
1884 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
1885 purple_blist_remove_buddy(b
);
1888 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
1889 purple_blist_remove_buddy(b
);
1891 entry
= entry
->next
;
1895 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1897 int len
= msg
->bodylen
;
1899 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1902 const gchar
*contacts_delta
;
1903 xmlnode
*group_node
;
1904 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1908 /* Convert the contact from XML to Purple Buddies */
1909 isc
= xmlnode_from_str(msg
->body
, len
);
1914 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
1915 if (contacts_delta
) {
1916 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1919 if (!strcmp(isc
->name
, "contactList")) {
1922 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1923 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1924 const char *name
= xmlnode_get_attrib(group_node
, "name");
1926 if (!strncmp(name
, "~", 1)) {
1927 name
= _("Other Contacts");
1929 group
->name
= g_strdup(name
);
1930 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1932 sipe_group_add(sip
, group
);
1935 // Make sure we have at least one group
1936 if (g_slist_length(sip
->groups
) == 0) {
1937 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1938 PurpleGroup
*purple_group
;
1939 group
->name
= g_strdup(_("Other Contacts"));
1941 purple_group
= purple_group_new(group
->name
);
1942 purple_blist_add_group(purple_group
, NULL
);
1943 sip
->groups
= g_slist_append(sip
->groups
, group
);
1946 /* Parse contacts */
1947 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1948 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
1949 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
1950 gchar
* groups
= g_strdup(xmlnode_get_attrib(item
, "groups"));
1951 gchar
* buddy_name
= sip_uri_from_name(uri
);
1952 gchar
**item_groups
;
1953 struct sipe_group
*group
= NULL
;
1954 struct sipe_buddy
*buddy
= NULL
;
1957 // assign to group Other Contacts if nothing else received
1958 if(!groups
|| !strcmp("", groups
) ) {
1959 group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
1960 groups
= group
? g_strdup_printf("%d", group
->id
) : "1";
1963 item_groups
= g_strsplit(groups
, " ", 0);
1965 while (item_groups
[i
]) {
1966 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
1968 // If couldn't find the right group for this contact, just put them in the first group we have
1969 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1970 group
= sip
->groups
->data
;
1973 if (group
!= NULL
) {
1974 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1976 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1977 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1980 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
1981 if (name
!= NULL
&& strlen(name
) != 0) {
1982 purple_blist_alias_buddy(b
, name
);
1987 buddy
= g_new0(struct sipe_buddy
, 1);
1988 buddy
->name
= g_strdup(b
->name
);
1989 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
1992 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1994 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
1996 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2001 } // while, contact groups
2002 g_strfreev(item_groups
);
2010 sipe_cleanup_local_blist(sip
);
2014 //subscribe to buddies
2015 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2016 if(sip
->batched_support
){
2017 sipe_subscribe_presence_batched(sip
, NULL
);
2020 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2022 sip
->subscribed_buddies
= TRUE
;
2029 * Subscribe roaming contacts
2031 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
)
2033 gchar
*to
= sip_uri_self(sip
);
2034 gchar
*tmp
= get_contact(sip
);
2035 gchar
*hdr
= g_strdup_printf(
2036 "Event: vnd-microsoft-roaming-contacts\r\n"
2037 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2038 "Supported: com.microsoft.autoextend\r\n"
2039 "Supported: ms-benotify\r\n"
2040 "Proxy-Require: ms-benotify\r\n"
2041 "Supported: ms-piggyback-first-notify\r\n"
2042 "Contact: %s\r\n", tmp
);
2045 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2050 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
,
2051 SIPE_UNUSED_PARAMETER
void *unused
)
2053 gchar
*to
= sip_uri_self(sip
);
2054 gchar
*tmp
= get_contact(sip
);
2055 gchar
*hdr
= g_strdup_printf(
2056 "Event: presence.wpending\r\n"
2057 "Accept: text/xml+msrtc.wpending\r\n"
2058 "Supported: com.microsoft.autoextend\r\n"
2059 "Supported: ms-benotify\r\n"
2060 "Proxy-Require: ms-benotify\r\n"
2061 "Supported: ms-piggyback-first-notify\r\n"
2062 "Contact: %s\r\n", tmp
);
2065 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2071 * Fires on deregistration event initiated by server.
2072 * [MS-SIPREGE] SIP extension.
2077 // Content-Type: text/registration-event
2078 // subscription-state: terminated;expires=0
2079 // ms-diagnostics-public: 4141;reason="User disabled"
2081 // deregistered;event=rejected
2083 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2085 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2086 gchar
*event
= NULL
;
2087 gchar
*reason
= NULL
;
2088 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2090 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2091 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2093 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2094 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2095 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2096 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2098 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2102 if (warning
!= NULL
) {
2103 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2104 } else { // for LCS2005
2106 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2107 error_id
= 4140; // [MS-SIPREGE]
2108 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2109 reason
= g_strdup(_("You have been signed off because you've signed in at another location"));
2110 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2112 reason
= g_strdup(_("User disabled")); // [MS-OCER]
2113 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2115 reason
= g_strdup(_("User moved")); // [MS-OCER]
2119 warning
= g_strdup_printf(_("Unregistered by Server: %s."), reason
? reason
: _("no reason given"));
2122 sip
->gc
->wants_to_die
= TRUE
;
2123 purple_connection_error(sip
->gc
, warning
);
2128 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2130 xmlnode
*xn_provision_group_list
;
2133 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2135 /* provisionGroup */
2136 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2137 if (!strcmp("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2138 g_free(sip
->focus_factory_uri
);
2139 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2140 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2141 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2145 xmlnode_free(xn_provision_group_list
);
2148 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2150 const gchar
*contacts_delta
;
2153 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2159 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2162 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2169 free_container(struct sipe_container
*container
)
2173 if (!container
) return;
2175 entry
= container
->members
;
2177 g_free(entry
->data
);
2178 entry
= g_slist_remove(entry
, entry
->data
);
2184 * Finds locally stored MS-PRES container member
2186 static struct sipe_container_member
*
2187 sipe_find_container_member(struct sipe_container
*container
,
2191 struct sipe_container_member
*member
;
2194 if (container
== NULL
|| type
== NULL
) {
2198 entry
= container
->members
;
2200 member
= entry
->data
;
2201 if (!g_strcasecmp(member
->type
, type
)
2202 && ((!member
->value
&& !value
)
2203 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2207 entry
= entry
->next
;
2213 * Finds locally stored MS-PRES container by id
2215 static struct sipe_container
*
2216 sipe_find_container(struct sipe_account_data
*sip
,
2219 struct sipe_container
*container
;
2226 entry
= sip
->containers
;
2228 container
= entry
->data
;
2229 if (id
== container
->id
) {
2232 entry
= entry
->next
;
2246 sipe_find_access_level(struct sipe_account_data
*sip
,
2250 guint containers
[] = {32000, 400, 300, 200, 100};
2253 for (i
= 0; i
< 5; i
++) {
2254 struct sipe_container_member
*member
;
2255 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2256 if (!container
) continue;
2258 member
= sipe_find_container_member(container
, type
, value
);
2260 return containers
[i
];
2268 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2270 guint container_version
,
2271 const gchar
* action
,
2275 gchar
*self
= sip_uri_self(sip
);
2276 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2279 gchar
*body
= g_strdup_printf(
2280 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2281 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2282 "</setContainerMembers>",
2290 contact
= get_contact(sip
);
2291 hdr
= g_strdup_printf("Contact: %s\r\n"
2292 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2295 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2303 free_publication(struct sipe_publication
*publication
)
2305 g_free(publication
->category
);
2306 g_free(publication
);
2309 /* key is <category><instance><container> */
2311 sipe_is_our_publication(struct sipe_account_data
*sip
,
2316 /* filling keys for our publications if not yet cached */
2317 if (!sip
->our_publication_keys
) {
2318 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
2319 guint machine_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
2321 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2322 g_strdup_printf("<%s><%d><%d>", "device", device_instance
, 2));
2323 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2324 g_strdup_printf("<%s><%d><%d>", "state", machine_instance
, 2));
2325 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2326 g_strdup_printf("<%s><%d><%d>", "state", machine_instance
, 3));
2329 entry
= sip
->our_publication_keys
;
2331 if (!strcmp(entry
->data
, key
)) {
2334 entry
= entry
->next
;
2340 * When we receive some self (BE) NOTIFY with a new subscriber
2341 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2344 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2351 char *display_name
= NULL
;
2352 PurpleBuddy
*pbuddy
;
2356 GHashTable
*our_publications_tmp
= NULL
;
2358 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2360 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2363 contact
= get_contact(sip
);
2364 to
= sip_uri_self(sip
);
2368 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2369 const gchar
*name
= xmlnode_get_attrib(node
, "name");
2370 guint instance
= atoi(xmlnode_get_attrib(node
, "instance"));
2371 guint container
= atoi(xmlnode_get_attrib(node
, "container"));
2372 guint version
= atoi(xmlnode_get_attrib(node
, "version"));
2373 /* key is <category><instance><container> */
2374 gchar
*key
= g_strdup_printf("<%s><%d><%d>", name
, instance
, container
);
2375 purple_debug_info("sipe", "sipe_process_roaming_self: key=%s version=%d\n", key
, version
);
2376 if (sipe_is_our_publication(sip
, key
)) {
2377 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2378 publication
->category
= g_strdup(name
);
2379 publication
->instance
= instance
;
2380 publication
->container
= container
;
2381 publication
->version
= version
;
2383 if (!our_publications_tmp
) {
2384 our_publications_tmp
= g_hash_table_new_full(
2385 g_str_hash
, g_str_equal
,
2386 g_free
, (GDestroyNotify
)free_publication
);
2388 g_hash_table_insert(our_publications_tmp
, g_strdup(key
), publication
);
2389 purple_debug_info("sipe", "sipe_process_roaming_self: added key=%s version=%d\n", key
, version
);
2393 if (our_publications_tmp
) {
2394 g_hash_table_destroy(sip
->our_publications
);
2395 sip
->our_publications
= our_publications_tmp
;
2396 our_publications_tmp
= NULL
;
2398 purple_debug_info("sipe", "sipe_process_roaming_self: sip->our_publications size=%d\n",
2399 sip
->our_publications
? g_hash_table_size(sip
->our_publications
) : -1);
2402 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2403 guint id
= atoi(xmlnode_get_attrib(node
, "id"));
2404 struct sipe_container
*container
= sipe_find_container(sip
, id
);
2407 sip
->containers
= g_slist_remove(sip
->containers
, container
);
2408 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
2409 free_container(container
);
2411 container
= g_new0(struct sipe_container
, 1);
2413 container
->version
= atoi(xmlnode_get_attrib(node
, "version"));
2414 sip
->containers
= g_slist_append(sip
->containers
, container
);
2415 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
2417 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
2418 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
2419 member
->type
= xmlnode_get_attrib(node2
, "type");
2420 member
->value
= xmlnode_get_attrib(node2
, "value");
2421 container
->members
= g_slist_append(container
->members
, member
);
2422 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
2423 member
->type
, member
->value
? member
->value
: "");
2427 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
2428 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
2429 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
2430 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
2431 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
2432 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
2433 /* initial set-up to let counterparties see your status */
2434 if (sameEnterpriseAL
< 0) {
2435 struct sipe_container
*container
= sipe_find_container(sip
, 200);
2436 guint version
= container
? container
->version
: 0;
2437 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
2439 if (federatedAL
< 0) {
2440 struct sipe_container
*container
= sipe_find_container(sip
, 100);
2441 guint version
= container
? container
->version
: 0;
2442 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
2444 sip
->access_level_set
= TRUE
;
2448 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2450 const char *acknowledged
;
2454 user
= xmlnode_get_attrib(node
, "user");
2455 if (!user
) continue;
2456 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
2457 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
2458 uri_user
= sip_uri_from_name(user
);
2459 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri_user
);
2461 alias
= purple_buddy_get_local_alias(pbuddy
);
2462 uri_alias
= sip_uri_from_name(alias
);
2463 if (display_name
&& !g_ascii_strcasecmp(uri_user
, uri_alias
)) { // 'bad' alias
2464 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri_user
, display_name
);
2465 purple_blist_alias_buddy(pbuddy
, display_name
);
2470 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
2471 if(!g_ascii_strcasecmp(acknowledged
,"false")){
2472 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
2473 if (!purple_find_buddy(sip
->account
, uri_user
)) {
2474 purple_account_request_add(sip
->account
, uri_user
, _("you"), display_name
, NULL
);
2477 hdr
= g_strdup_printf(
2479 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2481 body
= g_strdup_printf(
2482 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2483 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2484 "</setSubscribers>", user
);
2486 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
2490 g_free(display_name
);
2499 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
)
2501 gchar
*to
= sip_uri_self(sip
);
2502 gchar
*tmp
= get_contact(sip
);
2503 gchar
*hdr
= g_strdup_printf(
2504 "Event: vnd-microsoft-roaming-ACL\r\n"
2505 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
2506 "Supported: com.microsoft.autoextend\r\n"
2507 "Supported: ms-benotify\r\n"
2508 "Proxy-Require: ms-benotify\r\n"
2509 "Supported: ms-piggyback-first-notify\r\n"
2510 "Contact: %s\r\n", tmp
);
2513 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2519 * To request for presence information about the user, access level settings that have already been configured by the user
2520 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
2521 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
2524 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
)
2526 gchar
*to
= sip_uri_self(sip
);
2527 gchar
*tmp
= get_contact(sip
);
2528 gchar
*hdr
= g_strdup_printf(
2529 "Event: vnd-microsoft-roaming-self\r\n"
2530 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
2531 "Supported: ms-benotify\r\n"
2532 "Proxy-Require: ms-benotify\r\n"
2533 "Supported: ms-piggyback-first-notify\r\n"
2535 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
2537 gchar
*body
=g_strdup(
2538 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
2539 "<roaming type=\"categories\"/>"
2540 "<roaming type=\"containers\"/>"
2541 "<roaming type=\"subscribers\"/></roamingList>");
2544 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2553 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
)
2555 gchar
*to
= sip_uri_self(sip
);
2556 gchar
*tmp
= get_contact(sip
);
2557 gchar
*hdr
= g_strdup_printf(
2558 "Event: vnd-microsoft-provisioning\r\n"
2559 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
2560 "Supported: com.microsoft.autoextend\r\n"
2561 "Supported: ms-benotify\r\n"
2562 "Proxy-Require: ms-benotify\r\n"
2563 "Supported: ms-piggyback-first-notify\r\n"
2565 "Contact: %s\r\n", tmp
);
2568 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
2573 /** Subscription for provisioning information to help with initial
2574 * configuration. This subscription is a one-time query (denoted by the Expires header,
2575 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
2576 * configuration, meeting policies, and policy settings that Communicator must enforce.
2577 * TODO: for what we need this information.
2580 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
)
2582 gchar
*to
= sip_uri_self(sip
);
2583 gchar
*tmp
= get_contact(sip
);
2584 gchar
*hdr
= g_strdup_printf(
2585 "Event: vnd-microsoft-provisioning-v2\r\n"
2586 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
2587 "Supported: com.microsoft.autoextend\r\n"
2588 "Supported: ms-benotify\r\n"
2589 "Proxy-Require: ms-benotify\r\n"
2590 "Supported: ms-piggyback-first-notify\r\n"
2593 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
2594 gchar
*body
= g_strdup(
2595 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
2596 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
2597 "<provisioningGroup name=\"ucPolicy\"/>"
2598 "</provisioningGroupList>");
2601 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2607 /* IM Session (INVITE and MESSAGE methods) */
2609 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
2611 get_end_points (struct sipe_account_data
*sip
,
2612 struct sip_session
*session
)
2616 if (session
== NULL
) {
2620 res
= g_strdup_printf("<sip:%s>", sip
->username
);
2622 SIPE_DIALOG_FOREACH
{
2624 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
2627 if (dialog
->theirepid
) {
2629 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
2632 } SIPE_DIALOG_FOREACH_END
;
2638 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_account_data
*sip
,
2640 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2642 gboolean ret
= TRUE
;
2644 if (msg
->response
!= 200) {
2645 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
2649 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
2655 * Asks UA/proxy about its capabilities.
2657 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
2659 gchar
*to
= sip_uri(who
);
2660 gchar
*contact
= get_contact(sip
);
2661 gchar
*request
= g_strdup_printf(
2662 "Accept: application/sdp\r\n"
2663 "Contact: %s\r\n", contact
);
2666 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
2673 sipe_notify_user(struct sipe_account_data
*sip
,
2674 struct sip_session
*session
,
2675 PurpleMessageFlags flags
,
2676 const gchar
*message
)
2678 PurpleConversation
*conv
;
2680 if (!session
->conv
) {
2681 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
2683 conv
= session
->conv
;
2685 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
2689 sipe_present_info(struct sipe_account_data
*sip
,
2690 struct sip_session
*session
,
2691 const gchar
*message
)
2693 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
2697 sipe_present_err(struct sipe_account_data
*sip
,
2698 struct sip_session
*session
,
2699 const gchar
*message
)
2701 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_ERROR
, message
);
2705 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
2706 struct sip_session
*session
,
2708 const gchar
*message
)
2710 char *msg
, *msg_tmp
;
2712 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
2713 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2715 msg_tmp
= g_strdup_printf( _("This message was not delivered to %s because one or more recipients are offline:\n%s") ,
2716 who
? who
: "", msg
? msg
: "");
2717 sipe_present_err(sip
, session
, msg_tmp
);
2723 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
);
2726 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
2727 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2729 gboolean ret
= TRUE
;
2730 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2731 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
2732 struct sip_dialog
*dialog
;
2738 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2743 dialog
= sipe_dialog_find(session
, with
);
2745 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2750 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2751 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
2753 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2755 if (msg
->response
>= 400) {
2756 PurpleBuddy
*pbuddy
;
2757 gchar
*alias
= with
;
2759 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
2761 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
2762 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
2765 sipe_present_message_undelivered_err(sip
, session
, alias
, message
);
2768 gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
2770 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
));
2771 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
2772 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
2775 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2776 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
2777 key
, g_hash_table_size(session
->unconfirmed_messages
));
2783 if (ret
) sipe_im_process_queue(sip
, session
);
2788 sipe_is_election_finished(struct sip_session
*session
);
2791 sipe_election_result(struct sipe_account_data
*sip
,
2795 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
2796 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2798 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2799 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2800 struct sip_dialog
*dialog
;
2801 struct sip_session
*session
;
2803 session
= sipe_session_find_chat_by_callid(sip
, callid
);
2805 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
2809 if (msg
->response
== 200 && !strncmp(contenttype
, "application/x-ms-mim", 20)) {
2810 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2811 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
2812 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
2814 if (xn_request_rm_response
) {
2815 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
2816 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
2818 dialog
= sipe_dialog_find(session
, with
);
2820 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
2824 if (allow
&& !g_strcasecmp(allow
, "true")) {
2825 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
2826 dialog
->election_vote
= 1;
2827 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
2828 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
2829 dialog
->election_vote
= -1;
2832 if (sipe_is_election_finished(session
)) {
2833 sipe_election_result(sip
, session
);
2836 } else if (xn_set_rm_response
) {
2839 xmlnode_free(xn_action
);
2846 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
)
2855 sipe_parse_html(msg
, &msgformat
, &msgtext
);
2856 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
2858 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2861 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2864 msgr
= g_strdup("");
2867 tmp
= get_contact(sip
);
2868 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
2869 //hdr = g_strdup("Content-Type: text/rtf\r\n");
2870 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
2871 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n", tmp
, msgr
);
2875 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
2882 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
2884 GSList
*entry2
= session
->outgoing_message_queue
;
2886 char *queued_msg
= entry2
->data
;
2888 /* for multiparty chat or conference */
2889 if (session
->is_multiparty
|| session
->focus_uri
) {
2890 gchar
*who
= sip_uri_self(sip
);
2891 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
2892 PURPLE_MESSAGE_SEND
, queued_msg
, time(NULL
));
2896 SIPE_DIALOG_FOREACH
{
2899 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
2901 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
2902 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
2903 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
2904 key
, g_hash_table_size(session
->unconfirmed_messages
));
2907 sipe_send_message(sip
, dialog
, queued_msg
);
2908 } SIPE_DIALOG_FOREACH_END
;
2910 entry2
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2916 sipe_refer_notify(struct sipe_account_data
*sip
,
2917 struct sip_session
*session
,
2924 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
2926 hdr
= g_strdup_printf(
2928 "Subscription-State: %s\r\n"
2929 "Content-Type: message/sipfrag\r\n",
2930 status
>= 200 ? "terminated" : "active");
2932 body
= g_strdup_printf(
2933 "SIP/2.0 %d %s\r\n",
2936 send_sip_request(sip
->gc
, "NOTIFY", who
, who
, hdr
, body
, dialog
, NULL
);
2943 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2945 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2946 struct sip_session
*session
;
2947 struct sip_dialog
*dialog
;
2951 struct sipmsg
*request_msg
= trans
->msg
;
2953 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2956 session
= sipe_session_find_chat_by_callid(sip
, callid
);
2958 session
= sipe_session_find_im(sip
, with
);
2961 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2966 dialog
= sipe_dialog_find(session
, with
);
2968 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2973 sipe_dialog_parse(dialog
, msg
, TRUE
);
2975 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2976 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
2978 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2980 if (msg
->response
!= 200) {
2981 PurpleBuddy
*pbuddy
;
2982 gchar
*alias
= with
;
2984 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2986 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
2987 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
2991 sipe_present_message_undelivered_err(sip
, session
, alias
, message
);
2993 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
2994 sipe_present_err(sip
, session
, tmp_msg
);
2998 sipe_dialog_remove(session
, with
);
3006 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
3007 dialog
->outgoing_invite
= NULL
;
3008 dialog
->is_established
= TRUE
;
3010 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
3012 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
3013 g_free(referred_by
);
3016 /* add user to chat if it is a multiparty session */
3017 if (session
->is_multiparty
) {
3018 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3020 PURPLE_CBFLAGS_NONE
, TRUE
);
3023 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
3024 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
3025 if (session
->outgoing_message_queue
) {
3026 char *queued_msg
= session
->outgoing_message_queue
->data
;
3027 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
3032 sipe_im_process_queue(sip
, session
);
3034 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3035 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
3036 key
, g_hash_table_size(session
->unconfirmed_messages
));
3045 sipe_invite(struct sipe_account_data
*sip
,
3046 struct sip_session
*session
,
3048 const gchar
*msg_body
,
3049 const gchar
*referred_by
,
3050 const gboolean is_triggered
)
3057 char *ms_text_format
= g_strdup("");
3058 gchar
*roster_manager
;
3060 gchar
*referred_by_str
;
3061 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3063 if (dialog
&& dialog
->is_established
) {
3064 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
3069 dialog
= sipe_dialog_add(session
);
3070 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
3071 dialog
->with
= g_strdup(who
);
3074 if (!(dialog
->ourtag
)) {
3075 dialog
->ourtag
= gentag();
3088 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
3089 purple_debug_info("sipe", "sipe_invite: msgformat=%s\n", msgformat
);
3091 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3095 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
3099 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
3100 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
3105 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
3106 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
3107 purple_debug_info("sipe", "sipe_invite: added message %s to unconfirmed_messages(count=%d)\n",
3108 key
, g_hash_table_size(session
->unconfirmed_messages
));
3112 contact
= get_contact(sip
);
3113 end_points
= get_end_points(sip
, session
);
3114 self
= sip_uri_self(sip
);
3115 roster_manager
= g_strdup_printf(
3116 "Roster-Manager: %s\r\n"
3117 "EndPoints: %s\r\n",
3120 referred_by_str
= referred_by
?
3122 "Referred-By: %s\r\n",
3125 hdr
= g_strdup_printf(
3126 "Supported: ms-sender\r\n"
3132 "Content-Type: application/sdp\r\n",
3133 (session
->roster_manager
&& !strcmp(session
->roster_manager
, self
)) ? roster_manager
: "",
3135 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
3136 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
3139 g_free(ms_text_format
);
3142 body
= g_strdup_printf(
3144 "o=- 0 0 IN IP4 %s\r\n"
3148 "m=message %d sip null\r\n"
3149 "a=accept-types:text/plain text/html image/gif "
3150 "multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3151 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
3153 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
3154 to
, to
, hdr
, body
, dialog
, process_invite_response
);
3157 g_free(roster_manager
);
3159 g_free(referred_by_str
);
3166 sipe_refer(struct sipe_account_data
*sip
,
3167 struct sip_session
*session
,
3172 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
3173 session
->roster_manager
);
3175 contact
= get_contact(sip
);
3176 hdr
= g_strdup_printf(
3178 "Refer-to: <%s>\r\n"
3179 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
3180 "Require: com.microsoft.rtc-multiparty\r\n",
3184 dialog
->ourtag
? ";tag=" : "",
3185 dialog
->ourtag
? dialog
->ourtag
: "",
3188 send_sip_request(sip
->gc
, "REFER",
3189 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
3196 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
3197 struct sip_dialog
*dialog
,
3200 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
3202 gchar
*body
= g_strdup_printf(
3203 "<?xml version=\"1.0\"?>\r\n"
3204 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3205 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
3206 sip
->username
, bid
);
3208 send_sip_request(sip
->gc
, "INFO",
3209 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
3215 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
3216 struct sip_dialog
*dialog
)
3218 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
3220 gchar
*body
= g_strdup_printf(
3221 "<?xml version=\"1.0\"?>\r\n"
3222 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3223 "<SetRM uri=\"sip:%s\"/></action>\r\n",
3226 send_sip_request(sip
->gc
, "INFO",
3227 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
3233 sipe_session_close(struct sipe_account_data
*sip
,
3234 struct sip_session
* session
)
3236 if (session
&& session
->focus_uri
) {
3237 conf_session_close(sip
, session
);
3241 SIPE_DIALOG_FOREACH
{
3242 /* @TODO slow down BYE message sending rate */
3243 /* @see single subscription code */
3244 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
3245 } SIPE_DIALOG_FOREACH_END
;
3247 sipe_session_remove(sip
, session
);
3252 sipe_session_close_all(struct sipe_account_data
*sip
)
3255 while ((entry
= sip
->sessions
) != NULL
) {
3256 sipe_session_close(sip
, entry
->data
);
3261 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
3263 struct sipe_account_data
*sip
= gc
->proto_data
;
3265 purple_debug_info("sipe", "conversation with %s closed\n", who
);
3266 sipe_session_close(sip
, sipe_session_find_im(sip
, who
));
3270 sipe_chat_leave (PurpleConnection
*gc
, int id
)
3272 struct sipe_account_data
*sip
= gc
->proto_data
;
3273 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
3275 sipe_session_close(sip
, session
);
3278 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
3279 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3281 struct sipe_account_data
*sip
= gc
->proto_data
;
3282 struct sip_session
*session
;
3283 struct sip_dialog
*dialog
;
3285 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
3287 session
= sipe_session_find_or_add_im(sip
, who
);
3288 dialog
= sipe_dialog_find(session
, who
);
3290 // Queue the message
3291 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
3293 if (dialog
&& dialog
->callid
) {
3294 sipe_im_process_queue(sip
, session
);
3295 } else if (!dialog
|| !dialog
->outgoing_invite
) {
3296 // Need to send the INVITE to get the outgoing dialog setup
3297 sipe_invite(sip
, session
, who
, what
, NULL
, FALSE
);
3303 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
3304 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3306 struct sipe_account_data
*sip
= gc
->proto_data
;
3307 struct sip_session
*session
;
3309 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
3311 session
= sipe_session_find_chat_by_id(sip
, id
);
3313 // Queue the message
3314 if (session
&& session
->dialogs
) {
3315 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
3317 sipe_im_process_queue(sip
, session
);
3323 /* End IM Session (INVITE and MESSAGE methods) */
3325 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3327 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
3328 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3329 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3330 struct sip_session
*session
;
3332 purple_debug_info("sipe", "process_incoming_info: \n%s\n", msg
->body
? msg
->body
: "");
3334 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3336 session
= sipe_session_find_im(sip
, from
);
3343 if (!strncmp(contenttype
, "application/x-ms-mim", 20)) {
3344 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3345 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
3346 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
3348 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
3350 if (xn_request_rm
) {
3351 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
3352 int bid
= atoi(xmlnode_get_attrib(xn_request_rm
, "bid"));
3353 gchar
*body
= g_strdup_printf(
3354 "<?xml version=\"1.0\"?>\r\n"
3355 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3356 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
3358 session
->bid
< bid
? "true" : "false");
3359 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3361 } else if (xn_set_rm
) {
3363 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
3364 g_free(session
->roster_manager
);
3365 session
->roster_manager
= g_strdup(rm
);
3367 body
= g_strdup_printf(
3368 "<?xml version=\"1.0\"?>\r\n"
3369 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3370 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
3372 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3375 xmlnode_free(xn_action
);
3378 /* looks like purple lacks typing notification for chat */
3379 if (!session
->is_multiparty
&& !session
->focus_uri
) {
3380 xmlnode
*xn_keyboard_activity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3381 const char *status
= xmlnode_get_attrib(xmlnode_get_child(xn_keyboard_activity
, "status"),
3383 if (status
&& !strcmp(status
, "type")) {
3384 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
3385 } else if (status
&& !strcmp(status
, "idle")) {
3386 serv_got_typing_stopped(sip
->gc
, from
);
3388 xmlnode_free(xn_keyboard_activity
);
3391 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3396 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3398 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3399 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3400 struct sip_session
*session
;
3402 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3404 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3406 session
= sipe_session_find_im(sip
, from
);
3413 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
3414 g_free(session
->roster_manager
);
3415 session
->roster_manager
= NULL
;
3418 /* This what BYE is essentially for - terminating dialog */
3419 sipe_dialog_remove(session
, from
);
3420 if (session
->focus_uri
&& !g_strcasecmp(from
, session
->im_mcu_uri
)) {
3421 sipe_conf_immcu_closed(sip
, session
);
3422 } else if (session
->is_multiparty
) {
3423 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
3429 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3431 gchar
*self
= sip_uri_self(sip
);
3432 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3433 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3434 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
3435 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
3436 struct sip_session
*session
;
3437 struct sip_dialog
*dialog
;
3439 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3440 dialog
= sipe_dialog_find(session
, from
);
3442 if (!session
|| !dialog
|| !session
->roster_manager
|| strcmp(session
->roster_manager
, self
)) {
3443 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
3445 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
3447 sipe_invite(sip
, session
, refer_to
, NULL
, referred_by
, FALSE
);
3453 g_free(referred_by
);
3457 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
3459 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
3460 struct sip_session
*session
;
3461 struct sip_dialog
*dialog
;
3463 if (state
== PURPLE_NOT_TYPING
)
3466 session
= sipe_session_find_im(sip
, who
);
3467 dialog
= sipe_dialog_find(session
, who
);
3469 if (session
&& dialog
&& dialog
->is_established
) {
3470 send_sip_request(gc
, "INFO", who
, who
,
3471 "Content-Type: application/xml\r\n",
3472 SIPE_SEND_TYPING
, dialog
, NULL
);
3474 return SIPE_TYPING_SEND_TIMEOUT
;
3477 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
3479 GSList
*tmp
= sip
->transactions
;
3480 time_t currtime
= time(NULL
);
3482 struct transaction
*trans
= tmp
->data
;
3484 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
3485 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
3488 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
3490 sendout_sipmsg(sip
, trans
->msg
);
3497 static void do_reauthenticate_cb(struct sipe_account_data
*sip
,
3498 SIPE_UNUSED_PARAMETER
void *unused
)
3500 /* register again when security token expires */
3501 /* we have to start a new authentication as the security token
3502 * is almost expired by sending a not signed REGISTER message */
3503 purple_debug_info("sipe", "do a full reauthentication\n");
3504 sipe_auth_free(&sip
->registrar
);
3505 sipe_auth_free(&sip
->proxy
);
3506 sip
->registerstatus
= 0;
3508 sip
->reauthenticate_set
= FALSE
;
3511 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3515 gboolean found
= FALSE
;
3517 from
= parse_from(sipmsg_find_header(msg
, "From"));
3521 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
3523 contenttype
= sipmsg_find_header(msg
, "Content-Type");
3524 if (!strncmp(contenttype
, "text/plain", 10)
3525 || !strncmp(contenttype
, "text/html", 9)
3526 || !strncmp(contenttype
, "multipart/related", 21))
3528 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3529 gchar
*html
= get_html_message(contenttype
, msg
->body
);
3531 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
3533 session
= sipe_session_find_im(sip
, from
);
3536 if (session
&& session
->focus_uri
) { /* a conference */
3537 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
3538 gchar
*sender
= parse_from(tmp
);
3540 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
3541 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3543 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
3544 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
3545 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3547 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3550 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3553 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
3554 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3559 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
3563 state
= xmlnode_get_child(isc
, "state");
3566 purple_debug_info("sipe", "process_incoming_message: no state found\n");
3571 statedata
= xmlnode_get_data(state
);
3573 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
3574 else serv_got_typing_stopped(sip
->gc
, from
);
3579 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3583 purple_debug_info("sipe", "got unknown mime-type");
3584 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
3589 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3591 gchar
*ms_text_format
;
3596 gboolean is_multiparty
= FALSE
;
3597 gboolean is_triggered
= FALSE
;
3598 gboolean was_multiparty
= TRUE
;
3599 gboolean just_joined
= FALSE
;
3601 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3602 gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
3603 gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
3604 gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
3605 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
3606 GSList
*end_points
= NULL
;
3607 struct sip_session
*session
;
3609 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? msg
->body
: "");
3611 /* Invitation to join conference */
3612 if (!strncmp(content_type
, "application/ms-conf-invite+xml", 30)) {
3613 process_incoming_invite_conf(sip
, msg
);
3617 /* Only accept text invitations */
3618 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
3619 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3623 // TODO There *must* be a better way to clean up the To header to add a tag...
3624 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
3625 oldHeader
= sipmsg_find_header(msg
, "To");
3627 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
3628 sipmsg_remove_header_now(msg
, "To");
3629 sipmsg_add_header_now(msg
, "To", newHeader
);
3632 if (end_points_hdr
) {
3633 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
3635 if (g_slist_length(end_points
) > 2) {
3636 is_multiparty
= TRUE
;
3639 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
3640 is_triggered
= TRUE
;
3641 is_multiparty
= TRUE
;
3644 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3645 /* Convert to multiparty */
3646 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
3647 g_free(session
->with
);
3648 session
->with
= NULL
;
3649 was_multiparty
= FALSE
;
3650 session
->is_multiparty
= TRUE
;
3651 session
->chat_id
= rand();
3654 if (!session
&& is_multiparty
) {
3655 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
3658 from
= parse_from(sipmsg_find_header(msg
, "From"));
3660 session
= sipe_session_find_or_add_im(sip
, from
);
3663 g_free(session
->callid
);
3664 session
->callid
= g_strdup(callid
);
3666 session
->is_multiparty
= is_multiparty
;
3667 if (roster_manager
) {
3668 session
->roster_manager
= g_strdup(roster_manager
);
3671 if (is_multiparty
&& end_points
) {
3672 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
3673 GSList
*entry
= end_points
;
3675 struct sip_dialog
*dialog
;
3676 struct sipendpoint
*end_point
= entry
->data
;
3677 entry
= entry
->next
;
3679 if (!g_strcasecmp(from
, end_point
->contact
) ||
3680 !g_strcasecmp(to
, end_point
->contact
))
3683 dialog
= sipe_dialog_find(session
, end_point
->contact
);
3685 g_free(dialog
->theirepid
);
3686 dialog
->theirepid
= end_point
->epid
;
3687 end_point
->epid
= NULL
;
3689 dialog
= sipe_dialog_add(session
);
3691 dialog
->callid
= g_strdup(session
->callid
);
3692 dialog
->with
= end_point
->contact
;
3693 end_point
->contact
= NULL
;
3694 dialog
->theirepid
= end_point
->epid
;
3695 end_point
->epid
= NULL
;
3699 /* send triggered INVITE */
3700 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, TRUE
);
3707 GSList
*entry
= end_points
;
3709 struct sipendpoint
*end_point
= entry
->data
;
3710 entry
= entry
->next
;
3711 g_free(end_point
->contact
);
3712 g_free(end_point
->epid
);
3715 g_slist_free(end_points
);
3719 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
3721 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
3723 dialog
= sipe_dialog_add(session
);
3725 dialog
->callid
= g_strdup(session
->callid
);
3726 dialog
->with
= g_strdup(from
);
3727 sipe_dialog_parse(dialog
, msg
, FALSE
);
3729 if (!dialog
->ourtag
) {
3730 dialog
->ourtag
= newTag
;
3737 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
3741 if (is_multiparty
&& !session
->conv
) {
3742 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++sip
->chat_seq
);
3743 gchar
*self
= sip_uri_self(sip
);
3744 /* create prpl chat */
3745 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_name
);
3746 session
->chat_name
= g_strdup(chat_name
);
3747 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
3749 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3751 PURPLE_CBFLAGS_NONE
, FALSE
);
3756 if (is_multiparty
&& !was_multiparty
) {
3757 /* add current IM counterparty to chat */
3758 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3759 sipe_dialog_first(session
)->with
, NULL
,
3760 PURPLE_CBFLAGS_NONE
, FALSE
);
3763 /* add inviting party to chat */
3764 if (just_joined
&& session
->conv
) {
3765 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3767 PURPLE_CBFLAGS_NONE
, TRUE
);
3770 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
3771 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
3772 if (ms_text_format
) {
3773 if (!strncmp(ms_text_format
, "text/plain", 10) || !strncmp(ms_text_format
, "text/html", 9)) {
3775 gchar
*html
= get_html_message(ms_text_format
, NULL
);
3777 if (is_multiparty
) {
3778 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
3779 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
3781 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3784 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message received
3790 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
3791 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3792 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
3794 body
= g_strdup_printf(
3796 "o=- 0 0 IN IP4 %s\r\n"
3800 "m=message %d sip sip:%s\r\n"
3801 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3802 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
3803 sip
->realport
, sip
->username
);
3804 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3808 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3812 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
3813 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3814 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
3816 body
= g_strdup_printf(
3818 "o=- 0 0 IN IP4 0.0.0.0\r\n"
3820 "c=IN IP4 0.0.0.0\r\n"
3822 "m=message %d sip sip:%s\r\n"
3823 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3824 sip
->realport
, sip
->username
);
3825 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3829 static void sipe_connection_cleanup(struct sipe_account_data
*);
3830 static void create_connection(struct sipe_account_data
*, gchar
*, int);
3832 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3835 const gchar
*expires_header
;
3837 GSList
*hdr
= msg
->headers
;
3838 struct siphdrelement
*elem
;
3840 expires_header
= sipmsg_find_header(msg
, "Expires");
3841 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
3842 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
3844 switch (msg
->response
) {
3847 sip
->registerstatus
= 0;
3849 gchar
*contact_hdr
= NULL
;
3855 if (!sip
->reregister_set
) {
3856 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
3857 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
3858 g_free(action_name
);
3859 sip
->reregister_set
= TRUE
;
3862 sip
->registerstatus
= 3;
3865 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3867 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3870 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3874 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3875 fill_auth(tmp
, &sip
->registrar
);
3878 if (!sip
->reauthenticate_set
) {
3879 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
3880 guint reauth_timeout
;
3881 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
3882 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
3883 reauth_timeout
= sip
->registrar
.expires
- 300;
3885 /* NTLM: we have to reauthenticate as our security token expires
3886 after eight hours (be five minutes early) */
3887 reauth_timeout
= (8 * 3600) - 300;
3889 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
3890 g_free(action_name
);
3891 sip
->reauthenticate_set
= TRUE
;
3894 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
3896 epid
= get_epid(sip
);
3897 uuid
= generateUUIDfromEPID(epid
);
3900 // There can be multiple Contact headers (one per location where the user is logged in) so
3901 // make sure to only get the one for this uuid
3902 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
3903 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
3904 if (valid_contact
) {
3905 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
3906 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
3907 g_free(valid_contact
);
3910 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
3915 g_free(sip
->contact
);
3917 sip
->contact
= g_strdup_printf("<%s>", gruu
);
3920 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
3921 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
);
3923 sip
->msrtc_event_categories
= FALSE
;
3924 sip
->batched_support
= FALSE
;
3929 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
3930 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
3931 sip
->msrtc_event_categories
= TRUE
;
3932 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->msrtc_event_categories
);
3934 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
3935 sip
->batched_support
= TRUE
;
3936 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s: %d\n", elem
->value
,sip
->batched_support
);
3939 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
3940 gchar
**caps
= g_strsplit(elem
->value
,",",0);
3943 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
3944 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
3949 hdr
= g_slist_next(hdr
);
3953 if (!sip
->subscribed
) { //do it just once, not every re-register
3955 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
3956 (GCompareFunc
)g_ascii_strcasecmp
)) {
3957 sipe_subscribe_roaming_contacts(sip
);
3960 /* For 2007+ it does not make sence to subscribe to:
3961 * vnd-microsoft-roaming-ACL
3962 * vnd-microsoft-provisioning (not v2)
3964 * These are for backward compatibility.
3966 if (sip
->msrtc_event_categories
)
3968 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
3969 (GCompareFunc
)g_ascii_strcasecmp
)) {
3970 sipe_subscribe_roaming_self(sip
);
3972 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
3973 (GCompareFunc
)g_ascii_strcasecmp
)) {
3974 sipe_subscribe_roaming_provisioning_v2(sip
);
3977 /* For 2005- servers */
3980 //sipe_options_request(sip, sip->sipdomain);
3982 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
3983 (GCompareFunc
)g_ascii_strcasecmp
)) {
3984 sipe_subscribe_roaming_acl(sip
);
3986 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
3987 (GCompareFunc
)g_ascii_strcasecmp
)) {
3988 sipe_subscribe_roaming_provisioning(sip
);
3990 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
3991 (GCompareFunc
)g_ascii_strcasecmp
)) {
3992 sipe_subscribe_presence_wpending(sip
, msg
);
3995 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
3996 sip
->subscribed
= TRUE
;
3999 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
4000 "timeout=", ";", NULL
);
4001 if (timeout
!= NULL
) {
4002 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
4003 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
4004 sip
->keepalive_timeout
);
4008 // Should we remove the transaction here?
4009 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
4010 transactions_remove(sip
, tc
);
4015 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
4017 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
4018 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
4022 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
4025 tmp
= g_strsplit(parts
[0], ":", 0);
4026 hostname
= g_strdup(tmp
[0]);
4027 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
4031 tmp
= g_strsplit(parts
[i
], "=", 0);
4033 if (g_strcasecmp("transport", tmp
[0]) == 0) {
4034 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
4035 transport
= SIPE_TRANSPORT_TCP
;
4036 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
4037 transport
= SIPE_TRANSPORT_UDP
;
4046 /* Close old connection */
4047 sipe_connection_cleanup(sip
);
4049 /* Create new connection */
4050 sip
->transport
= transport
;
4051 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
4052 hostname
, port
, TRANSPORT_DESCRIPTOR
);
4053 create_connection(sip
, hostname
, port
);
4059 if (sip
->registerstatus
!= 2) {
4060 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
4061 if (sip
->registrar
.retries
> 3) {
4062 sip
->gc
->wants_to_die
= TRUE
;
4063 purple_connection_error(sip
->gc
, _("Wrong Password"));
4067 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4069 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
4072 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4075 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
4076 fill_auth(tmp
, &sip
->registrar
);
4077 sip
->registerstatus
= 2;
4078 if (sip
->account
->disconnecting
) {
4079 do_register_exp(sip
, 0);
4087 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
4088 if (warning
!= NULL
) {
4090 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
4092 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
4093 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
4096 warning
= g_strdup(_("You have been rejected by the server"));
4099 sip
->gc
->wants_to_die
= TRUE
;
4100 purple_connection_error(sip
->gc
, warning
);
4107 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
4108 if (warning
!= NULL
) {
4109 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
4110 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
4113 warning
= g_strdup(_("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator"));
4116 sip
->gc
->wants_to_die
= TRUE
;
4117 purple_connection_error(sip
->gc
, warning
);
4124 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
4125 if (warning
!= NULL
) {
4126 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
4127 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
4130 warning
= g_strdup(_("Service unavailable: no reason given"));
4133 sip
->gc
->wants_to_die
= TRUE
;
4134 purple_connection_error(sip
->gc
, warning
);
4143 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4146 xmlnode
*xn_categories
;
4147 xmlnode
*xn_category
;
4149 const char *activity
= NULL
;
4151 xn_categories
= xmlnode_from_str(data
, len
);
4152 uri
= xmlnode_get_attrib(xn_categories
, "uri");
4154 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
4156 xn_category
= xmlnode_get_next_twin(xn_category
) )
4158 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
4160 if (!strcmp(attrVar
, "note"))
4163 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4168 xn_node
= xmlnode_get_child(xn_category
, "note");
4169 if (!xn_node
) continue;
4170 xn_node
= xmlnode_get_child(xn_node
, "body");
4171 if (!xn_node
) continue;
4172 note
= xmlnode_get_data(xn_node
);
4173 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s),note(%s)\n",uri
,note
? note
: "");
4174 g_free(sbuddy
->annotation
);
4175 sbuddy
->annotation
= NULL
;
4176 if (note
) sbuddy
->annotation
= g_strdup(note
);
4182 else if(!strcmp(attrVar
, "state"))
4186 xn_node
= xmlnode_get_child(xn_category
, "state");
4187 if (!xn_node
) continue;
4188 xn_node
= xmlnode_get_child(xn_node
, "availability");
4189 if (!xn_node
) continue;
4191 data
= xmlnode_get_data(xn_node
);
4196 activity
= SIPE_STATUS_ID_UNKNOWN
;
4197 else if (avail
< 4500)
4198 activity
= SIPE_STATUS_ID_AVAILABLE
;
4199 else if (avail
< 6000)
4200 activity
= SIPE_STATUS_ID_BRB
;
4201 else if (avail
< 7500)
4202 activity
= SIPE_STATUS_ID_ONPHONE
;
4203 else if (avail
< 9000)
4204 activity
= SIPE_STATUS_ID_BUSY
;
4205 else if (avail
< 12000)
4206 activity
= SIPE_STATUS_ID_DND
;
4207 else if (avail
< 18000)
4208 activity
= SIPE_STATUS_ID_AWAY
;
4210 activity
= SIPE_STATUS_ID_OFFLINE
;
4214 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", activity
);
4215 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
4218 xmlnode_free(xn_categories
);
4221 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
4223 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4224 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
4225 payload
->host
= g_strdup(host
);
4226 payload
->buddies
= server
;
4227 sipe_subscribe_presence_batched_routed(sip
, payload
);
4228 sipe_subscribe_presence_batched_routed_free(payload
);
4231 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4234 xmlnode
*xn_resource
;
4235 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
4240 xn_list
= xmlnode_from_str(data
, len
);
4242 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
4244 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
4246 const char *uri
, *state
;
4247 xmlnode
*xn_instance
;
4249 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
4250 if (!xn_instance
) continue;
4252 uri
= xmlnode_get_attrib(xn_resource
, "uri");
4253 state
= xmlnode_get_attrib(xn_instance
, "state");
4254 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
4256 if (strstr(state
, "resubscribe")) {
4257 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
4258 struct sipe_buddy
*sbuddy
;
4259 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
4260 gchar
*user
= g_strdup(uri
);
4261 host
= g_strdup(poolFqdn
);
4262 server
= g_hash_table_lookup(servers
, host
);
4263 server
= g_slist_append(server
, user
);
4264 g_hash_table_insert(servers
, host
, server
);
4266 sipe_subscribe_presence_single(sip
, (void *) uri
);
4268 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4270 sbuddy
->resubscribed
= TRUE
;
4275 /* Send out any deferred poolFqdn subscriptions */
4276 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
4277 g_hash_table_destroy(servers
);
4279 xmlnode_free(xn_list
);
4282 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4286 gchar
*activity
= NULL
;
4288 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
4289 gboolean isonline
= FALSE
;
4290 xmlnode
*display_name_node
;
4292 pidf
= xmlnode_from_str(data
, len
);
4294 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data
);
4298 uri
= xmlnode_get_attrib(pidf
, "entity");
4300 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
4302 if ((status
= xmlnode_get_child(tuple
, "status"))) {
4303 basicstatus
= xmlnode_get_child(status
, "basic");
4308 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
4313 getbasic
= xmlnode_get_data(basicstatus
);
4315 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
4320 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
4321 if (strstr(getbasic
, "open")) {
4326 display_name_node
= xmlnode_get_child(pidf
, "display-name");
4327 // updating display name if alias was just URI
4328 if (display_name_node
) {
4329 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
4330 GSList
*entry
= buddies
;
4331 PurpleBuddy
*p_buddy
;
4332 char * display_name
= xmlnode_get_data(display_name_node
);
4335 const char *server_alias
;
4338 p_buddy
= entry
->data
;
4340 alias
= (char *)purple_buddy_get_alias(p_buddy
);
4341 alias
= alias
? sip_uri_from_name(alias
) : NULL
;
4342 if (!alias
|| !g_ascii_strcasecmp(uri
, alias
)) {
4343 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
4344 purple_blist_alias_buddy(p_buddy
, display_name
);
4348 server_alias
= purple_buddy_get_server_alias(p_buddy
);
4350 ( (server_alias
&& strcmp(display_name
, server_alias
))
4351 || !server_alias
|| strlen(server_alias
) == 0 )
4353 purple_blist_server_alias_buddy(p_buddy
, display_name
);
4356 entry
= entry
->next
;
4358 g_free(display_name
);
4361 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
4362 if ((status
= xmlnode_get_child(tuple
, "status"))) {
4363 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
4364 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
4365 activity
= xmlnode_get_data(basicstatus
);
4366 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
4373 const gchar
* status_id
= NULL
;
4375 if (strstr(activity
, "busy")) {
4376 status_id
= SIPE_STATUS_ID_BUSY
;
4377 } else if (strstr(activity
, "away")) {
4378 status_id
= SIPE_STATUS_ID_AWAY
;
4383 status_id
= SIPE_STATUS_ID_AVAILABLE
;
4386 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
4387 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
4389 purple_prpl_got_user_status(sip
->account
, uri
, SIPE_STATUS_ID_OFFLINE
, NULL
);
4396 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4398 const char *availability
;
4399 const char *activity
;
4400 const char *display_name
= NULL
;
4401 const char *activity_name
= NULL
;
4406 struct sipe_buddy
*sbuddy
;
4408 xmlnode
*xn_presentity
= xmlnode_from_str(data
, len
);
4410 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
4411 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
4412 xmlnode
*xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
4413 xmlnode
*xn_email
= xmlnode_get_child(xn_presentity
, "email");
4414 const char *email
= xn_email
? xmlnode_get_attrib(xn_email
, "email") : NULL
;
4415 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
4416 xmlnode
*xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
):NULL
;
4417 const char *avail
= xn_state
? xmlnode_get_attrib(xn_state
, "avail") : NULL
;
4419 xmlnode
*xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
4420 char *note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
4421 xmlnode
*xn_devices
= xmlnode_get_child(xn_presentity
, "devices");
4422 xmlnode
*xn_device_presence
= xn_devices
? xmlnode_get_child(xn_devices
, "devicePresence") : NULL
;
4423 xmlnode
*xn_device_name
= xn_device_presence
? xmlnode_get_child(xn_device_presence
, "deviceName") : NULL
;
4424 const char *device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
4426 name
= xmlnode_get_attrib(xn_presentity
, "uri");
4427 uri
= sip_uri_from_name(name
);
4428 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
4429 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
4431 // updating display name if alias was just URI
4432 if (xn_display_name
) {
4433 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
4434 GSList
*entry
= buddies
;
4435 PurpleBuddy
*p_buddy
;
4436 display_name
= xmlnode_get_attrib(xn_display_name
, "displayName");
4439 const char *email_str
, *server_alias
;
4441 p_buddy
= entry
->data
;
4443 if (!g_ascii_strcasecmp(name
, purple_buddy_get_alias(p_buddy
))) {
4444 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
4445 purple_blist_alias_buddy(p_buddy
, display_name
);
4448 server_alias
= purple_buddy_get_server_alias(p_buddy
);
4450 ( (server_alias
&& strcmp(display_name
, server_alias
))
4451 || !server_alias
|| strlen(server_alias
) == 0 )
4453 purple_blist_server_alias_buddy(p_buddy
, display_name
);
4457 email_str
= purple_blist_node_get_string((PurpleBlistNode
*)p_buddy
, "email");
4458 if (!email_str
|| g_ascii_strcasecmp(email_str
, email
)) {
4459 purple_blist_node_set_string((PurpleBlistNode
*)p_buddy
, "email", email
);
4463 entry
= entry
->next
;
4467 avl
= atoi(availability
);
4468 act
= atoi(activity
);
4470 if(sip
->msrtc_event_categories
){
4471 if (act
== 100 && avl
== 0)
4472 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4473 else if (act
== 100 && avl
== 300)
4474 activity_name
= SIPE_STATUS_ID_AWAY
;
4475 else if (act
== 300 && avl
== 300)
4476 activity_name
= SIPE_STATUS_ID_BRB
;
4477 else if (act
== 400 && avl
== 300)
4478 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4479 else if (act
== 500 && act
== 300)
4480 activity_name
= SIPE_STATUS_ID_ONPHONE
;
4481 else if (act
== 600 && avl
== 300)
4482 activity_name
= SIPE_STATUS_ID_BUSY
;
4483 else if (act
== 0 && avl
== 0){ //MSRTC elements are zero
4484 if(avail
){ //Check for LegacyInterop elements
4487 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4488 else if (avl
== 3500)
4489 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4490 else if (avl
== 15500)
4491 activity_name
= SIPE_STATUS_ID_AWAY
;
4492 else if (avl
== 6500)
4493 activity_name
= SIPE_STATUS_ID_BUSY
;
4494 else if (avl
== 12500)
4495 activity_name
= SIPE_STATUS_ID_BRB
;
4500 if(activity_name
== NULL
){
4502 activity_name
= SIPE_STATUS_ID_AWAY
;
4503 else if (act
<= 150)
4504 activity_name
= SIPE_STATUS_ID_LUNCH
;
4505 else if (act
<= 300)
4506 activity_name
= SIPE_STATUS_ID_BRB
;
4507 else if (act
<= 400)
4508 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4509 else if (act
<= 500)
4510 activity_name
= SIPE_STATUS_ID_ONPHONE
;
4511 else if (act
<= 600)
4512 activity_name
= SIPE_STATUS_ID_BUSY
;
4514 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
4517 activity_name
= SIPE_STATUS_ID_OFFLINE
;
4520 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4523 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
4524 sbuddy
->annotation
= NULL
;
4525 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
4527 if (sbuddy
->device_name
) { g_free(sbuddy
->device_name
); }
4528 sbuddy
->device_name
= NULL
;
4529 if (device_name
) { sbuddy
->device_name
= g_strdup(device_name
); }
4532 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
4533 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
4535 xmlnode_free(xn_presentity
);
4539 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4541 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4543 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
4545 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
4546 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
4548 const char *content
= msg
->body
;
4549 unsigned length
= msg
->bodylen
;
4550 PurpleMimeDocument
*mime
= NULL
;
4552 if (strstr(ctype
, "multipart"))
4554 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
4555 const char *content_type
;
4557 mime
= purple_mime_document_parse(doc
);
4558 parts
= purple_mime_document_get_parts(mime
);
4560 content
= purple_mime_part_get_data(parts
->data
);
4561 length
= purple_mime_part_get_length(parts
->data
);
4562 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
4563 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
4565 process_incoming_notify_rlmi_resub(sip
, content
, length
);
4567 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
4569 process_incoming_notify_msrtc(sip
, content
, length
);
4573 process_incoming_notify_rlmi(sip
, content
, length
);
4575 parts
= parts
->next
;
4581 purple_mime_document_free(mime
);
4584 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
4586 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
4588 else if(strstr(ctype
, "application/rlmi+xml"))
4590 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
4593 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
4595 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
4599 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
4603 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
4605 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4606 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
4608 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
4611 strstr(ctype
, "multipart") &&
4612 (strstr(ctype
, "application/rlmi+xml") ||
4613 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
4614 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
4615 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
4616 GList
*parts
= purple_mime_document_get_parts(mime
);
4617 GSList
*buddies
= NULL
;
4618 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4621 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
4622 purple_mime_part_get_length(parts
->data
));
4623 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
4625 buddies
= g_slist_append(buddies
, uri
);
4628 parts
= parts
->next
;
4631 if (mime
) purple_mime_document_free(mime
);
4633 payload
->host
= who
;
4634 payload
->buddies
= buddies
;
4635 sipe_schedule_action(action_name
, timeout
,
4636 sipe_subscribe_presence_batched_routed
,
4637 sipe_subscribe_presence_batched_routed_free
,
4639 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
4642 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, NULL
, sip
, who
);
4643 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
4645 g_free(action_name
);
4649 * Dispatcher for all incoming subscription information
4650 * whether it comes from NOTIFY, BENOTIFY requests or
4651 * piggy-backed to subscription's OK responce.
4653 * @param request whether initiated from BE/NOTIFY request or OK-response message.
4654 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
4656 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
4658 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4659 gchar
*event
= sipmsg_find_header(msg
, "Event");
4660 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
4663 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event
? event
: "", msg
->body
);
4664 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n\n", subscription_state
? subscription_state
: "");
4666 /* implicit subscriptions */
4667 if (content_type
&& purple_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
4668 sipe_process_imdn(sip
, msg
);
4673 const gchar
*expires_header
;
4674 expires_header
= sipmsg_find_header(msg
, "Expires");
4675 timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
4676 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n\n", timeout
);
4677 timeout
= (timeout
- 60) > 60 ? (timeout
- 60) : timeout
; // 1 min ahead of expiration
4680 /* for one off subscriptions (send with Expire: 0) */
4681 if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning-v2"))
4683 sipe_process_provisioning_v2(sip
, msg
);
4686 if (!subscription_state
|| strstr(subscription_state
, "active"))
4688 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
4690 sipe_process_presence(sip
, msg
);
4692 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
4694 sipe_process_roaming_contacts(sip
, msg
);
4696 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self"))
4698 sipe_process_roaming_self(sip
, msg
);
4700 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
4702 sipe_process_roaming_acl(sip
, msg
);
4704 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
4706 sipe_process_presence_wpending(sip
, msg
);
4708 else if (event
&& !g_ascii_strcasecmp(event
, "conference"))
4710 sipe_process_conference(sip
, msg
);
4714 //The server sends a (BE)NOTIFY with the status 'terminated'
4715 if (request
&& subscription_state
&& strstr(subscription_state
, "terminated") ) {
4716 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4717 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from
);
4721 if (timeout
&& event
) {// For LSC 2005 and OCS 2007
4722 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
4723 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
4725 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
4726 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_contacts, NULL, sip, msg);
4727 g_free(action_name);
4729 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
4730 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
4732 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
4733 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_acl, NULL, sip, msg);
4734 g_free(action_name);
4737 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
4738 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
4740 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
4741 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
4742 g_free(action_name
);
4744 else if (!g_ascii_strcasecmp(event
, "presence") &&
4745 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
4747 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
4748 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
4749 if(sip
->batched_support
) {
4750 gchar
*my_self
= sip_uri_self(sip
);
4751 if(!g_ascii_strcasecmp(who
, my_self
)){
4752 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_batched
, NULL
, sip
, NULL
);
4753 purple_debug_info("sipe", "Resubscription full batched list in %d\n",timeout
);
4754 g_free(who
); /* unused */
4757 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
4762 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, who
);
4763 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
,timeout
);
4765 g_free(action_name
);
4766 /* "who" will be freed by the action we just scheduled */
4770 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
4772 sipe_process_registration_notify(sip
, msg
);
4775 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
4776 if (request
&& !benotify
)
4778 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4785 static gchar* gen_xpidf(struct sipe_account_data *sip)
4787 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4789 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
4790 "<display name=\"sip:%s\"/>\r\n"
4791 "<atom id=\"1234\">\r\n"
4792 "<address uri=\"sip:%s\">\r\n"
4793 "<status status=\"%s\"/>\r\n"
4806 static gchar* gen_pidf(struct sipe_account_data *sip)
4808 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
4809 "<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"
4810 "<tuple id=\"0\">\r\n"
4812 "<basic>open</basic>\r\n"
4813 "<ep:activities>\r\n"
4814 " <ep:activity>%s</ep:activity>\r\n"
4818 "<ci:display-name>%s</ci:display-name>\r\n"
4827 static void send_presence_soap(struct sipe_account_data
*sip
, const char * note
)
4829 int availability
= 300; // online
4830 int activity
= 400; // Available
4833 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
)) {
4835 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4837 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4839 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4841 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4843 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4845 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_INVISIBLE
) ||
4846 !strcmp(sip
->status
, SIPE_STATUS_ID_OFFLINE
)) {
4847 availability
= 0; // offline
4850 activity
= 400; // available
4853 name
= g_strdup_printf("sip: sip:%s", sip
->username
);
4854 //@TODO: send user data - state; add hostname in upper case
4855 body
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
4856 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
4862 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4863 SIPE_UNUSED_PARAMETER
struct transaction
*tc
)
4865 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4866 if (msg
->response
== 200) {
4867 sip
->status_version
= 0;
4868 send_presence_status(sip
);
4874 process_send_presence_category_publish_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4875 SIPE_UNUSED_PARAMETER
struct transaction
*tc
)
4877 if (msg
->response
== 409) {
4878 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
4879 // TODO need to parse the version #'s?
4880 gchar
*uri
= sip_uri_self(sip
);
4881 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
4885 purple_debug_info("sipe", "process_send_presence_category_publish_response = %s\n", msg
->body
);
4887 tmp
= get_contact(sip
);
4888 hdr
= g_strdup_printf("Contact: %s\r\n"
4889 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4891 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
4901 static void send_presence_category_publish(struct sipe_account_data
*sip
, const char * note
)
4908 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
) ||
4909 !strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
4911 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_DND
)) {
4913 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
4915 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
4917 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4919 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4921 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_UNKNOWN
)) {
4924 // Offline or invisible
4928 uri
= sip_uri_self(sip
);
4929 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
4930 sip
->status_version
, code
,
4931 sip
->status_version
, code
,
4932 sip
->status_version
, note
? note
: "",
4933 sip
->status_version
, note
? note
: "",
4934 sip
->status_version
, note
? note
: ""
4936 sip
->status_version
++;
4938 tmp
= get_contact(sip
);
4939 hdr
= g_strdup_printf("Contact: %s\r\n"
4940 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4942 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
4950 static void send_presence_status(struct sipe_account_data
*sip
)
4952 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
4954 if (!status
) return;
4956 note
= purple_status_get_attr_string(status
, "message");
4958 if(sip
->msrtc_event_categories
){
4959 send_presence_category_publish(sip
, note
);
4961 send_presence_soap(sip
, note
);
4965 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
4967 gboolean found
= FALSE
;
4968 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
4969 if (msg
->response
== 0) { /* request */
4970 if (!strcmp(msg
->method
, "MESSAGE")) {
4971 process_incoming_message(sip
, msg
);
4973 } else if (!strcmp(msg
->method
, "NOTIFY")) {
4974 purple_debug_info("sipe","send->process_incoming_notify\n");
4975 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
4977 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
4978 purple_debug_info("sipe","send->process_incoming_benotify\n");
4979 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
4981 } else if (!strcmp(msg
->method
, "INVITE")) {
4982 process_incoming_invite(sip
, msg
);
4984 } else if (!strcmp(msg
->method
, "REFER")) {
4985 process_incoming_refer(sip
, msg
);
4987 } else if (!strcmp(msg
->method
, "OPTIONS")) {
4988 process_incoming_options(sip
, msg
);
4990 } else if (!strcmp(msg
->method
, "INFO")) {
4991 process_incoming_info(sip
, msg
);
4993 } else if (!strcmp(msg
->method
, "ACK")) {
4994 // ACK's don't need any response
4996 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
4997 // LCS 2005 sends us these - just respond 200 OK
4999 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5000 } else if (!strcmp(msg
->method
, "BYE")) {
5001 process_incoming_bye(sip
, msg
);
5004 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
5006 } else { /* response */
5007 struct transaction
*trans
= transactions_find(sip
, msg
);
5009 if (msg
->response
== 407) {
5010 gchar
*resend
, *auth
, *ptmp
;
5012 if (sip
->proxy
.retries
> 30) return;
5013 sip
->proxy
.retries
++;
5014 /* do proxy authentication */
5016 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
5018 fill_auth(ptmp
, &sip
->proxy
);
5019 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
5020 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
5021 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
5023 resend
= sipmsg_to_string(trans
->msg
);
5024 /* resend request */
5025 sendout_pkt(sip
->gc
, resend
);
5028 if (msg
->response
== 100 || msg
->response
== 180) {
5029 /* ignore provisional response */
5030 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
5032 sip
->proxy
.retries
= 0;
5033 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
5034 if (msg
->response
== 401)
5036 sip
->registrar
.retries
++;
5040 sip
->registrar
.retries
= 0;
5042 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
5044 if (msg
->response
== 401) {
5045 gchar
*resend
, *auth
, *ptmp
;
5047 if (sip
->registrar
.retries
> 4) return;
5048 sip
->registrar
.retries
++;
5051 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5053 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
5056 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
5060 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
5062 fill_auth(ptmp
, &sip
->registrar
);
5063 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
5064 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
5065 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
5067 //sipmsg_remove_header_now(trans->msg, "Authorization");
5068 //sipmsg_add_header(trans->msg, "Authorization", auth);
5070 resend
= sipmsg_to_string(trans
->msg
);
5071 /* resend request */
5072 sendout_pkt(sip
->gc
, resend
);
5077 if (trans
->callback
) {
5078 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
5079 /* call the callback to process response*/
5080 (trans
->callback
)(sip
, msg
, trans
);
5082 /* Not sure if this is needed or what needs to be done
5083 but transactions seem to be removed prematurely so
5084 this only removes them if the response is 200 OK */
5085 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
5086 /*Has a bug and it's unneccesary*/
5087 /*transactions_remove(sip, trans);*/
5093 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
5097 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
5101 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
5109 /* according to the RFC remove CRLF at the beginning */
5110 while (*cur
== '\r' || *cur
== '\n') {
5113 if (cur
!= conn
->inbuf
) {
5114 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
5115 conn
->inbufused
= strlen(conn
->inbuf
);
5118 /* Received a full Header? */
5119 sip
->processing_input
= TRUE
;
5120 while (sip
->processing_input
&&
5121 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
5122 time_t currtime
= time(NULL
);
5125 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
5126 msg
= sipmsg_parse_header(conn
->inbuf
);
5129 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
5130 if (restlen
>= msg
->bodylen
) {
5131 dummy
= g_malloc(msg
->bodylen
+ 1);
5132 memcpy(dummy
, cur
, msg
->bodylen
);
5133 dummy
[msg
->bodylen
] = '\0';
5135 cur
+= msg
->bodylen
;
5136 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
5137 conn
->inbufused
= strlen(conn
->inbuf
);
5139 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
5140 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
5146 purple_debug_info("sipe", "body:\n%s", msg->body);
5149 // Verify the signature before processing it
5150 if (sip
->registrar
.gssapi_context
) {
5151 struct sipmsg_breakdown msgbd
;
5152 gchar
*signature_input_str
;
5155 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
5156 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
5158 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
5160 if (rspauth
!= NULL
) {
5161 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
5162 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
5163 process_input_message(sip
, msg
);
5165 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
5166 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
5167 sip
->gc
->wants_to_die
= TRUE
;
5169 } else if (msg
->response
== 401) {
5170 purple_connection_error(sip
->gc
, _("Wrong Password"));
5171 sip
->gc
->wants_to_die
= TRUE
;
5173 g_free(signature_input_str
);
5176 sipmsg_breakdown_free(&msgbd
);
5178 process_input_message(sip
, msg
);
5185 static void sipe_udp_process(gpointer data
, gint source
,
5186 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
5188 PurpleConnection
*gc
= data
;
5189 struct sipe_account_data
*sip
= gc
->proto_data
;
5194 static char buffer
[65536];
5195 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
5197 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
5198 msg
= sipmsg_parse_msg(buffer
);
5199 if (msg
) process_input_message(sip
, msg
);
5203 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
5205 struct sipe_account_data
*sip
= gc
->proto_data
;
5206 PurpleSslConnection
*gsc
= sip
->gsc
;
5208 purple_debug_error("sipe", "%s",debug
);
5209 purple_connection_error(gc
, msg
);
5211 /* Invalidate this connection. Next send will open a new one */
5213 connection_remove(sip
, gsc
->fd
);
5214 purple_ssl_close(gsc
);
5220 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
5221 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
5223 PurpleConnection
*gc
= data
;
5224 struct sipe_account_data
*sip
;
5225 struct sip_connection
*conn
;
5227 gboolean firstread
= TRUE
;
5229 /* NOTE: This check *IS* necessary */
5230 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
5231 purple_ssl_close(gsc
);
5235 sip
= gc
->proto_data
;
5236 conn
= connection_find(sip
, gsc
->fd
);
5238 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
5239 gc
->wants_to_die
= TRUE
;
5240 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
5244 /* Read all available data from the SSL connection */
5246 /* Increase input buffer size as needed */
5247 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
5248 conn
->inbuflen
+= SIMPLE_BUF_INC
;
5249 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
5250 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
5253 /* Try to read as much as there is space left in the buffer */
5254 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
5255 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
5257 if (len
< 0 && errno
== EAGAIN
) {
5258 /* Try again later */
5260 } else if (len
< 0) {
5261 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
5263 } else if (firstread
&& (len
== 0)) {
5264 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
5268 conn
->inbufused
+= len
;
5271 /* Equivalence indicates that there is possibly more data to read */
5272 } while (len
== readlen
);
5274 conn
->inbuf
[conn
->inbufused
] = '\0';
5275 process_input(sip
, conn
);
5279 static void sipe_input_cb(gpointer data
, gint source
,
5280 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
5282 PurpleConnection
*gc
= data
;
5283 struct sipe_account_data
*sip
= gc
->proto_data
;
5285 struct sip_connection
*conn
= connection_find(sip
, source
);
5287 purple_debug_error("sipe", "Connection not found!\n");
5291 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
5292 conn
->inbuflen
+= SIMPLE_BUF_INC
;
5293 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
5296 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
5298 if (len
< 0 && errno
== EAGAIN
)
5300 else if (len
<= 0) {
5301 purple_debug_info("sipe", "sipe_input_cb: read error\n");
5302 connection_remove(sip
, source
);
5303 if (sip
->fd
== source
) sip
->fd
= -1;
5307 conn
->inbufused
+= len
;
5308 conn
->inbuf
[conn
->inbufused
] = '\0';
5310 process_input(sip
, conn
);
5313 /* Callback for new connections on incoming TCP port */
5314 static void sipe_newconn_cb(gpointer data
, gint source
,
5315 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
5317 PurpleConnection
*gc
= data
;
5318 struct sipe_account_data
*sip
= gc
->proto_data
;
5319 struct sip_connection
*conn
;
5321 int newfd
= accept(source
, NULL
, NULL
);
5323 conn
= connection_create(sip
, newfd
);
5325 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
5328 static void login_cb(gpointer data
, gint source
,
5329 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
5331 PurpleConnection
*gc
= data
;
5332 struct sipe_account_data
*sip
;
5333 struct sip_connection
*conn
;
5335 if (!PURPLE_CONNECTION_IS_VALID(gc
))
5343 purple_connection_error(gc
, _("Could not connect"));
5347 sip
= gc
->proto_data
;
5349 sip
->last_keepalive
= time(NULL
);
5351 conn
= connection_create(sip
, source
);
5355 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
5358 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
5359 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
5361 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
5362 if (sip
== NULL
) return;
5367 static guint
sipe_ht_hash_nick(const char *nick
)
5369 char *lc
= g_utf8_strdown(nick
, -1);
5370 guint bucket
= g_str_hash(lc
);
5376 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
5378 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
5381 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
5383 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5385 sip
->listen_data
= NULL
;
5387 if (listenfd
== -1) {
5388 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5394 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
5395 sip
->listenfd
= sip
->fd
;
5397 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
5399 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
5403 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
5404 SIPE_UNUSED_PARAMETER
const char *error_message
)
5406 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5408 sip
->query_data
= NULL
;
5410 if (!hosts
|| !hosts
->data
) {
5411 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
5415 hosts
= g_slist_remove(hosts
, hosts
->data
);
5416 g_free(sip
->serveraddr
);
5417 sip
->serveraddr
= hosts
->data
;
5418 hosts
= g_slist_remove(hosts
, hosts
->data
);
5420 hosts
= g_slist_remove(hosts
, hosts
->data
);
5421 g_free(hosts
->data
);
5422 hosts
= g_slist_remove(hosts
, hosts
->data
);
5425 /* create socket for incoming connections */
5426 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
5427 sipe_udp_host_resolved_listen_cb
, sip
);
5428 if (sip
->listen_data
== NULL
) {
5429 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5434 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
5435 PurpleSslErrorType error
,
5438 PurpleConnection
*gc
= data
;
5439 struct sipe_account_data
*sip
;
5441 /* If the connection is already disconnected, we don't need to do anything else */
5442 if (!PURPLE_CONNECTION_IS_VALID(gc
))
5445 sip
= gc
->proto_data
;
5450 case PURPLE_SSL_CONNECT_FAILED
:
5451 purple_connection_error(gc
, _("Connection Failed"));
5453 case PURPLE_SSL_HANDSHAKE_FAILED
:
5454 purple_connection_error(gc
, _("SSL Handshake Failed"));
5456 case PURPLE_SSL_CERTIFICATE_INVALID
:
5457 purple_connection_error(gc
, _("SSL Certificate Invalid"));
5463 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
5465 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
5466 PurpleProxyConnectData
*connect_data
;
5468 sip
->listen_data
= NULL
;
5470 sip
->listenfd
= listenfd
;
5471 if (sip
->listenfd
== -1) {
5472 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
5476 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
5477 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
5478 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
5479 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
5480 sipe_newconn_cb
, sip
->gc
);
5481 purple_debug_info("sipe", "connecting to %s port %d\n",
5482 sip
->realhostname
, sip
->realport
);
5483 /* open tcp connection to the server */
5484 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
5485 sip
->realport
, login_cb
, sip
->gc
);
5487 if (connect_data
== NULL
) {
5488 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
5492 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
5494 PurpleAccount
*account
= sip
->account
;
5495 PurpleConnection
*gc
= sip
->gc
;
5498 port
= (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
5501 sip
->realhostname
= hostname
;
5502 sip
->realport
= port
;
5504 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
5507 /* TODO: is there a good default grow size? */
5508 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
5509 sip
->txbuf
= purple_circ_buffer_new(0);
5511 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
5513 if (!purple_ssl_is_supported()) {
5514 gc
->wants_to_die
= TRUE
;
5515 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
5519 purple_debug_info("sipe", "using SSL\n");
5521 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
5522 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
5523 if (sip
->gsc
== NULL
) {
5524 purple_connection_error(gc
, _("Could not create SSL context"));
5527 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
5529 purple_debug_info("sipe", "using UDP\n");
5531 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
5532 if (sip
->query_data
== NULL
) {
5533 purple_connection_error(gc
, _("Could not resolve hostname"));
5537 purple_debug_info("sipe", "using TCP\n");
5538 /* create socket for incoming connections */
5539 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
5540 sipe_tcp_connect_listen_cb
, sip
);
5541 if (sip
->listen_data
== NULL
) {
5542 purple_connection_error(gc
, _("Could not create listen socket"));
5548 /* Service list for autodection */
5549 static const struct sipe_service_data service_autodetect
[] = {
5550 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
5551 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
5552 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
5553 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
5557 /* Service list for SSL/TLS */
5558 static const struct sipe_service_data service_tls
[] = {
5559 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
5560 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
5564 /* Service list for TCP */
5565 static const struct sipe_service_data service_tcp
[] = {
5566 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
5567 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
5571 /* Service list for UDP */
5572 static const struct sipe_service_data service_udp
[] = {
5573 { "sip", "udp", SIPE_TRANSPORT_UDP
},
5577 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
5578 static void resolve_next_service(struct sipe_account_data
*sip
,
5579 const struct sipe_service_data
*start
)
5582 sip
->service_data
= start
;
5584 sip
->service_data
++;
5585 if (sip
->service_data
->service
== NULL
) {
5587 /* Try connecting to the SIP hostname directly */
5588 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
5589 if (sip
->auto_transport
) {
5590 // If SSL is supported, default to using it; OCS servers aren't configured
5591 // by default to accept TCP
5592 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
5593 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
5594 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
5597 hostname
= g_strdup(sip
->sipdomain
);
5598 create_connection(sip
, hostname
, 0);
5603 /* Try to resolve next service */
5604 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
5605 sip
->service_data
->transport
,
5610 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
5612 struct sipe_account_data
*sip
= data
;
5614 sip
->srv_query_data
= NULL
;
5616 /* find the host to connect to */
5618 gchar
*hostname
= g_strdup(resp
->hostname
);
5619 int port
= resp
->port
;
5620 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
5624 sip
->transport
= sip
->service_data
->type
;
5626 create_connection(sip
, hostname
, port
);
5628 resolve_next_service(sip
, NULL
);
5632 static void sipe_login(PurpleAccount
*account
)
5634 PurpleConnection
*gc
;
5635 struct sipe_account_data
*sip
;
5636 gchar
**signinname_login
, **userserver
;
5637 const char *transport
;
5639 const char *username
= purple_account_get_username(account
);
5640 gc
= purple_account_get_connection(account
);
5642 purple_debug_info("sipe", "sipe_login: username '%s'\n", username
);
5644 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
5645 gc
->wants_to_die
= TRUE
;
5646 purple_connection_error(gc
, _("SIP Exchange username contains invalid characters"));
5650 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
5651 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
5652 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
5654 sip
->account
= account
;
5655 sip
->reregister_set
= FALSE
;
5656 sip
->reauthenticate_set
= FALSE
;
5657 sip
->subscribed
= FALSE
;
5658 sip
->subscribed_buddies
= FALSE
;
5660 /* username format: <username>,[<optional login>] */
5661 signinname_login
= g_strsplit(username
, ",", 2);
5662 purple_debug_info("sipe", "sipe_login: signinname[0] '%s'\n", signinname_login
[0]);
5664 /* ensure that username format is name@domain */
5665 if (!strchr(signinname_login
[0], '@') || g_str_has_prefix(signinname_login
[0], "@") || g_str_has_suffix(signinname_login
[0], "@")) {
5666 g_strfreev(signinname_login
);
5667 gc
->wants_to_die
= TRUE
;
5668 purple_connection_error(gc
, _("Username should be valid SIP URI\nExample: user@company.com"));
5671 sip
->username
= g_strdup(signinname_login
[0]);
5673 /* login name specified? */
5674 if (signinname_login
[1] && strlen(signinname_login
[1])) {
5675 gchar
**domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
5676 gboolean has_domain
= domain_user
[1] != NULL
;
5677 purple_debug_info("sipe", "sipe_login: signinname[1] '%s'\n", signinname_login
[1]);
5678 sip
->authdomain
= has_domain
? g_strdup(domain_user
[0]) : NULL
;
5679 sip
->authuser
= g_strdup(domain_user
[has_domain
? 1 : 0]);
5680 purple_debug_info("sipe", "sipe_login: auth domain '%s' user '%s'\n",
5681 sip
->authdomain
? sip
->authdomain
: "", sip
->authuser
);
5682 g_strfreev(domain_user
);
5685 userserver
= g_strsplit(signinname_login
[0], "@", 2);
5686 purple_debug_info("sipe", "sipe_login: user '%s' server '%s'\n", userserver
[0], userserver
[1]);
5687 purple_connection_set_display_name(gc
, userserver
[0]);
5688 sip
->sipdomain
= g_strdup(userserver
[1]);
5689 g_strfreev(userserver
);
5690 g_strfreev(signinname_login
);
5692 if (strchr(sip
->username
, ' ') != NULL
) {
5693 gc
->wants_to_die
= TRUE
;
5694 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
5698 sip
->password
= g_strdup(purple_connection_get_password(gc
));
5700 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
5702 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
5704 /* TODO: Set the status correctly. */
5705 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
5707 sip
->auto_transport
= FALSE
;
5708 transport
= purple_account_get_string(account
, "transport", "auto");
5709 userserver
= g_strsplit(purple_account_get_string(account
, "server", ""), ":", 2);
5710 if (userserver
[0]) {
5711 /* Use user specified server[:port] */
5715 port
= atoi(userserver
[1]);
5717 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login: user specified SIP server %s:%d\n",
5718 userserver
[0], port
);
5720 if (strcmp(transport
, "auto") == 0) {
5721 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
5722 } else if (strcmp(transport
, "tls") == 0) {
5723 sip
->transport
= SIPE_TRANSPORT_TLS
;
5724 } else if (strcmp(transport
, "tcp") == 0) {
5725 sip
->transport
= SIPE_TRANSPORT_TCP
;
5727 sip
->transport
= SIPE_TRANSPORT_UDP
;
5730 create_connection(sip
, g_strdup(userserver
[0]), port
);
5732 /* Server auto-discovery */
5733 if (strcmp(transport
, "auto") == 0) {
5734 sip
->auto_transport
= TRUE
;
5735 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
5736 } else if (strcmp(transport
, "tls") == 0) {
5737 resolve_next_service(sip
, service_tls
);
5738 } else if (strcmp(transport
, "tcp") == 0) {
5739 resolve_next_service(sip
, service_tcp
);
5741 resolve_next_service(sip
, service_udp
);
5744 g_strfreev(userserver
);
5747 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
5749 connection_free_all(sip
);
5754 if (sip
->query_data
!= NULL
)
5755 purple_dnsquery_destroy(sip
->query_data
);
5756 sip
->query_data
= NULL
;
5758 if (sip
->srv_query_data
!= NULL
)
5759 purple_srv_cancel(sip
->srv_query_data
);
5760 sip
->srv_query_data
= NULL
;
5762 if (sip
->listen_data
!= NULL
)
5763 purple_network_listen_cancel(sip
->listen_data
);
5764 sip
->listen_data
= NULL
;
5766 if (sip
->gsc
!= NULL
)
5767 purple_ssl_close(sip
->gsc
);
5770 sipe_auth_free(&sip
->registrar
);
5771 sipe_auth_free(&sip
->proxy
);
5774 purple_circ_buffer_destroy(sip
->txbuf
);
5777 g_free(sip
->realhostname
);
5778 sip
->realhostname
= NULL
;
5781 purple_input_remove(sip
->listenpa
);
5783 if (sip
->tx_handler
)
5784 purple_input_remove(sip
->tx_handler
);
5785 sip
->tx_handler
= 0;
5786 if (sip
->resendtimeout
)
5787 purple_timeout_remove(sip
->resendtimeout
);
5788 sip
->resendtimeout
= 0;
5789 if (sip
->timeouts
) {
5790 GSList
*entry
= sip
->timeouts
;
5792 struct scheduled_action
*sched_action
= entry
->data
;
5793 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
5794 purple_timeout_remove(sched_action
->timeout_handler
);
5795 if (sched_action
->destroy
) {
5796 (*sched_action
->destroy
)(sched_action
->payload
);
5798 g_free(sched_action
->name
);
5799 g_free(sched_action
);
5800 entry
= entry
->next
;
5803 g_slist_free(sip
->timeouts
);
5805 if (sip
->allow_events
) {
5806 GSList
*entry
= sip
->allow_events
;
5808 g_free(entry
->data
);
5809 entry
= entry
->next
;
5812 g_slist_free(sip
->allow_events
);
5814 if (sip
->containers
) {
5815 GSList
*entry
= sip
->containers
;
5817 free_container((struct sipe_container
*)entry
->data
);
5818 entry
= entry
->next
;
5821 g_slist_free(sip
->containers
);
5824 g_free(sip
->contact
);
5825 sip
->contact
= NULL
;
5827 g_free(sip
->regcallid
);
5828 sip
->regcallid
= NULL
;
5830 if (sip
->serveraddr
)
5831 g_free(sip
->serveraddr
);
5832 sip
->serveraddr
= NULL
;
5834 if (sip
->focus_factory_uri
)
5835 g_free(sip
->focus_factory_uri
);
5836 sip
->focus_factory_uri
= NULL
;
5839 sip
->processing_input
= FALSE
;
5843 * A callback for g_hash_table_foreach_remove
5845 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
5846 SIPE_UNUSED_PARAMETER gpointer user_data
)
5848 sipe_free_buddy((struct sipe_buddy
*) buddy
);
5850 /* We must return TRUE as the key/value have already been deleted */
5854 static void sipe_close(PurpleConnection
*gc
)
5856 struct sipe_account_data
*sip
= gc
->proto_data
;
5859 /* leave all conversations */
5860 sipe_session_close_all(sip
);
5861 sipe_session_remove_all(sip
);
5864 if (PURPLE_CONNECTION_IS_CONNECTED(sip
->gc
)) {
5865 do_register_exp(sip
, 0);
5868 sipe_connection_cleanup(sip
);
5869 g_free(sip
->sipdomain
);
5870 g_free(sip
->username
);
5871 g_free(sip
->password
);
5872 g_free(sip
->authdomain
);
5873 g_free(sip
->authuser
);
5874 g_free(sip
->status
);
5876 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
5877 g_hash_table_destroy(sip
->buddies
);
5878 g_hash_table_destroy(sip
->our_publications
);
5880 if (sip
->our_publication_keys
) {
5881 GSList
*entry
= sip
->our_publication_keys
;
5883 g_free(entry
->data
);
5884 entry
= entry
->next
;
5887 g_slist_free(sip
->our_publication_keys
);
5889 g_free(gc
->proto_data
);
5890 gc
->proto_data
= NULL
;
5893 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
5894 SIPE_UNUSED_PARAMETER
void *user_data
)
5896 PurpleAccount
*acct
= purple_connection_get_account(gc
);
5897 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
5898 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
5900 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
5901 purple_conversation_present(conv
);
5905 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
5906 SIPE_UNUSED_PARAMETER
void *user_data
)
5909 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
5910 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
5913 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
5914 SIPE_UNUSED_PARAMETER
struct transaction
*tc
)
5916 PurpleNotifySearchResults
*results
;
5917 PurpleNotifySearchColumn
*column
;
5918 xmlnode
*searchResults
;
5920 int match_count
= 0;
5921 gboolean more
= FALSE
;
5924 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
5926 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5927 if (!searchResults
) {
5928 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
5932 results
= purple_notify_searchresults_new();
5934 if (results
== NULL
) {
5935 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
5936 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
5938 xmlnode_free(searchResults
);
5942 column
= purple_notify_searchresults_column_new(_("User Name"));
5943 purple_notify_searchresults_column_add(results
, column
);
5945 column
= purple_notify_searchresults_column_new(_("Name"));
5946 purple_notify_searchresults_column_add(results
, column
);
5948 column
= purple_notify_searchresults_column_new(_("Company"));
5949 purple_notify_searchresults_column_add(results
, column
);
5951 column
= purple_notify_searchresults_column_new(_("Country"));
5952 purple_notify_searchresults_column_add(results
, column
);
5954 column
= purple_notify_searchresults_column_new(_("Email"));
5955 purple_notify_searchresults_column_add(results
, column
);
5957 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
5960 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
5961 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
5962 g_strfreev(uri_parts
);
5964 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
5965 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
5966 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
5967 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
5969 purple_notify_searchresults_row_add(results
, row
);
5973 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
5974 char *data
= xmlnode_get_data_unescaped(mrow
);
5975 more
= (g_strcasecmp(data
, "true") == 0);
5979 secondary
= g_strdup_printf(
5980 dngettext(GETTEXT_PACKAGE
,
5981 "Found %d contact%s:",
5982 "Found %d contacts%s:", match_count
),
5983 match_count
, more
? _(" (more matched your query)") : "");
5985 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
5986 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
5987 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
5990 xmlnode_free(searchResults
);
5994 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
5996 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
5997 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
6001 PurpleRequestField
*field
= entries
->data
;
6002 const char *id
= purple_request_field_get_id(field
);
6003 const char *value
= purple_request_field_string_get_value(field
);
6005 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
6007 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
6008 } while ((entries
= g_list_next(entries
)) != NULL
);
6012 gchar
*query
= g_strjoinv(NULL
, attrs
);
6013 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
6014 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
6015 send_soap_request_with_cb(gc
->proto_data
, body
,
6016 (TransCallback
) process_search_contact_response
, NULL
);
6024 static void sipe_show_find_contact(PurplePluginAction
*action
)
6026 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
6027 PurpleRequestFields
*fields
;
6028 PurpleRequestFieldGroup
*group
;
6029 PurpleRequestField
*field
;
6031 fields
= purple_request_fields_new();
6032 group
= purple_request_field_group_new(NULL
);
6033 purple_request_fields_add_group(fields
, group
);
6035 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
6036 purple_request_field_group_add_field(group
, field
);
6037 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
6038 purple_request_field_group_add_field(group
, field
);
6039 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
6040 purple_request_field_group_add_field(group
, field
);
6041 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
6042 purple_request_field_group_add_field(group
, field
);
6044 purple_request_fields(gc
,
6046 _("Search for a Contact"),
6047 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
6049 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
6051 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
6054 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
6055 SIPE_UNUSED_PARAMETER gpointer context
)
6058 PurplePluginAction
*act
;
6060 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
6061 menu
= g_list_prepend(menu
, act
);
6063 menu
= g_list_reverse(menu
);
6068 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
6072 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
6078 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
6084 static char *sipe_status_text(PurpleBuddy
*buddy
)
6086 struct sipe_account_data
*sip
;
6087 struct sipe_buddy
*sbuddy
;
6090 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
6091 if (sip
) //happens on pidgin exit
6093 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
6094 if (sbuddy
&& sbuddy
->annotation
)
6096 text
= g_strdup(sbuddy
->annotation
);
6103 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
6105 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
6106 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
6107 struct sipe_account_data
*sip
;
6108 struct sipe_buddy
*sbuddy
;
6109 char *annotation
= NULL
;
6111 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
6112 if (sip
) //happens on pidgin exit
6114 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
6117 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
6122 if (purple_presence_is_online(presence
))
6124 purple_notify_user_info_add_pair(user_info
, _("Status"), purple_status_get_name(status
));
6129 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
6136 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
6139 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
6140 g_hash_table_insert(table
, "login_label", (gpointer
)_("user@company.com"));
6144 static PurpleBuddy
*
6145 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
6148 const gchar
*server_alias
, *email
;
6149 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
6151 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
6153 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
6155 server_alias
= g_strdup(purple_buddy_get_server_alias(buddy
));
6157 purple_blist_server_alias_buddy(clone
, server_alias
);
6160 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
6162 purple_blist_node_set_string((PurpleBlistNode
*)clone
, "email", email
);
6165 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
6167 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
6172 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
6174 PurpleBuddy
*buddy
, *b
;
6175 PurpleConnection
*gc
;
6176 PurpleGroup
* group
= purple_find_group(group_name
);
6178 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
6180 buddy
= (PurpleBuddy
*)node
;
6182 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
6183 gc
= purple_account_get_connection(buddy
->account
);
6185 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
6187 b
= purple_blist_add_buddy_clone(group
, buddy
);
6190 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
6194 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
6196 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6198 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: buddy->name=%s\n", buddy
->name
);
6200 /* 2007+ conference */
6201 if (sip
->msrtc_event_categories
)
6203 sipe_conf_add(sip
, buddy
->name
);
6205 else /* 2005- multiparty chat */
6207 gchar
*self
= sip_uri_self(sip
);
6208 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++sip
->chat_seq
);
6209 struct sip_session
*session
;
6211 session
= sipe_session_add_chat(sip
);
6212 session
->roster_manager
= g_strdup(self
);
6214 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, g_strdup(chat_name
));
6215 session
->chat_name
= g_strdup(chat_name
);
6216 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
6217 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
6218 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, FALSE
);
6226 sipe_is_election_finished(struct sip_session
*session
)
6228 gboolean res
= TRUE
;
6230 SIPE_DIALOG_FOREACH
{
6231 if (dialog
->election_vote
== 0) {
6235 } SIPE_DIALOG_FOREACH_END
;
6238 session
->is_voting_in_progress
= FALSE
;
6244 sipe_election_start(struct sipe_account_data
*sip
,
6245 struct sip_session
*session
)
6247 int election_timeout
;
6249 if (session
->is_voting_in_progress
) {
6250 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
6253 session
->is_voting_in_progress
= TRUE
;
6255 session
->bid
= rand();
6257 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
6259 SIPE_DIALOG_FOREACH
{
6260 /* reset election_vote for each chat participant */
6261 dialog
->election_vote
= 0;
6263 /* send RequestRM to each chat participant*/
6264 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
6265 } SIPE_DIALOG_FOREACH_END
;
6267 election_timeout
= 15; /* sec */
6268 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
6272 * @param who a URI to whom to invite to chat
6275 sipe_invite_to_chat(struct sipe_account_data
*sip
,
6276 struct sip_session
*session
,
6280 if (session
->focus_uri
)
6282 sipe_invite_conf(sip
, session
, who
);
6284 else /* a multi-party chat */
6286 gchar
*self
= sip_uri_self(sip
);
6287 if (session
->roster_manager
) {
6288 if (!strcmp(session
->roster_manager
, self
)) {
6289 sipe_invite(sip
, session
, who
, NULL
, NULL
, FALSE
);
6291 sipe_refer(sip
, session
, who
);
6294 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: no RM available\n");
6296 session
->pending_invite_queue
= slist_insert_unique_sorted(
6297 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
6299 sipe_election_start(sip
, session
);
6306 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
6307 struct sip_session
*session
)
6310 GSList
*entry
= session
->pending_invite_queue
;
6313 invitee
= entry
->data
;
6314 sipe_invite_to_chat(sip
, session
, invitee
);
6315 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
6321 sipe_election_result(struct sipe_account_data
*sip
,
6324 struct sip_session
*session
= (struct sip_session
*)sess
;
6326 gboolean has_won
= TRUE
;
6328 if (session
->roster_manager
) {
6329 purple_debug_info("sipe",
6330 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
6334 session
->is_voting_in_progress
= FALSE
;
6336 SIPE_DIALOG_FOREACH
{
6337 if (dialog
->election_vote
< 0) {
6339 rival
= dialog
->with
;
6342 } SIPE_DIALOG_FOREACH_END
;
6345 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
6347 session
->roster_manager
= sip_uri_self(sip
);
6349 SIPE_DIALOG_FOREACH
{
6350 /* send SetRM to each chat participant*/
6351 sipe_send_election_set_rm(sip
, dialog
);
6352 } SIPE_DIALOG_FOREACH_END
;
6354 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
6358 sipe_process_pending_invite_queue(sip
, session
);
6362 * For 2007+ conference only.
6365 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_name
)
6367 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6368 struct sip_session
*session
;
6370 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
6371 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_name=%s\n", chat_name
);
6373 session
= sipe_session_find_chat_by_name(sip
, chat_name
);
6375 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
6379 * For 2007+ conference only.
6382 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_name
)
6384 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6385 struct sip_session
*session
;
6387 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
6388 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_name=%s\n", chat_name
);
6390 session
= sipe_session_find_chat_by_name(sip
, chat_name
);
6392 sipe_conf_delete_user(sip
, session
, buddy
->name
);
6396 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, const char *chat_name
)
6398 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6399 struct sip_session
*session
;
6401 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: buddy->name=%s\n", buddy
->name
);
6402 purple_debug_info("sipe", "sipe_buddy_menu_chat_cb: chat_name=%s\n", chat_name
);
6404 session
= sipe_session_find_chat_by_name(sip
, chat_name
);
6406 sipe_invite_to_chat(sip
, session
, buddy
->name
);
6410 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
6413 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
6415 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
6418 char *mailto
= g_strdup_printf("mailto:%s", email
);
6419 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
6423 char *const parmList
[] = {mailto
, NULL
};
6424 if ((pid
= fork()) == -1)
6426 purple_debug_info("sipe", "fork() error\n");
6430 execvp("xdg-email", parmList
);
6431 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
6439 //@TODO resolve env variable %WINDIR% first
6440 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
6443 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
6452 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
6457 * A menu which appear when right-clicking on buddy in contact list.
6460 sipe_buddy_menu(PurpleBuddy
*buddy
)
6462 PurpleBlistNode
*g_node
;
6463 PurpleGroup
*group
, *gr_parent
;
6464 PurpleMenuAction
*act
;
6466 GList
*menu_groups
= NULL
;
6467 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
6468 gchar
*self
= sip_uri_self(sip
);
6470 SIPE_SESSION_FOREACH
{
6471 if (strcmp(self
, buddy
->name
) && session
->chat_name
&& session
->conv
)
6473 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
6475 PurpleConvChatBuddyFlags flags
;
6476 PurpleConvChatBuddyFlags flags_us
;
6478 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
6479 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
6480 if (session
->focus_uri
6481 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
6482 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
6484 gchar
*label
= g_strdup_printf(_("Make Leader of '%s'"), session
->chat_name
);
6485 act
= purple_menu_action_new(label
,
6486 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
6487 g_strdup(session
->chat_name
), NULL
);
6489 menu
= g_list_prepend(menu
, act
);
6492 if (session
->focus_uri
6493 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
6495 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_name
);
6496 act
= purple_menu_action_new(label
,
6497 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
6498 g_strdup(session
->chat_name
), NULL
);
6500 menu
= g_list_prepend(menu
, act
);
6505 if (!session
->focus_uri
6506 || (session
->focus_uri
&& !session
->locked
))
6508 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_name
);
6509 act
= purple_menu_action_new(label
,
6510 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
6511 g_strdup(session
->chat_name
), NULL
);
6513 menu
= g_list_prepend(menu
, act
);
6517 } SIPE_SESSION_FOREACH_END
;
6519 act
= purple_menu_action_new(_("New Chat"),
6520 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
6522 menu
= g_list_prepend(menu
, act
);
6524 act
= purple_menu_action_new(_("Send Email..."),
6525 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
6527 menu
= g_list_prepend(menu
, act
);
6529 gr_parent
= purple_buddy_get_group(buddy
);
6530 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
6531 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
6534 group
= (PurpleGroup
*)g_node
;
6535 if (group
== gr_parent
)
6538 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
6541 act
= purple_menu_action_new(purple_group_get_name(group
),
6542 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
6544 menu_groups
= g_list_prepend(menu_groups
, act
);
6546 menu_groups
= g_list_reverse(menu_groups
);
6548 act
= purple_menu_action_new(_("Copy to"),
6551 menu
= g_list_prepend(menu
, act
);
6552 menu
= g_list_reverse(menu
);
6559 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
6561 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
6562 struct sip_session
*session
;
6564 session
= sipe_session_find_chat_by_name(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
6565 sipe_conf_modify_conference_lock(sip
, session
, locked
);
6569 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
6571 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
6572 sipe_conf_modify_lock(chat
, FALSE
);
6576 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
6578 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
6579 sipe_conf_modify_lock(chat
, TRUE
);
6583 sipe_chat_menu(PurpleChat
*chat
)
6585 PurpleMenuAction
*act
;
6586 PurpleConvChatBuddyFlags flags_us
;
6588 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
6589 struct sip_session
*session
;
6590 gchar
*self
= sip_uri_self(sip
);
6592 session
= sipe_session_find_chat_by_name(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
6593 if (!session
) return NULL
;
6595 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
6597 if (session
->focus_uri
6598 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
6600 if (session
->locked
) {
6601 act
= purple_menu_action_new(_("Unlock Conversation"),
6602 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
6604 menu
= g_list_prepend(menu
, act
);
6606 act
= purple_menu_action_new(_("Lock Conversation"),
6607 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
6609 menu
= g_list_prepend(menu
, act
);
6613 menu
= g_list_reverse(menu
);
6620 sipe_blist_node_menu(PurpleBlistNode
*node
)
6622 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
6623 return sipe_buddy_menu((PurpleBuddy
*) node
);
6624 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
6625 return sipe_chat_menu((PurpleChat
*)node
);
6632 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
6634 gboolean ret
= TRUE
;
6635 char *username
= (char *)trans
->payload
;
6637 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
6638 PurpleBuddy
*pbuddy
;
6639 struct sipe_buddy
*sbuddy
;
6641 char *server_alias
= NULL
;
6643 const char *device_name
= NULL
;
6645 purple_debug_info("sipe", "Fetching %s's user info for %s\n", username
, sip
->username
);
6647 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, username
);
6648 alias
= purple_buddy_get_local_alias(pbuddy
);
6652 //will query buddy UA's capabilities and send answer to log
6653 sipe_options_request(sip
, username
);
6655 sbuddy
= g_hash_table_lookup(sip
->buddies
, username
);
6658 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
6662 if (msg
->response
!= 200) {
6663 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
6665 xmlnode
*searchResults
;
6668 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
6669 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
6670 if (!searchResults
) {
6671 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
6672 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
6673 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
6674 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
6675 purple_notify_user_info_add_pair(info
, _("Job Title"), g_strdup(xmlnode_get_attrib(mrow
, "title")));
6676 purple_notify_user_info_add_pair(info
, _("Office"), g_strdup(xmlnode_get_attrib(mrow
, "office")));
6677 purple_notify_user_info_add_pair(info
, _("Business Phone"), g_strdup(xmlnode_get_attrib(mrow
, "phone")));
6678 purple_notify_user_info_add_pair(info
, _("Company"), g_strdup(xmlnode_get_attrib(mrow
, "company")));
6679 purple_notify_user_info_add_pair(info
, _("City"), g_strdup(xmlnode_get_attrib(mrow
, "city")));
6680 purple_notify_user_info_add_pair(info
, _("State"), g_strdup(xmlnode_get_attrib(mrow
, "state")));
6681 purple_notify_user_info_add_pair(info
, _("Country"), g_strdup(xmlnode_get_attrib(mrow
, "country")));
6682 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
6683 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
6684 if (!email
|| strcmp("", email
)) {
6685 if (!purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email")) {
6686 purple_blist_node_set_string((PurpleBlistNode
*)pbuddy
, "email", email
);
6690 xmlnode_free(searchResults
);
6693 purple_notify_user_info_add_section_break(info
);
6695 if (!server_alias
|| !strcmp("", server_alias
)) {
6696 g_free(server_alias
);
6697 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
6699 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
6703 // same as server alias, do not present
6704 alias
= (alias
&& server_alias
&& !strcmp(alias
, server_alias
)) ? NULL
: alias
;
6707 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
6710 if (!email
|| !strcmp("", email
)) {
6712 email
= g_strdup(purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email"));
6714 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
6720 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
6723 /* show a buddy's user info in a nice dialog box */
6724 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
6725 username
, /* buddy's username */
6727 NULL
, /* callback called when dialog closed */
6728 NULL
); /* userdata for callback */
6734 * AD search first, LDAP based
6736 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
6738 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
6739 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
6741 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
6742 send_soap_request_with_cb((struct sipe_account_data
*)gc
->proto_data
, body
,
6743 (TransCallback
) process_get_info_response
, (gpointer
)g_strdup(username
));
6748 static PurplePlugin
*my_protocol
= NULL
;
6750 static PurplePluginProtocolInfo prpl_info
=
6752 OPT_PROTO_CHAT_TOPIC
,
6753 NULL
, /* user_splits */
6754 NULL
, /* protocol_options */
6755 NO_BUDDY_ICONS
, /* icon_spec */
6756 sipe_list_icon
, /* list_icon */
6757 NULL
, /* list_emblems */
6758 sipe_status_text
, /* status_text */
6759 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
6760 sipe_status_types
, /* away_states */
6761 sipe_blist_node_menu
, /* blist_node_menu */
6762 NULL
, /* chat_info */
6763 NULL
, /* chat_info_defaults */
6764 sipe_login
, /* login */
6765 sipe_close
, /* close */
6766 sipe_im_send
, /* send_im */
6767 NULL
, /* set_info */ // TODO maybe
6768 sipe_send_typing
, /* send_typing */
6769 sipe_get_info
, /* get_info */
6770 sipe_set_status
, /* set_status */
6771 NULL
, /* set_idle */
6772 NULL
, /* change_passwd */
6773 sipe_add_buddy
, /* add_buddy */
6774 NULL
, /* add_buddies */
6775 sipe_remove_buddy
, /* remove_buddy */
6776 NULL
, /* remove_buddies */
6777 sipe_add_permit
, /* add_permit */
6778 sipe_add_deny
, /* add_deny */
6779 sipe_add_deny
, /* rem_permit */
6780 sipe_add_permit
, /* rem_deny */
6781 dummy_permit_deny
, /* set_permit_deny */
6782 NULL
, /* join_chat */
6783 NULL
, /* reject_chat */
6784 NULL
, /* get_chat_name */
6785 sipe_chat_invite
, /* chat_invite */
6786 sipe_chat_leave
, /* chat_leave */
6787 NULL
, /* chat_whisper */
6788 sipe_chat_send
, /* chat_send */
6789 sipe_keep_alive
, /* keepalive */
6790 NULL
, /* register_user */
6791 NULL
, /* get_cb_info */ // deprecated
6792 NULL
, /* get_cb_away */ // deprecated
6793 sipe_alias_buddy
, /* alias_buddy */
6794 sipe_group_buddy
, /* group_buddy */
6795 sipe_rename_group
, /* rename_group */
6796 NULL
, /* buddy_free */
6797 sipe_convo_closed
, /* convo_closed */
6798 purple_normalize_nocase
, /* normalize */
6799 NULL
, /* set_buddy_icon */
6800 sipe_remove_group
, /* remove_group */
6801 NULL
, /* get_cb_real_name */ // TODO?
6802 NULL
, /* set_chat_topic */
6803 NULL
, /* find_blist_chat */
6804 NULL
, /* roomlist_get_list */
6805 NULL
, /* roomlist_cancel */
6806 NULL
, /* roomlist_expand_category */
6807 NULL
, /* can_receive_file */
6808 NULL
, /* send_file */
6809 NULL
, /* new_xfer */
6810 NULL
, /* offline_message */
6811 NULL
, /* whiteboard_prpl_ops */
6812 sipe_send_raw
, /* send_raw */
6813 NULL
, /* roomlist_room_serialize */
6814 NULL
, /* unregister_user */
6815 NULL
, /* send_attention */
6816 NULL
, /* get_attention_types */
6818 sizeof(PurplePluginProtocolInfo
), /* struct_size */
6819 sipe_get_account_text_table
, /* get_account_text_table */
6823 static PurplePluginInfo info
= {
6824 PURPLE_PLUGIN_MAGIC
,
6825 PURPLE_MAJOR_VERSION
,
6826 PURPLE_MINOR_VERSION
,
6827 PURPLE_PLUGIN_PROTOCOL
, /**< type */
6828 NULL
, /**< ui_requirement */
6830 NULL
, /**< dependencies */
6831 PURPLE_PRIORITY_DEFAULT
, /**< priority */
6832 "prpl-sipe", /**< id */
6833 "Microsoft LCS/OCS", /**< name */
6834 VERSION
, /**< version */
6835 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
6836 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
6837 "Anibal Avelar <avelar@gmail.com>, " /**< author */
6838 "Gabriel Burt <gburt@novell.com>", /**< author */
6839 PURPLE_WEBSITE
, /**< homepage */
6840 sipe_plugin_load
, /**< load */
6841 sipe_plugin_unload
, /**< unload */
6842 sipe_plugin_destroy
, /**< destroy */
6843 NULL
, /**< ui_info */
6844 &prpl_info
, /**< extra_info */
6853 static void sipe_plugin_destroy(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
6857 entry
= prpl_info
.protocol_options
;
6859 purple_account_option_destroy(entry
->data
);
6860 entry
= g_list_delete_link(entry
, entry
);
6862 prpl_info
.protocol_options
= NULL
;
6864 entry
= prpl_info
.user_splits
;
6866 purple_account_user_split_destroy(entry
->data
);
6867 entry
= g_list_delete_link(entry
, entry
);
6869 prpl_info
.user_splits
= NULL
;
6872 static void init_plugin(PurplePlugin
*plugin
)
6874 PurpleAccountUserSplit
*split
;
6875 PurpleAccountOption
*option
;
6880 purple_debug_info(PACKAGE
, "bindtextdomain = %s\n", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
6881 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s\n",
6882 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
6883 textdomain(GETTEXT_PACKAGE
);
6886 purple_plugin_register(plugin
);
6888 split
= purple_account_user_split_new(_("Login \n user or DOMAIN\\user or\n user@company.com "), NULL
, ',');
6889 purple_account_user_split_set_reverse(split
, FALSE
);
6890 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
6892 option
= purple_account_option_string_new(_("Server[:Port]\n(Leave empty for auto-discovery)"), "server", "");
6893 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6895 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
6896 purple_account_option_add_list_item(option
, _("Auto"), "auto");
6897 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
6898 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
6899 purple_account_option_add_list_item(option
, _("UDP"), "udp");
6900 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6902 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
6903 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
6905 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
6906 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6909 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
6910 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6912 /* Suitable for sspi/NTLM, sspi/Kerberos and krb5 security mechanisms
6913 * No login/password is taken into account if this option present,
6914 * instead used default credentials stored in OS.
6916 option
= purple_account_option_bool_new(_("Use Single Sign-On"), "sso", TRUE
);
6917 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
6919 my_protocol
= plugin
;
6922 /* I had to redefined the function for it load, but works */
6923 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
6924 plugin
->info
= &(info
);
6925 init_plugin((plugin
));
6926 sipe_plugin_load((plugin
));
6927 return purple_plugin_register(plugin
);