6 * Copyright (C) 2010 pier11 <pier11@operamail.com>
7 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
8 * Copyright (C) 2009 pier11 <pier11@operamail.com>
9 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
10 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
11 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
14 * Thanks to Google's Summer of Code Program and the helpful mentors
17 * Session-based SIP MESSAGE documentation:
18 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
36 #include <sys/socket.h>
37 #include <sys/ioctl.h>
38 #include <sys/types.h>
39 #include <netinet/in.h>
45 #define _LIBC_INTERNAL_
58 #include "accountopt.h"
60 #include "conversation.h"
77 #include "sipe-chat.h"
78 #include "sipe-conf.h"
80 #include "sipe-dialog.h"
82 #include "sipe-session.h"
83 #include "sipe-utils.h"
85 #include "sipe-sign.h"
89 /* Backward compatibility when compiling against 2.4.x API */
90 #if !PURPLE_VERSION_CHECK(2,5,0)
91 #define PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY 0x0100
94 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
95 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
97 /* Keep in sync with sipe_transport_type! */
98 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
99 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
101 /* Status identifiers (see also: sipe_status_types()) */
102 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
103 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
104 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
105 /* PURPLE_STATUS_UNAVAILABLE: */
106 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
107 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
108 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
109 #define SIPE_STATUS_ID_ONPHONE "on-the-phone" /* On the phone */
110 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
111 /* PURPLE_STATUS_AWAY: */
112 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
113 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
114 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
115 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
116 /* Calendar-driven status for 2005 systems */
117 #define SIPE_STATUS_ID_MEETING "in-a-meeting" /* In a meeting */
118 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
119 /* ??? PURPLE_STATUS_MOBILE */
120 /* ??? PURPLE_STATUS_TUNE */
122 /* Status attributes (see also sipe_status_types() */
123 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
125 /* Action name templates */
126 #define ACTION_NAME_PRESENCE "<presence><%s>"
128 /** Allows to send typed messages from chat window again after account reinstantiation. */
130 sipe_rejoin_chat(PurpleConversation
*conv
)
132 if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
&&
133 PURPLE_CONV_CHAT(conv
)->left
)
135 PURPLE_CONV_CHAT(conv
)->left
= FALSE
;
136 purple_conversation_update(conv
, PURPLE_CONV_UPDATE_CHATLEFT
);
140 static char *genbranch()
142 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
143 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
144 rand() & 0xFFFF, rand() & 0xFFFF);
148 static char *default_ua
= NULL
;
150 sipe_get_useragent(struct sipe_account_data
*sip
)
152 const char *useragent
= purple_account_get_string(sip
->account
, "useragent", "");
153 if (is_empty(useragent
)) {
155 /*@TODO: better approach to define _user_ OS, it's version and host architecture */
157 #if defined(__linux__) || defined(__linux) || defined(__LINUX__)
158 #define SIPE_TARGET_PLATFORM "linux"
159 #elif defined(__NetBSD__) ||defined( __OpenBSD__) || defined(__FreeBSD__)
160 #define SIPE_TARGET_PLATFORM "bsd"
161 # elif defined(__APPLE__) || defined(__MACOS__)
162 #define SIPE_TARGET_PLATFORM "macosx"
163 #elif defined(__solaris__) || defined(__sun)
164 #define SIPE_TARGET_PLATFORM "sun"
165 #elif defined(_WIN32)
166 #define SIPE_TARGET_PLATFORM "win"
168 #define SIPE_TARGET_PLATFORM "generic"
171 #if defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)
172 #define SIPE_TARGET_ARCH "x86_64"
173 #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386)
174 #define SIPE_TARGET_ARCH "i386"
175 #elif defined(__ppc64__)
176 #define SIPE_TARGET_ARCH "ppc64"
177 #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR)
178 #define SIPE_TARGET_ARCH "ppc"
180 #define SIPE_TARGET_ARCH "other"
183 default_ua
= g_strdup_printf("Purple/%s Sipe/%s (%s-%s; %s)",
184 purple_core_get_version(),
186 SIPE_TARGET_PLATFORM
,
188 sip
->server_version
? sip
->server_version
: "");
190 useragent
= default_ua
;
195 static const char *sipe_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount
*a
,
196 SIPE_UNUSED_PARAMETER PurpleBuddy
*b
)
201 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
203 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
);
205 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
206 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
209 static void sipe_close(PurpleConnection
*gc
);
211 static void send_presence_status(struct sipe_account_data
*sip
);
213 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
215 static void sipe_keep_alive(PurpleConnection
*gc
)
217 struct sipe_account_data
*sip
= gc
->proto_data
;
218 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
219 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
220 gchar buf
[2] = {0, 0};
221 purple_debug_info("sipe", "sending keep alive\n");
222 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
224 time_t now
= time(NULL
);
225 if ((sip
->keepalive_timeout
> 0) &&
226 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
227 #if PURPLE_VERSION_CHECK(2,4,0)
228 && ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
231 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
232 sendout_pkt(gc
, "\r\n\r\n");
233 sip
->last_keepalive
= now
;
238 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
240 struct sip_connection
*ret
= NULL
;
241 GSList
*entry
= sip
->openconns
;
244 if (ret
->fd
== fd
) return ret
;
250 static void sipe_auth_free(struct sip_auth
*auth
)
252 g_free(auth
->opaque
);
256 g_free(auth
->target
);
258 auth
->type
= AUTH_TYPE_UNSET
;
261 g_free(auth
->gssapi_data
);
262 auth
->gssapi_data
= NULL
;
263 sip_sec_destroy_context(auth
->gssapi_context
);
264 auth
->gssapi_context
= NULL
;
267 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
269 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
271 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
275 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
277 struct sip_connection
*conn
= connection_find(sip
, fd
);
279 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
280 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
286 static void connection_free_all(struct sipe_account_data
*sip
)
288 struct sip_connection
*ret
= NULL
;
289 GSList
*entry
= sip
->openconns
;
292 connection_remove(sip
, ret
->fd
);
293 entry
= sip
->openconns
;
297 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
300 const char *authuser
= sip
->authuser
;
304 if (!authuser
|| strlen(authuser
) < 1) {
305 authuser
= sip
->username
;
308 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
309 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
311 // If we have a signature for the message, include that
312 if (msg
->signature
) {
313 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
);
316 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
317 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
321 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
324 purple_account_get_bool(sip
->account
, "sso", TRUE
),
325 sip
->authdomain
? sip
->authdomain
: "",
330 if (!gssapi_data
|| !auth
->gssapi_context
) {
331 sip
->gc
->wants_to_die
= TRUE
;
332 purple_connection_error(sip
->gc
, _("Failed to authenticate to server"));
336 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
337 ret
= g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth_protocol
, opaque
, auth
->realm
, auth
->target
, gssapi_data
);
343 return g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth_protocol
, auth
->realm
, auth
->target
);
345 } else { /* Digest */
347 /* Calculate new session key */
349 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
350 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
351 authuser
, auth
->realm
, sip
->password
,
352 auth
->gssapi_data
, NULL
);
355 sprintf(noncecount
, "%08d", auth
->nc
++);
356 response
= purple_cipher_http_digest_calculate_response("md5",
357 msg
->method
, msg
->target
, NULL
, NULL
,
358 auth
->gssapi_data
, noncecount
, NULL
,
360 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
362 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
);
368 static char *parse_attribute(const char *attrname
, const char *source
)
370 const char *tmp
, *tmp2
;
372 int len
= strlen(attrname
);
374 if (!strncmp(source
, attrname
, len
)) {
376 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
378 retval
= g_strndup(tmp
, tmp2
- tmp
);
380 retval
= g_strdup(tmp
);
386 static void fill_auth(gchar
*hdr
, struct sip_auth
*auth
)
392 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
396 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
397 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
398 auth
->type
= AUTH_TYPE_NTLM
;
401 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
402 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
403 auth
->type
= AUTH_TYPE_KERBEROS
;
407 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
408 auth
->type
= AUTH_TYPE_DIGEST
;
412 parts
= g_strsplit(hdr
, "\", ", 0);
413 for (i
= 0; parts
[i
]; i
++) {
416 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
418 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
419 g_free(auth
->gssapi_data
);
420 auth
->gssapi_data
= tmp
;
422 if (auth
->type
== AUTH_TYPE_NTLM
) {
423 /* NTLM module extracts nonce from gssapi-data */
427 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
428 /* Only used with AUTH_TYPE_DIGEST */
429 g_free(auth
->gssapi_data
);
430 auth
->gssapi_data
= tmp
;
431 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
432 g_free(auth
->opaque
);
434 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
438 if (auth
->type
== AUTH_TYPE_DIGEST
) {
439 /* Throw away old session key */
440 g_free(auth
->opaque
);
445 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
446 g_free(auth
->target
);
455 static void sipe_canwrite_cb(gpointer data
,
456 SIPE_UNUSED_PARAMETER gint source
,
457 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
459 PurpleConnection
*gc
= data
;
460 struct sipe_account_data
*sip
= gc
->proto_data
;
464 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
466 if (max_write
== 0) {
467 if (sip
->tx_handler
!= 0){
468 purple_input_remove(sip
->tx_handler
);
474 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
476 if (written
< 0 && errno
== EAGAIN
)
478 else if (written
<= 0) {
479 /*TODO: do we really want to disconnect on a failure to write?*/
480 purple_connection_error(gc
, _("Could not write"));
484 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
487 static void sipe_canwrite_cb_ssl(gpointer data
,
488 SIPE_UNUSED_PARAMETER gint src
,
489 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
491 PurpleConnection
*gc
= data
;
492 struct sipe_account_data
*sip
= gc
->proto_data
;
496 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
498 if (max_write
== 0) {
499 if (sip
->tx_handler
!= 0) {
500 purple_input_remove(sip
->tx_handler
);
506 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
508 if (written
< 0 && errno
== EAGAIN
)
510 else if (written
<= 0) {
511 /*TODO: do we really want to disconnect on a failure to write?*/
512 purple_connection_error(gc
, _("Could not write"));
516 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
519 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
521 static void send_later_cb(gpointer data
, gint source
,
522 SIPE_UNUSED_PARAMETER
const gchar
*error
)
524 PurpleConnection
*gc
= data
;
525 struct sipe_account_data
*sip
;
526 struct sip_connection
*conn
;
528 if (!PURPLE_CONNECTION_IS_VALID(gc
))
536 purple_connection_error(gc
, _("Could not connect"));
540 sip
= gc
->proto_data
;
542 sip
->connecting
= FALSE
;
543 sip
->last_keepalive
= time(NULL
);
545 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
547 /* If there is more to write now, we need to register a handler */
548 if (sip
->txbuf
->bufused
> 0)
549 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
551 conn
= connection_create(sip
, source
);
552 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
555 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
557 struct sipe_account_data
*sip
;
558 struct sip_connection
*conn
;
560 if (!PURPLE_CONNECTION_IS_VALID(gc
))
562 if (gsc
) purple_ssl_close(gsc
);
566 sip
= gc
->proto_data
;
569 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
570 sip
->connecting
= FALSE
;
571 sip
->last_keepalive
= time(NULL
);
573 conn
= connection_create(sip
, gsc
->fd
);
575 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
580 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
581 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
583 PurpleConnection
*gc
= data
;
584 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
585 if (sip
== NULL
) return;
587 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
589 /* If there is more to write now */
590 if (sip
->txbuf
->bufused
> 0) {
591 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
596 static void sendlater(PurpleConnection
*gc
, const char *buf
)
598 struct sipe_account_data
*sip
= gc
->proto_data
;
600 if (!sip
->connecting
) {
601 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
602 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
603 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
605 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
606 purple_connection_error(gc
, _("Could not create socket"));
609 sip
->connecting
= TRUE
;
612 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
613 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
615 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
618 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
620 struct sipe_account_data
*sip
= gc
->proto_data
;
621 time_t currtime
= time(NULL
);
622 int writelen
= strlen(buf
);
625 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sending - %s######\n%s######\n", ctime(&currtime
), tmp
= fix_newlines(buf
));
627 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
628 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
629 purple_debug_info("sipe", "could not send packet\n");
638 if (sip
->tx_handler
) {
643 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
645 ret
= write(sip
->fd
, buf
, writelen
);
649 if (ret
< 0 && errno
== EAGAIN
)
651 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
656 if (ret
< writelen
) {
657 if (!sip
->tx_handler
){
659 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
662 sip
->tx_handler
= purple_input_add(sip
->fd
,
663 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
668 /* XXX: is it OK to do this? You might get part of a request sent
669 with part of another. */
670 if (sip
->txbuf
->bufused
> 0)
671 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
673 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
679 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
681 sendout_pkt(gc
, buf
);
685 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
687 GSList
*tmp
= msg
->headers
;
690 GString
*outstr
= g_string_new("");
691 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
693 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
694 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
695 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
696 tmp
= g_slist_next(tmp
);
698 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
699 sendout_pkt(sip
->gc
, outstr
->str
);
700 g_string_free(outstr
, TRUE
);
703 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
707 if (sip
->registrar
.type
== AUTH_TYPE_UNSET
) {
711 if (sip
->registrar
.gssapi_context
) {
712 struct sipmsg_breakdown msgbd
;
713 gchar
*signature_input_str
;
715 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
716 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
717 sip
->registrar
.ntlm_num
++;
718 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
719 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
720 if (signature_input_str
!= NULL
) {
721 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
722 msg
->signature
= signature_hex
;
723 msg
->rand
= g_strdup(msgbd
.rand
);
724 msg
->num
= g_strdup(msgbd
.num
);
725 g_free(signature_input_str
);
727 sipmsg_breakdown_free(&msgbd
);
730 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
731 buf
= auth_header(sip
, &sip
->registrar
, msg
);
733 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
736 } 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")) {
737 sip
->registrar
.nc
= 3;
739 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
741 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
744 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
749 buf
= auth_header(sip
, &sip
->registrar
, msg
);
750 sipmsg_add_header_now_pos(msg
, "Proxy-Authorization", buf
, 5);
753 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
757 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
758 const char *text
, const char *body
)
762 GString
*outstr
= g_string_new("");
763 struct sipe_account_data
*sip
= gc
->proto_data
;
766 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
768 contact
= get_contact(sip
);
769 sipmsg_add_header(msg
, "Contact", contact
);
774 sprintf(len
, "%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
775 sipmsg_add_header(msg
, "Content-Length", len
);
777 sipmsg_add_header(msg
, "Content-Length", "0");
780 msg
->response
= code
;
782 sipmsg_strip_headers(msg
, keepers
);
783 sipmsg_merge_new_headers(msg
);
784 sign_outgoing_message(msg
, sip
, msg
->method
);
786 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
789 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
790 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
792 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
793 tmp
= g_slist_next(tmp
);
795 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
796 sendout_pkt(gc
, outstr
->str
);
797 g_string_free(outstr
, TRUE
);
800 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
802 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
803 purple_debug_info("sipe", "sip->transactions count:%d after removal\n", g_slist_length(sip
->transactions
));
804 if (trans
->msg
) sipmsg_free(trans
->msg
);
805 if (trans
->payload
) {
806 (*trans
->payload
->destroy
)(trans
->payload
->data
);
807 g_free(trans
->payload
);
813 static struct transaction
*
814 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
816 gchar
*call_id
= NULL
;
818 struct transaction
*trans
= g_new0(struct transaction
, 1);
820 trans
->time
= time(NULL
);
821 trans
->msg
= (struct sipmsg
*)msg
;
822 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
823 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
824 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
825 trans
->callback
= callback
;
826 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
827 purple_debug_info("sipe", "sip->transactions count:%d after addition\n", g_slist_length(sip
->transactions
));
831 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
833 struct transaction
*trans
;
834 GSList
*transactions
= sip
->transactions
;
835 gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
836 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
837 gchar
*key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
839 while (transactions
) {
840 trans
= transactions
->data
;
841 if (!g_strcasecmp(trans
->key
, key
)) {
845 transactions
= transactions
->next
;
853 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
854 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
855 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
857 struct sipe_account_data
*sip
= gc
->proto_data
;
858 const char *addh
= "";
861 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
862 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
863 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
864 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
865 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
866 gchar
*route
= g_strdup("");
867 gchar
*epid
= get_epid(sip
);
868 int cseq
= dialog
? ++dialog
->cseq
: 1 /* as Call-Id is new in this case */;
869 struct transaction
*trans
= NULL
;
871 if (dialog
&& dialog
->routes
)
873 GSList
*iter
= dialog
->routes
;
878 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
880 iter
= g_slist_next(iter
);
884 if (!ourtag
&& !dialog
) {
888 if (!strcmp(method
, "REGISTER")) {
889 if (sip
->regcallid
) {
891 callid
= g_strdup(sip
->regcallid
);
893 sip
->regcallid
= g_strdup(callid
);
898 if (addheaders
) addh
= addheaders
;
900 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
901 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
902 "From: <sip:%s>%s%s;epid=%s\r\n"
903 "To: <%s>%s%s%s%s\r\n"
904 "Max-Forwards: 70\r\n"
909 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
911 dialog
&& dialog
->request
? dialog
->request
: url
,
912 TRANSPORT_DESCRIPTOR
,
913 purple_network_get_my_ip(-1),
915 branch
? ";branch=" : "",
916 branch
? branch
: "",
918 ourtag
? ";tag=" : "",
919 ourtag
? ourtag
: "",
922 theirtag
? ";tag=" : "",
923 theirtag
? theirtag
: "",
924 theirepid
? ";epid=" : "",
925 theirepid
? theirepid
: "",
928 sipe_get_useragent(sip
),
932 body
? (gsize
) strlen(body
) : 0,
936 //printf ("parsing msg buf:\n%s\n\n", buf);
937 msg
= sipmsg_parse_msg(buf
);
948 sign_outgoing_message (msg
, sip
, method
);
950 buf
= sipmsg_to_string (msg
);
952 /* add to ongoing transactions */
953 /* ACK isn't supposed to be answered ever. So we do not keep transaction for it. */
954 if (strcmp(method
, "ACK")) {
955 trans
= transactions_add_buf(sip
, msg
, tc
);
959 sendout_pkt(gc
, buf
);
966 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
969 send_soap_request_with_cb(struct sipe_account_data
*sip
,
972 TransCallback callback
,
973 struct transaction_payload
*payload
)
975 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sip
);
976 gchar
*contact
= get_contact(sip
);
977 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
978 "Content-Type: application/SOAP+xml\r\n",contact
);
980 struct transaction
*trans
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
981 trans
->payload
= payload
;
988 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
990 send_soap_request_with_cb(sip
, NULL
, body
, NULL
, NULL
);
993 static char *get_contact_register(struct sipe_account_data
*sip
)
995 char *epid
= get_epid(sip
);
996 char *uuid
= generateUUIDfromEPID(epid
);
997 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
);
1003 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1011 if (!sip
->sipdomain
) return;
1013 uri
= sip_uri_from_name(sip
->sipdomain
);
1014 expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1015 to
= sip_uri_self(sip
);
1016 contact
= get_contact_register(sip
);
1017 hdr
= g_strdup_printf("Contact: %s\r\n"
1018 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1019 "Event: registration\r\n"
1020 "Allow-Events: presence\r\n"
1021 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1022 "%s", contact
, expires
);
1026 sip
->registerstatus
= 1;
1028 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1029 process_register_response
);
1036 static void do_register_cb(struct sipe_account_data
*sip
,
1037 SIPE_UNUSED_PARAMETER
void *unused
)
1039 do_register_exp(sip
, -1);
1040 sip
->reregister_set
= FALSE
;
1043 static void do_register(struct sipe_account_data
*sip
)
1045 do_register_exp(sip
, -1);
1049 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1051 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1052 send_soap_request(sip
, body
);
1057 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1060 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1062 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1065 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1069 void sipe_auth_user_cb(void * data
)
1071 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1074 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1079 void sipe_deny_user_cb(void * data
)
1081 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1084 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1089 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1091 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1092 sipe_contact_allow_deny(sip
, name
, TRUE
);
1096 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1098 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1099 sipe_contact_allow_deny(sip
, name
, FALSE
);
1103 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1105 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1106 sipe_contact_set_acl(sip, name, "");
1110 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1114 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1115 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1117 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1119 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1120 if (!watchers
) return;
1122 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1123 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1124 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1125 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1127 // TODO pull out optional displayName to pass as alias
1129 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1130 job
->who
= remote_user
;
1132 purple_account_request_authorization(
1146 xmlnode_free(watchers
);
1151 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1153 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1154 if (!purple_group
) {
1155 purple_group
= purple_group_new(group
->name
);
1156 purple_blist_add_group(purple_group
, NULL
);
1160 group
->purple_group
= purple_group
;
1161 sip
->groups
= g_slist_append(sip
->groups
, group
);
1162 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1164 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1168 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1170 struct sipe_group
*group
;
1176 entry
= sip
->groups
;
1178 group
= entry
->data
;
1179 if (group
->id
== id
) {
1182 entry
= entry
->next
;
1187 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1189 struct sipe_group
*group
;
1195 entry
= sip
->groups
;
1197 group
= entry
->data
;
1198 if (!strcmp(group
->name
, name
)) {
1201 entry
= entry
->next
;
1207 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1210 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1211 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1212 send_soap_request(sip
, body
);
1214 g_free(group
->name
);
1215 group
->name
= g_strdup(name
);
1219 * Only appends if no such value already stored.
1222 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1223 GSList
* res
= list
;
1224 if (!g_slist_find_custom(list
, data
, func
)) {
1225 res
= g_slist_insert_sorted(list
, data
, func
);
1231 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1232 return group1
->id
- group2
->id
;
1236 * Returns string like "2 4 7 8" - group ids buddy belong to.
1239 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1242 //creating array from GList, converting int to gchar*
1243 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1244 GSList
*entry
= buddy
->groups
;
1246 struct sipe_group
* group
= entry
->data
;
1247 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1248 entry
= entry
->next
;
1252 res
= g_strjoinv(" ", ids_arr
);
1253 g_strfreev(ids_arr
);
1258 * Sends buddy update to server
1261 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1263 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1264 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1266 if (buddy
&& purple_buddy
) {
1267 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1269 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1270 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1272 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1273 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1275 send_soap_request(sip
, body
);
1281 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1283 if (msg
->response
== 200) {
1284 struct sipe_group
*group
;
1285 struct group_user_context
*ctx
= trans
->payload
->data
;
1289 struct sipe_buddy
*buddy
;
1291 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1296 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1302 group_id
= xmlnode_get_data(node
);
1308 group
= g_new0(struct sipe_group
, 1);
1309 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1311 group
->name
= g_strdup(ctx
->group_name
);
1313 sipe_group_add(sip
, group
);
1315 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1317 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1320 sipe_group_set_user(sip
, ctx
->user_name
);
1328 static void sipe_group_context_destroy(gpointer data
)
1330 struct group_user_context
*ctx
= data
;
1331 g_free(ctx
->group_name
);
1332 g_free(ctx
->user_name
);
1336 static void sipe_group_create (struct sipe_account_data
*sip
, const gchar
*name
, const gchar
* who
)
1338 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
1339 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
1341 ctx
->group_name
= g_strdup(name
);
1342 ctx
->user_name
= g_strdup(who
);
1343 payload
->destroy
= sipe_group_context_destroy
;
1344 payload
->data
= ctx
;
1346 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1347 send_soap_request_with_cb(sip
, NULL
, body
, process_add_group_response
, payload
);
1352 * Data structure for scheduled actions
1355 struct scheduled_action
{
1358 * Format is <Event>[<Data>...]
1359 * Example: <presence><sip:user@domain.com> or <registration>
1362 guint timeout_handler
;
1363 gboolean repetitive
;
1365 GDestroyNotify destroy
;
1366 struct sipe_account_data
*sip
;
1372 * Should return FALSE if repetitive action is not needed
1374 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1377 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1378 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1379 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1380 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1381 ret
= sched_action
->repetitive
;
1382 if (sched_action
->destroy
) {
1383 (*sched_action
->destroy
)(sched_action
->payload
);
1385 g_free(sched_action
->name
);
1386 g_free(sched_action
);
1391 * Kills action timer effectively cancelling
1394 * @param name of action
1396 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1400 if (!sip
->timeouts
|| !name
) return;
1402 entry
= sip
->timeouts
;
1404 struct scheduled_action
*sched_action
= entry
->data
;
1405 if(!strcmp(sched_action
->name
, name
)) {
1406 GSList
*to_delete
= entry
;
1407 entry
= entry
->next
;
1408 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1409 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1410 purple_timeout_remove(sched_action
->timeout_handler
);
1411 if (sched_action
->destroy
) {
1412 (*sched_action
->destroy
)(sched_action
->payload
);
1414 g_free(sched_action
->name
);
1415 g_free(sched_action
);
1417 entry
= entry
->next
;
1423 sipe_schedule_action0(const gchar
*name
,
1427 GDestroyNotify destroy
,
1428 struct sipe_account_data
*sip
,
1431 struct scheduled_action
*sched_action
;
1433 /* Make sure each action only exists once */
1434 sipe_cancel_scheduled_action(sip
, name
);
1436 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1437 sched_action
= g_new0(struct scheduled_action
, 1);
1438 sched_action
->repetitive
= FALSE
;
1439 sched_action
->name
= g_strdup(name
);
1440 sched_action
->action
= action
;
1441 sched_action
->destroy
= destroy
;
1442 sched_action
->sip
= sip
;
1443 sched_action
->payload
= payload
;
1444 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1445 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1446 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1447 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1451 sipe_schedule_action(const gchar
*name
,
1454 GDestroyNotify destroy
,
1455 struct sipe_account_data
*sip
,
1458 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1462 * Same as sipe_schedule_action() but timeout is in milliseconds.
1465 sipe_schedule_action_msec(const gchar
*name
,
1468 GDestroyNotify destroy
,
1469 struct sipe_account_data
*sip
,
1472 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1476 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1477 time_t calculate_from
);
1480 sipe_got_user_status(struct sipe_account_data
*sip
,
1482 const char *status_id
)
1484 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
1486 /* Check if on 2005 system contact's calendar is in Busy status,
1487 * then set/preserve it.
1489 if (!sip
->ocs2007
&& sipe_cal_get_status(sbuddy
, time(NULL
))== SIPE_CAL_BUSY
) {
1490 PurpleBuddy
*pbuddy
= purple_find_buddy(sip
->account
, sbuddy
->name
);
1491 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
1492 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
1494 sbuddy
->last_non_cal_status_id
= g_strdup(status_id
);
1495 sbuddy
->last_non_cal_activity
= sbuddy
->activity
;
1496 sbuddy
->activity
= g_strdup(_("In a meeting"));
1499 if (strcmp(purple_status_get_id(status
), SIPE_STATUS_ID_MEETING
)) {
1500 purple_debug_info("sipe", "sipe_got_user_status: to %s for %s\n", SIPE_STATUS_ID_MEETING
, sbuddy
->name
);
1501 purple_prpl_got_user_status(sip
->account
, sbuddy
->name
, SIPE_STATUS_ID_MEETING
, NULL
);
1504 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
1509 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
1510 struct sipe_buddy
*sbuddy
,
1511 struct sipe_account_data
*sip
)
1513 PurpleBuddy
*pbuddy
= purple_find_buddy(sip
->account
, sbuddy
->name
);
1514 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
1515 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
1516 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
) + 2*60 /* 2 min */);
1518 if (cal_status
< SIPE_CAL_NO_DATA
) {
1519 purple_debug_info("sipe", "update_calendar_status_cb: cal_status:%d for %s\n", cal_status
, sbuddy
->name
);
1521 if (cal_status
== SIPE_CAL_BUSY
) {
1523 if (strcmp(purple_status_get_id(status
), SIPE_STATUS_ID_MEETING
)) {
1524 purple_debug_info("sipe", "update_calendar_status_cb: to %s for %s\n", SIPE_STATUS_ID_MEETING
, sbuddy
->name
);
1525 sbuddy
->last_non_cal_status_id
= g_strdup(purple_status_get_id(status
));
1526 sbuddy
->last_non_cal_activity
= sbuddy
->activity
;
1527 sbuddy
->activity
= g_strdup(_("In a meeting"));
1528 purple_prpl_got_user_status(sip
->account
, sbuddy
->name
, SIPE_STATUS_ID_MEETING
, NULL
);
1531 if (!strcmp(purple_status_get_id(status
), SIPE_STATUS_ID_MEETING
)) {
1532 /* resetting status to last known */
1533 purple_debug_info("sipe", "update_calendar_status_cb: resetting to %s for %s\n",
1534 sbuddy
->last_non_cal_status_id
, sbuddy
->name
);
1535 g_free(sbuddy
->last_non_cal_status_id
);
1536 sbuddy
->last_non_cal_status_id
= NULL
;
1537 purple_prpl_got_user_status(sip
->account
, sbuddy
->name
, sbuddy
->last_non_cal_status_id
, NULL
);
1538 g_free(sbuddy
->activity
);
1539 sbuddy
->activity
= sbuddy
->last_non_cal_activity
;
1540 sbuddy
->last_non_cal_activity
= NULL
;
1546 * Updates contact's status
1547 * based on their calendar information.
1549 * Applicability: 2005 systems
1552 update_calendar_status(struct sipe_account_data
*sip
)
1554 purple_debug_info("sipe", "update_calendar_status() started.\n");
1555 g_hash_table_foreach(sip
->buddies
, (GHFunc
)update_calendar_status_cb
, (gpointer
)sip
);
1557 /* repeat scheduling */
1558 sipe_sched_calendar_status_update(sip
, time(NULL
) + 3*60 /* 3 min */);
1562 * Schedules process of contacts' status update
1563 * based on their calendar information.
1564 * Should be scheduled to the beginning of every
1565 * 15 min interval, like:
1566 * 13:00, 13:15, 13:30, 13:45, etc.
1568 * Applicability: 2005 systems
1571 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1572 time_t calculate_from
)
1574 int interval
= 15*60;
1575 /** start of the beginning of closest 15 min interval. */
1576 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1578 purple_debug_info("sipe", "sipe_sched_calendar_status_update: calculate_from time: %s",
1579 asctime(localtime(&calculate_from
)));
1580 purple_debug_info("sipe", "sipe_sched_calendar_status_update: next start time : %s",
1581 asctime(localtime(&next_start
)));
1583 sipe_schedule_action("<+2005-cal-status>",
1584 (int)(next_start
- time(NULL
)),
1585 (Action
)update_calendar_status
,
1592 * Schedules process of self status publish
1593 * based on own calendar information.
1594 * Should be scheduled to the beginning of every
1595 * 15 min interval, like:
1596 * 13:00, 13:15, 13:30, 13:45, etc.
1598 * Applicability: 2007+ systems
1601 sipe_sched_calendar_status_self_publish(struct sipe_account_data
*sip
,
1602 time_t calculate_from
)
1604 int interval
= 5*60;
1605 /** start of the beginning of closest 5 min interval. */
1606 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1608 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: calculate_from time: %s",
1609 asctime(localtime(&calculate_from
)));
1610 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: next start time : %s",
1611 asctime(localtime(&next_start
)));
1613 sipe_schedule_action("<+2007-cal-status>",
1614 (int)(next_start
- time(NULL
)),
1615 (Action
)publish_calendar_status_self
,
1621 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1623 /** Should be g_free()'d
1626 sipe_get_subscription_key(gchar
*event
,
1631 if (is_empty(event
)) return NULL
;
1633 if (event
&& !g_ascii_strcasecmp(event
, "presence")) {
1634 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1635 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, with
);
1637 /* @TODO drop participated buddies' just_added flag */
1639 /* Subscription is identified by <event> key */
1640 key
= g_strdup_printf("<%s>", event
);
1646 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
1647 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
1649 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
1650 gchar
*event
= sipmsg_find_header(msg
, "Event");
1653 /* The case with 2005 Public IM Connectivity (PIC) - no Event header */
1655 struct sipmsg
*request_msg
= trans
->msg
;
1656 event
= sipmsg_find_header(request_msg
, "Event");
1659 key
= sipe_get_subscription_key(event
, with
);
1661 /* 200 OK; 481 Call Leg Does Not Exist */
1662 if (key
&& (msg
->response
== 200 || msg
->response
== 481)) {
1663 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
1664 g_hash_table_remove(sip
->subscriptions
, key
);
1665 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
1669 /* create/store subscription dialog if not yet */
1670 if (msg
->response
== 200) {
1671 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1672 gchar
*cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
1675 struct sip_subscription
*subscription
= g_new0(struct sip_subscription
, 1);
1676 g_hash_table_insert(sip
->subscriptions
, g_strdup(key
), subscription
);
1678 subscription
->dialog
.callid
= g_strdup(callid
);
1679 subscription
->dialog
.cseq
= atoi(cseq
);
1680 subscription
->dialog
.with
= g_strdup(with
);
1681 subscription
->event
= g_strdup(event
);
1682 sipe_dialog_parse(&subscription
->dialog
, msg
, TRUE
);
1684 purple_debug_info("sipe", "process_subscribe_response: subscription dialog added for: %s\n", key
);
1693 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1695 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1700 static void sipe_subscribe_resource_uri(const char *name
,
1701 SIPE_UNUSED_PARAMETER gpointer value
,
1702 gchar
**resources_uri
)
1704 gchar
*tmp
= *resources_uri
;
1705 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1709 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1711 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1712 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1713 gchar
*tmp
= *resources_uri
;
1715 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
1717 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
1722 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1723 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1724 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1725 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1726 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1729 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1732 gchar
*contact
= get_contact(sip
);
1735 gchar
*require
= "";
1737 gchar
*autoextend
= "";
1738 gchar
*content_type
;
1739 struct sip_dialog
*dialog
;
1742 require
= ", categoryList";
1743 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1744 content_type
= "application/msrtc-adrl-categorylist+xml";
1745 content
= g_strdup_printf(
1746 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1747 "<action name=\"subscribe\" id=\"63792024\">\n"
1748 "<adhocList>\n%s</adhocList>\n"
1749 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1750 "<category name=\"calendarData\"/>\n"
1751 "<category name=\"contactCard\"/>\n"
1752 "<category name=\"note\"/>\n"
1753 "<category name=\"state\"/>\n"
1756 "</batchSub>", sip
->username
, resources_uri
);
1758 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1759 content_type
= "application/adrl+xml";
1760 content
= g_strdup_printf(
1761 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1762 "<create xmlns=\"\">\n%s</create>\n"
1763 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1765 g_free(resources_uri
);
1767 request
= g_strdup_printf(
1768 "Require: adhoclist%s\r\n"
1769 "Supported: eventlist\r\n"
1770 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1771 "Supported: ms-piggyback-first-notify\r\n"
1772 "%sSupported: ms-benotify\r\n"
1773 "Proxy-Require: ms-benotify\r\n"
1774 "Event: presence\r\n"
1775 "Content-Type: %s\r\n"
1776 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1779 /* subscribe to buddy presence */
1780 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1781 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1782 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1783 purple_debug_info("sipe", "sipe_subscribe_presence_batched_to: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
1785 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
1793 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
,
1794 SIPE_UNUSED_PARAMETER
void *unused
)
1796 gchar
*to
= sip_uri_self(sip
);
1797 gchar
*resources_uri
= g_strdup("");
1799 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1801 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1803 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1806 struct presence_batched_routed
{
1811 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1813 struct presence_batched_routed
*data
= payload
;
1814 GSList
*buddies
= data
->buddies
;
1816 g_free(buddies
->data
);
1817 buddies
= buddies
->next
;
1819 g_slist_free(data
->buddies
);
1824 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
1826 struct presence_batched_routed
*data
= payload
;
1827 GSList
*buddies
= data
->buddies
;
1828 gchar
*resources_uri
= g_strdup("");
1830 gchar
*tmp
= resources_uri
;
1831 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
1833 buddies
= buddies
->next
;
1835 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
1836 g_strdup(data
->host
));
1840 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1841 * The user sends a single SUBSCRIBE request to the subscribed contact.
1842 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1846 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
1850 gchar
*to
= sip_uri((char *)buddy_name
);
1851 gchar
*tmp
= get_contact(sip
);
1853 gchar
*content
= NULL
;
1854 gchar
*autoextend
= "";
1855 gchar
*content_type
= "";
1856 struct sip_dialog
*dialog
;
1857 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, to
);
1858 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1860 if (sbuddy
) sbuddy
->just_added
= FALSE
;
1863 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
1865 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1868 request
= g_strdup_printf(
1869 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1870 "Supported: ms-piggyback-first-notify\r\n"
1871 "%s%sSupported: ms-benotify\r\n"
1872 "Proxy-Require: ms-benotify\r\n"
1873 "Event: presence\r\n"
1874 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
1877 content
= g_strdup_printf(
1878 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1879 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1880 "<resource uri=\"%s\"%s\n"
1882 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1883 "<category name=\"calendarData\"/>\n"
1884 "<category name=\"contactCard\"/>\n"
1885 "<category name=\"note\"/>\n"
1886 "<category name=\"state\"/>\n"
1889 "</batchSub>", sip
->username
, to
, context
);
1894 /* subscribe to buddy presence */
1895 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1896 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1897 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1898 purple_debug_info("sipe", "sipe_subscribe_presence_single: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
1900 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
1908 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1910 purple_debug_info("sipe", "sipe_set_status: status=%s\n", purple_status_get_id(status
));
1912 if (!purple_status_is_active(status
))
1916 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
1920 g_free(sip
->status
);
1921 sip
->status
= g_strdup(purple_status_get_id(status
));
1923 /* schedule 2 sec to capture idle flag */
1924 action_name
= g_strdup_printf("<%s>", "+set-status");
1925 sipe_schedule_action(action_name
, 2, (Action
)send_presence_status
, NULL
, sip
, NULL
);
1926 g_free(action_name
);
1931 sipe_set_idle(PurpleConnection
* gc
,
1934 purple_debug_info("sipe", "sipe_set_idle: time=%d\n", time
);
1937 struct sipe_account_data
*sip
= gc
->proto_data
;
1940 sip
->was_idle
= sip
->is_idle
;
1941 sip
->is_idle
= (time
> 0);
1947 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
,
1948 SIPE_UNUSED_PARAMETER
const char *alias
)
1950 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1951 sipe_group_set_user(sip
, name
);
1955 sipe_group_buddy(PurpleConnection
*gc
,
1957 const char *old_group_name
,
1958 const char *new_group_name
)
1960 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1961 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1962 struct sipe_group
* old_group
= NULL
;
1963 struct sipe_group
* new_group
;
1965 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1966 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1968 if(!buddy
) { // buddy not in roaming list
1972 if (old_group_name
) {
1973 old_group
= sipe_group_find_by_name(sip
, old_group_name
);
1975 new_group
= sipe_group_find_by_name(sip
, new_group_name
);
1978 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1979 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1983 sipe_group_create(sip
, new_group_name
, who
);
1985 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1986 sipe_group_set_user(sip
, who
);
1990 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1992 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1994 /* libpurple can call us with undefined buddy or group */
1995 if (buddy
&& group
) {
1996 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1998 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1999 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
2000 purple_blist_rename_buddy(buddy
, buddy_name
);
2003 /* Prepend sip: if needed */
2004 if (strncmp("sip:", buddy
->name
, 4)) {
2005 gchar
*buf
= sip_uri_from_name(buddy
->name
);
2006 purple_blist_rename_buddy(buddy
, buf
);
2010 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
2011 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
2012 purple_debug_info("sipe", "sipe_add_buddy: adding %s\n", buddy
->name
);
2013 b
->name
= g_strdup(buddy
->name
);
2014 b
->just_added
= TRUE
;
2015 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
2016 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
2017 /* @TODO should go to callback */
2018 sipe_subscribe_presence_single(sip
, b
->name
);
2020 purple_debug_info("sipe", "sipe_add_buddy: buddy %s already in internal list\n", buddy
->name
);
2025 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
2029 * We are calling g_hash_table_foreach_steal(). That means that no
2030 * key/value deallocation functions are called. Therefore the glib
2031 * hash code does not touch the key (buddy->name) or value (buddy)
2032 * of the to-be-deleted hash node at all. It follows that we
2034 * - MUST free the memory for the key ourselves and
2035 * - ARE allowed to do it in this function
2037 * Conclusion: glib must be broken on the Windows platform if sipe
2038 * crashes with SIGTRAP when closing. You'll have to live
2039 * with the memory leak until this is fixed.
2041 g_free(buddy
->name
);
2043 g_free(buddy
->activity
);
2044 g_free(buddy
->meeting_subject
);
2045 g_free(buddy
->meeting_location
);
2046 g_free(buddy
->annotation
);
2048 g_free(buddy
->cal_start_time
);
2049 g_free(buddy
->cal_free_busy_base64
);
2050 g_free(buddy
->cal_free_busy
);
2051 g_free(buddy
->last_non_cal_status_id
);
2052 g_free(buddy
->last_non_cal_activity
);
2054 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
2056 g_free(buddy
->device_name
);
2057 g_slist_free(buddy
->groups
);
2062 * Unassociates buddy from group first.
2063 * Then see if no groups left, removes buddy completely.
2064 * Otherwise updates buddy groups on server.
2066 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2068 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2069 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
2070 struct sipe_group
*g
= NULL
;
2072 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2077 g
= sipe_group_find_by_name(sip
, group
->name
);
2081 b
->groups
= g_slist_remove(b
->groups
, g
);
2082 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
2085 if (g_slist_length(b
->groups
) < 1) {
2086 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
2087 sipe_cancel_scheduled_action(sip
, action_name
);
2088 g_free(action_name
);
2090 g_hash_table_remove(sip
->buddies
, buddy
->name
);
2093 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
2094 send_soap_request(sip
, body
);
2100 //updates groups on server
2101 sipe_group_set_user(sip
, b
->name
);
2107 sipe_rename_group(PurpleConnection
*gc
,
2108 const char *old_name
,
2110 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
2112 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2113 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, old_name
);
2115 sipe_group_rename(sip
, s_group
, group
->name
);
2117 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
2122 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
2124 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2125 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
2128 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
2129 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
2130 send_soap_request(sip
, body
);
2133 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
2134 g_free(s_group
->name
);
2137 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
2141 static GList
*sipe_status_types(SIPE_UNUSED_PARAMETER PurpleAccount
*acc
)
2143 PurpleStatusType
*type
;
2144 GList
*types
= NULL
;
2146 /* Macros to reduce code repetition.
2147 Translators: noun */
2148 #define SIPE_ADD_STATUS(prim,id,name) type = purple_status_type_new_with_attrs( \
2150 TRUE, TRUE, FALSE, \
2151 SIPE_STATUS_ATTR_ID_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), \
2153 types = g_list_append(types, type);
2154 #define SIPE_ADD_STATUS_NO_MSG(prim,id,name,user) type = purple_status_type_new( \
2155 prim, id, name, user); \
2156 types = g_list_append(types, type);
2159 SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE
,
2163 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2164 SIPE_STATUS_ID_BUSY
, _("Busy"));
2166 /* BusyIdle (not user settable) */
2167 SIPE_ADD_STATUS_NO_MSG(PURPLE_STATUS_UNAVAILABLE
,
2168 SIPE_STATUS_ID_BUSYIDLE
, _("BusyIdle"),
2171 /* Do Not Disturb (not user settable) */
2172 SIPE_ADD_STATUS_NO_MSG(PURPLE_STATUS_UNAVAILABLE
,
2173 SIPE_STATUS_ID_DND
, NULL
,
2176 /* In a meeting (not user settable)
2177 * Calendar-driven status for 2005 systems.
2179 SIPE_ADD_STATUS_NO_MSG(PURPLE_STATUS_UNAVAILABLE
,
2180 SIPE_STATUS_ID_MEETING
, _("Busy"),
2184 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2185 SIPE_STATUS_ID_BRB
, _("Be right back"));
2188 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2192 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2193 SIPE_STATUS_ID_ONPHONE
, _("On the phone"));
2196 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2197 SIPE_STATUS_ID_LUNCH
, _("Out to lunch"));
2199 /* Idle/Inactive (not user settable) */
2200 SIPE_ADD_STATUS_NO_MSG(PURPLE_STATUS_AVAILABLE
,
2201 SIPE_STATUS_ID_IDLE
, _("Inactive"),
2204 /* Appear Offline */
2205 SIPE_ADD_STATUS_NO_MSG(PURPLE_STATUS_INVISIBLE
,
2210 SIPE_ADD_STATUS_NO_MSG(PURPLE_STATUS_OFFLINE
,
2218 * A callback for g_hash_table_foreach
2220 static void sipe_buddy_subscribe_cb(SIPE_UNUSED_PARAMETER
char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
2222 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
2223 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
2224 guint time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
2225 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
2227 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy
->name
));
2228 g_free(action_name
);
2232 * Removes entries from purple buddy list
2233 * that does not correspond ones in the roaming contact list.
2235 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
2236 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
2237 GSList
*entry
= buddies
;
2238 struct sipe_buddy
*buddy
;
2242 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
2243 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
2246 g
= purple_buddy_get_group(b
);
2247 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
2249 gboolean in_sipe_groups
= FALSE
;
2250 GSList
*entry2
= buddy
->groups
;
2252 struct sipe_group
*group
= entry2
->data
;
2253 if (!strcmp(group
->name
, g
->name
)) {
2254 in_sipe_groups
= TRUE
;
2257 entry2
= entry2
->next
;
2259 if(!in_sipe_groups
) {
2260 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
2261 purple_blist_remove_buddy(b
);
2264 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
2265 purple_blist_remove_buddy(b
);
2267 entry
= entry
->next
;
2269 g_slist_free(buddies
);
2272 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2274 int len
= msg
->bodylen
;
2276 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
2279 const gchar
*contacts_delta
;
2280 xmlnode
*group_node
;
2281 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
2285 /* Convert the contact from XML to Purple Buddies */
2286 isc
= xmlnode_from_str(msg
->body
, len
);
2291 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
2292 if (contacts_delta
) {
2293 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2296 if (!strcmp(isc
->name
, "contactList")) {
2299 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
2300 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2301 const char *name
= xmlnode_get_attrib(group_node
, "name");
2303 if (!strncmp(name
, "~", 1)) {
2304 name
= _("Other Contacts");
2306 group
->name
= g_strdup(name
);
2307 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
2309 sipe_group_add(sip
, group
);
2312 // Make sure we have at least one group
2313 if (g_slist_length(sip
->groups
) == 0) {
2314 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2315 PurpleGroup
*purple_group
;
2316 group
->name
= g_strdup(_("Other Contacts"));
2318 purple_group
= purple_group_new(group
->name
);
2319 purple_blist_add_group(purple_group
, NULL
);
2320 sip
->groups
= g_slist_append(sip
->groups
, group
);
2323 /* Parse contacts */
2324 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
2325 const gchar
*uri
= xmlnode_get_attrib(item
, "uri");
2326 const gchar
*name
= xmlnode_get_attrib(item
, "name");
2328 struct sipe_buddy
*buddy
= NULL
;
2330 gchar
**item_groups
;
2333 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2334 tmp
= sip_uri_from_name(uri
);
2335 buddy_name
= g_ascii_strdown(tmp
, -1);
2338 /* assign to group Other Contacts if nothing else received */
2339 tmp
= g_strdup(xmlnode_get_attrib(item
, "groups"));
2340 if(!tmp
|| !strcmp("", tmp
) ) {
2341 struct sipe_group
*group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
2343 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
2345 item_groups
= g_strsplit(tmp
, " ", 0);
2348 while (item_groups
[i
]) {
2349 struct sipe_group
*group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2351 // If couldn't find the right group for this contact, just put them in the first group we have
2352 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2353 group
= sip
->groups
->data
;
2356 if (group
!= NULL
) {
2357 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2359 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2360 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2362 purple_debug_info("sipe", "Created new buddy %s with alias %s\n", buddy_name
, uri
);
2365 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
2366 if (name
!= NULL
&& strlen(name
) != 0) {
2367 purple_blist_alias_buddy(b
, name
);
2369 purple_debug_info("sipe", "Replaced buddy %s alias with %s\n", buddy_name
, name
);
2374 buddy
= g_new0(struct sipe_buddy
, 1);
2375 buddy
->name
= g_strdup(b
->name
);
2376 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2379 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2381 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2383 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2388 } // while, contact groups
2389 g_strfreev(item_groups
);
2394 sipe_cleanup_local_blist(sip
);
2398 //subscribe to buddies
2399 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2400 if(sip
->batched_support
){
2401 sipe_subscribe_presence_batched(sip
, NULL
);
2404 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2406 sip
->subscribed_buddies
= TRUE
;
2408 /* for 2005 systems schedule contacts' status update
2409 * based on their calendar information
2411 if (!sip
->ocs2007
) {
2412 //sipe_sched_calendar_status_update(sip, time(NULL));
2419 * Subscribe roaming contacts
2421 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
)
2423 gchar
*to
= sip_uri_self(sip
);
2424 gchar
*tmp
= get_contact(sip
);
2425 gchar
*hdr
= g_strdup_printf(
2426 "Event: vnd-microsoft-roaming-contacts\r\n"
2427 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2428 "Supported: com.microsoft.autoextend\r\n"
2429 "Supported: ms-benotify\r\n"
2430 "Proxy-Require: ms-benotify\r\n"
2431 "Supported: ms-piggyback-first-notify\r\n"
2432 "Contact: %s\r\n", tmp
);
2435 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2440 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
,
2441 SIPE_UNUSED_PARAMETER
void *unused
)
2444 struct sip_dialog
*dialog
;
2445 gchar
*to
= sip_uri_self(sip
);
2446 gchar
*tmp
= get_contact(sip
);
2447 gchar
*hdr
= g_strdup_printf(
2448 "Event: presence.wpending\r\n"
2449 "Accept: text/xml+msrtc.wpending\r\n"
2450 "Supported: com.microsoft.autoextend\r\n"
2451 "Supported: ms-benotify\r\n"
2452 "Proxy-Require: ms-benotify\r\n"
2453 "Supported: ms-piggyback-first-notify\r\n"
2454 "Contact: %s\r\n", tmp
);
2457 /* Subscription is identified by <event> key */
2458 key
= g_strdup_printf("<%s>", "presence.wpending");
2459 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2460 purple_debug_info("sipe", "sipe_subscribe_presence_wpending: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2462 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", dialog
, process_subscribe_response
);
2470 * Fires on deregistration event initiated by server.
2471 * [MS-SIPREGE] SIP extension.
2476 // Content-Type: text/registration-event
2477 // subscription-state: terminated;expires=0
2478 // ms-diagnostics-public: 4141;reason="User disabled"
2480 // deregistered;event=rejected
2482 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2484 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2485 gchar
*event
= NULL
;
2486 gchar
*reason
= NULL
;
2487 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2489 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2490 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2492 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2493 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2494 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2495 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2497 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2501 if (warning
!= NULL
) {
2502 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2503 } else { // for LCS2005
2505 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2506 error_id
= 4140; // [MS-SIPREGE]
2507 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2508 reason
= g_strdup(_("you are already signed in at another location"));
2509 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2511 reason
= g_strdup(_("user disabled")); // [MS-OCER]
2512 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2514 reason
= g_strdup(_("user moved")); // [MS-OCER]
2518 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
2521 sip
->gc
->wants_to_die
= TRUE
;
2522 purple_connection_error(sip
->gc
, warning
);
2527 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2529 xmlnode
*xn_provision_group_list
;
2532 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2534 /* provisionGroup */
2535 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2536 if (!strcmp("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2537 g_free(sip
->focus_factory_uri
);
2538 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2539 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2540 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2544 xmlnode_free(xn_provision_group_list
);
2547 /** for 2005 system */
2549 sipe_process_provisioning(struct sipe_account_data
*sip
,
2552 xmlnode
*xn_provision
;
2555 xn_provision
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2556 if ((node
= xmlnode_get_child(xn_provision
, "user"))) {
2557 purple_debug_info("sipe", "sipe_process_provisioning: uri=%s\n", xmlnode_get_attrib(node
, "uri"));
2558 if ((node
= xmlnode_get_child(node
, "line"))) {
2559 const gchar
*line_uri
= xmlnode_get_attrib(node
, "uri");
2560 const gchar
*server
= xmlnode_get_attrib(node
, "server");
2561 purple_debug_info("sipe", "sipe_process_provisioning: line_uri=%s server=%s\n", line_uri
, server
);
2562 sip_csta_open(sip
, line_uri
, server
);
2565 xmlnode_free(xn_provision
);
2568 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2570 const gchar
*contacts_delta
;
2573 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2579 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2582 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2589 free_container(struct sipe_container
*container
)
2593 if (!container
) return;
2595 entry
= container
->members
;
2597 g_free(entry
->data
);
2598 entry
= g_slist_remove(entry
, entry
->data
);
2604 * Finds locally stored MS-PRES container member
2606 static struct sipe_container_member
*
2607 sipe_find_container_member(struct sipe_container
*container
,
2611 struct sipe_container_member
*member
;
2614 if (container
== NULL
|| type
== NULL
) {
2618 entry
= container
->members
;
2620 member
= entry
->data
;
2621 if (!g_strcasecmp(member
->type
, type
)
2622 && ((!member
->value
&& !value
)
2623 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2627 entry
= entry
->next
;
2633 * Finds locally stored MS-PRES container by id
2635 static struct sipe_container
*
2636 sipe_find_container(struct sipe_account_data
*sip
,
2639 struct sipe_container
*container
;
2646 entry
= sip
->containers
;
2648 container
= entry
->data
;
2649 if (id
== container
->id
) {
2652 entry
= entry
->next
;
2666 sipe_find_access_level(struct sipe_account_data
*sip
,
2670 guint containers
[] = {32000, 400, 300, 200, 100};
2673 for (i
= 0; i
< 5; i
++) {
2674 struct sipe_container_member
*member
;
2675 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2676 if (!container
) continue;
2678 member
= sipe_find_container_member(container
, type
, value
);
2680 return containers
[i
];
2688 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2690 guint container_version
,
2691 const gchar
* action
,
2695 gchar
*self
= sip_uri_self(sip
);
2696 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2699 gchar
*body
= g_strdup_printf(
2700 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2701 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2702 "</setContainerMembers>",
2710 contact
= get_contact(sip
);
2711 hdr
= g_strdup_printf("Contact: %s\r\n"
2712 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2715 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2723 free_publication(struct sipe_publication
*publication
)
2725 g_free(publication
->category
);
2726 g_free(publication
->cal_event_hash
);
2727 g_free(publication
->note
);
2729 g_free(publication
->working_hours_xml_str
);
2730 g_free(publication
->fb_start_str
);
2731 g_free(publication
->free_busy_base64
);
2733 g_free(publication
);
2736 /* key is <category><instance><container> */
2738 sipe_is_our_publication(struct sipe_account_data
*sip
,
2743 /* filling keys for our publications if not yet cached */
2744 if (!sip
->our_publication_keys
) {
2745 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
2746 guint machine_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
2747 guint user_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
);
2748 guint calendar_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
2749 guint cal_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
);
2750 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
2751 guint note_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
);
2754 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2755 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
2757 /* state:machineState */
2758 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2759 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
2760 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2761 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
2763 /* state:userState */
2764 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2765 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
2766 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2767 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
2769 /* state:calendarState */
2770 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2771 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
2772 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2773 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
2775 /* state:calendarState OOF */
2776 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2777 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
2778 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2779 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
2782 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2783 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
2784 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2785 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
2786 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2787 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
2790 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2791 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
2792 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2793 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
2794 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2795 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
2797 /* calendarData:WorkingHours */
2798 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2799 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
2800 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2801 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
2802 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2803 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
2804 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2805 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
2806 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2807 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
2808 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2809 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
2811 /* calendarData:FreeBusy */
2812 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2813 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
2814 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2815 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
2816 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2817 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
2818 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2819 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
2820 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2821 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
2822 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2823 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
2825 //purple_debug_info("sipe", "sipe_is_our_publication: sip->our_publication_keys length=%d\n",
2826 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
2829 //purple_debug_info("sipe", "sipe_is_our_publication: key=%s\n", key);
2831 entry
= sip
->our_publication_keys
;
2833 //purple_debug_info("sipe", " sipe_is_our_publication: entry->data=%s\n", entry->data);
2834 if (!strcmp(entry
->data
, key
)) {
2837 entry
= entry
->next
;
2842 /** Property names to store in blist.xml */
2843 #define ALIAS_PROP "alias"
2844 #define EMAIL_PROP "email"
2845 #define PHONE_PROP "phone"
2846 #define PHONE_DISPLAY_PROP "phone-display"
2847 #define PHONE_MOBILE_PROP "phone-mobile"
2848 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
2849 #define PHONE_HOME_PROP "phone-home"
2850 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
2851 #define PHONE_OTHER_PROP "phone-other"
2852 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
2853 #define PHONE_CUSTOM1_PROP "phone-custom1"
2854 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
2855 #define SITE_PROP "site"
2856 #define COMPANY_PROP "company"
2857 #define DEPARTMENT_PROP "department"
2858 #define TITLE_PROP "title"
2859 #define OFFICE_PROP "office"
2860 /** implies work address */
2861 #define ADDRESS_STREET_PROP "address-street"
2862 #define ADDRESS_CITY_PROP "address-city"
2863 #define ADDRESS_STATE_PROP "address-state"
2864 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
2865 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
2867 * Update user information
2869 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2870 * @param property_name
2871 * @param property_value may be modified to strip white space
2874 sipe_update_user_info(struct sipe_account_data
*sip
,
2876 const char *property_name
,
2877 char *property_value
)
2879 GSList
*buddies
, *entry
;
2881 if (!property_name
|| strlen(property_name
) == 0) return;
2884 property_value
= g_strstrip(property_value
);
2886 entry
= buddies
= purple_find_buddies(sip
->account
, uri
); /* all buddies in different groups */
2888 const char *prop_str
;
2889 const char *server_alias
;
2890 PurpleBuddy
*p_buddy
= entry
->data
;
2892 /* for Display Name */
2893 if (!strcmp(property_name
, ALIAS_PROP
)) {
2894 if (property_value
&& sipe_is_bad_alias(uri
, purple_buddy_get_alias(p_buddy
))) {
2895 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, property_value
);
2896 purple_blist_alias_buddy(p_buddy
, property_value
);
2899 server_alias
= purple_buddy_get_server_alias(p_buddy
);
2900 if (property_value
&& strlen(property_value
) > 0 &&
2901 ( (server_alias
&& strcmp(property_value
, server_alias
))
2902 || !server_alias
|| strlen(server_alias
) == 0 )
2904 purple_blist_server_alias_buddy(p_buddy
, property_value
);
2907 /* for other properties */
2909 if (property_value
&& strlen(property_value
) > 0) {
2910 prop_str
= purple_blist_node_get_string(&p_buddy
->node
, property_name
);
2911 if (!prop_str
|| g_ascii_strcasecmp(prop_str
, property_value
)) {
2912 purple_blist_node_set_string(&p_buddy
->node
, property_name
, property_value
);
2917 entry
= entry
->next
;
2919 g_slist_free(buddies
);
2924 * Suitable for both 2005 and 2007 systems.
2926 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2928 * @param phone may be modified to strip white space
2929 * @param phone_display_string may be modified to strip white space
2932 sipe_update_user_phone(struct sipe_account_data
*sip
,
2934 const gchar
*phone_type
,
2936 gchar
*phone_display_string
)
2938 const char *phone_node
= PHONE_PROP
; /* work phone by default */
2939 const char *phone_display_node
= PHONE_DISPLAY_PROP
; /* work phone by default */
2941 if(!phone
|| strlen(phone
) == 0) return;
2943 if (phone_type
&& (!strcmp(phone_type
, "mobile") || !strcmp(phone_type
, "cell"))) {
2944 phone_node
= PHONE_MOBILE_PROP
;
2945 phone_display_node
= PHONE_MOBILE_DISPLAY_PROP
;
2946 } else if (phone_type
&& !strcmp(phone_type
, "home")) {
2947 phone_node
= PHONE_HOME_PROP
;
2948 phone_display_node
= PHONE_HOME_DISPLAY_PROP
;
2949 } else if (phone_type
&& !strcmp(phone_type
, "other")) {
2950 phone_node
= PHONE_OTHER_PROP
;
2951 phone_display_node
= PHONE_OTHER_DISPLAY_PROP
;
2952 } else if (phone_type
&& !strcmp(phone_type
, "custom1")) {
2953 phone_node
= PHONE_CUSTOM1_PROP
;
2954 phone_display_node
= PHONE_CUSTOM1_DISPLAY_PROP
;
2957 sipe_update_user_info(sip
, uri
, phone_node
, phone
);
2958 if (phone_display_string
) {
2959 sipe_update_user_info(sip
, uri
, phone_display_node
, phone_display_string
);
2964 sipe_update_calendar(struct sipe_account_data
*sip
)
2966 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
2968 purple_debug_info("sipe", "sipe_update_calendar: started.\n");
2970 if (!strcmp(calendar
, "EXCH")) {
2971 sipe_ews_update_calendar(sip
);
2974 /* schedule repeat */
2975 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_INTERVAL
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
2977 purple_debug_info("sipe", "sipe_update_calendar: finished.\n");
2981 send_publish_category_initial(struct sipe_account_data
*sip
);
2984 * When we receive some self (BE) NOTIFY with a new subscriber
2985 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2988 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2995 char *display_name
= NULL
;
2997 GSList
*category_names
= NULL
;
2999 purple_debug_info("sipe", "sipe_process_roaming_self\n");
3001 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3004 contact
= get_contact(sip
);
3005 to
= sip_uri_self(sip
);
3009 /* set list of categories participating in this XML */
3010 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3011 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3012 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
3014 purple_debug_info("sipe", "sipe_process_roaming_self: category_names length=%d\n",
3015 category_names
? (int) g_slist_length(category_names
) : -1);
3016 /* drop category information */
3017 if (category_names
) {
3018 GSList
*entry
= category_names
;
3020 GHashTable
*cat_publications
;
3021 const gchar
*category
= entry
->data
;
3022 entry
= entry
->next
;
3023 purple_debug_info("sipe", "sipe_process_roaming_self: dropping category: %s\n", category
);
3024 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
3025 if (cat_publications
) {
3026 g_hash_table_remove(sip
->our_publications
, category
);
3027 purple_debug_info("sipe", " sipe_process_roaming_self: dropped category: %s\n", category
);
3031 g_slist_free(category_names
);
3032 /* filling our categories reflected in roaming data */
3033 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3034 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3035 const gchar
*container
= xmlnode_get_attrib(node
, "container");
3036 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
3037 const gchar
*version
= xmlnode_get_attrib(node
, "version");
3038 guint version_int
= version
? atoi(version
) : 0;
3041 if (!container
|| !instance
) continue;
3043 /* key is <category><instance><container> */
3044 key
= g_strdup_printf("<%s><%s><%s>", name
, instance
, container
);
3045 purple_debug_info("sipe", "sipe_process_roaming_self: key=%s version=%d\n", key
, version_int
);
3046 if (sipe_is_our_publication(sip
, key
)) {
3047 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
3049 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3050 publication
->category
= g_strdup(name
);
3051 publication
->instance
= atoi(instance
);
3052 publication
->container
= atoi(container
);
3053 publication
->version
= version_int
;
3054 /* filling publication->availability */
3055 if (!strcmp(name
, "state")) {
3056 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3057 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3060 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3062 publication
->availability
= atoi(avail_str
);
3066 /* for calendarState */
3067 if (xn_state
&& !strcmp(xmlnode_get_attrib(xn_state
, "type"), "calendarState")) {
3068 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3069 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
3071 event
->start_time
= purple_str_to_time(xmlnode_get_attrib(xn_state
, "startTime"),
3072 FALSE
, NULL
, NULL
, NULL
);
3074 if (!strcmp(xmlnode_get_attrib(xn_activity
, "token"), "in-a-meeting")) {
3075 event
->is_meeting
= TRUE
;
3078 event
->subject
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingSubject"));
3079 event
->location
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingLocation"));
3081 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
3082 purple_debug_info("sipe", "sipe_process_roaming_self: hash=%s\n",
3083 publication
->cal_event_hash
);
3084 sipe_cal_event_free(event
);
3087 /* filling publication->note */
3088 if (!strcmp(name
, "note")) {
3089 xmlnode
*xn_body
= xmlnode_get_descendant(node
, "note", "body", NULL
);
3091 publication
->note
= xmlnode_get_data(xn_body
);
3095 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
3096 if (!strcmp(name
, "calendarData") && (publication
->container
== 300)) {
3097 xmlnode
*xn_free_busy
= xmlnode_get_descendant(node
, "calendarData", "freeBusy", NULL
);
3098 xmlnode
*xn_working_hours
= xmlnode_get_descendant(node
, "calendarData", "WorkingHours", NULL
);
3100 publication
->fb_start_str
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
3101 publication
->free_busy_base64
= xmlnode_get_data(xn_free_busy
);
3103 if (xn_working_hours
) {
3104 publication
->working_hours_xml_str
= xmlnode_to_str(xn_working_hours
, NULL
);
3108 if (!cat_publications
) {
3109 cat_publications
= g_hash_table_new_full(
3110 g_str_hash
, g_str_equal
,
3111 g_free
, (GDestroyNotify
)free_publication
);
3112 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
3113 purple_debug_info("sipe", "sipe_process_roaming_self: added GHashTable cat=%s\n", name
);
3115 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
3116 purple_debug_info("sipe", "sipe_process_roaming_self: added key=%s version=%d\n", key
, version_int
);
3120 /* userProperties published by server from AD */
3121 if (!sip
->csta
&& !strcmp(name
, "userProperties")) {
3123 /* line, for Remote Call Control (RCC) */
3124 for (line
= xmlnode_get_descendant(node
, "userProperties", "lines", "line", NULL
); line
; line
= xmlnode_get_next_twin(line
)) {
3125 const gchar
*line_server
= xmlnode_get_attrib(line
, "lineServer");
3126 const gchar
*line_type
= xmlnode_get_attrib(line
, "lineType");
3129 if (!line_server
|| (strcmp(line_type
, "Rcc") && strcmp(line_type
, "Dual"))) continue;
3131 line_uri
= xmlnode_get_data(line
);
3133 purple_debug_info("sipe", "sipe_process_roaming_self: line_uri=%s server=%s\n", line_uri
, line_server
);
3134 sip_csta_open(sip
, line_uri
, line_server
);
3142 purple_debug_info("sipe", "sipe_process_roaming_self: sip->our_publications size=%d\n",
3143 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
3146 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3147 guint id
= atoi(xmlnode_get_attrib(node
, "id"));
3148 struct sipe_container
*container
= sipe_find_container(sip
, id
);
3151 sip
->containers
= g_slist_remove(sip
->containers
, container
);
3152 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
3153 free_container(container
);
3155 container
= g_new0(struct sipe_container
, 1);
3157 container
->version
= atoi(xmlnode_get_attrib(node
, "version"));
3158 sip
->containers
= g_slist_append(sip
->containers
, container
);
3159 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
3161 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
3162 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
3163 member
->type
= xmlnode_get_attrib(node2
, "type");
3164 member
->value
= xmlnode_get_attrib(node2
, "value");
3165 container
->members
= g_slist_append(container
->members
, member
);
3166 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
3167 member
->type
, member
->value
? member
->value
: "");
3171 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
3172 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
3173 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
3174 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
3175 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
3176 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
3177 /* initial set-up to let counterparties see your status */
3178 if (sameEnterpriseAL
< 0) {
3179 struct sipe_container
*container
= sipe_find_container(sip
, 200);
3180 guint version
= container
? container
->version
: 0;
3181 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
3183 if (federatedAL
< 0) {
3184 struct sipe_container
*container
= sipe_find_container(sip
, 100);
3185 guint version
= container
? container
->version
: 0;
3186 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
3188 sip
->access_level_set
= TRUE
;
3192 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3194 const char *acknowledged
;
3198 user
= xmlnode_get_attrib(node
, "user"); /* without 'sip:' prefix */
3199 if (!user
) continue;
3200 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
3201 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
3202 uri
= sip_uri_from_name(user
);
3204 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
3206 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
3207 if(!g_ascii_strcasecmp(acknowledged
,"false")){
3208 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
3209 if (!purple_find_buddy(sip
->account
, uri
)) {
3210 purple_account_request_add(sip
->account
, uri
, _("you"), display_name
, NULL
);
3213 hdr
= g_strdup_printf(
3215 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
3217 body
= g_strdup_printf(
3218 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
3219 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
3220 "</setSubscribers>", user
);
3222 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
3226 g_free(display_name
);
3234 /* Publish initial state if not yet.
3235 * Assuming this happens on initial responce to subscription to roaming-self
3236 * so we've already updated our roaming data in full.
3239 if (sip
->ocs2007
&& !sip
->initial_state_published
) {
3240 send_publish_category_initial(sip
);
3241 sip
->initial_state_published
= TRUE
;
3243 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3247 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
)
3249 gchar
*to
= sip_uri_self(sip
);
3250 gchar
*tmp
= get_contact(sip
);
3251 gchar
*hdr
= g_strdup_printf(
3252 "Event: vnd-microsoft-roaming-ACL\r\n"
3253 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
3254 "Supported: com.microsoft.autoextend\r\n"
3255 "Supported: ms-benotify\r\n"
3256 "Proxy-Require: ms-benotify\r\n"
3257 "Supported: ms-piggyback-first-notify\r\n"
3258 "Contact: %s\r\n", tmp
);
3261 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
3267 * To request for presence information about the user, access level settings that have already been configured by the user
3268 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
3269 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
3272 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
)
3274 gchar
*to
= sip_uri_self(sip
);
3275 gchar
*tmp
= get_contact(sip
);
3276 gchar
*hdr
= g_strdup_printf(
3277 "Event: vnd-microsoft-roaming-self\r\n"
3278 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
3279 "Supported: ms-benotify\r\n"
3280 "Proxy-Require: ms-benotify\r\n"
3281 "Supported: ms-piggyback-first-notify\r\n"
3283 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
3285 gchar
*body
=g_strdup(
3286 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
3287 "<roaming type=\"categories\"/>"
3288 "<roaming type=\"containers\"/>"
3289 "<roaming type=\"subscribers\"/></roamingList>");
3292 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3301 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
)
3303 gchar
*to
= sip_uri_self(sip
);
3304 gchar
*tmp
= get_contact(sip
);
3305 gchar
*hdr
= g_strdup_printf(
3306 "Event: vnd-microsoft-provisioning\r\n"
3307 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
3308 "Supported: com.microsoft.autoextend\r\n"
3309 "Supported: ms-benotify\r\n"
3310 "Proxy-Require: ms-benotify\r\n"
3311 "Supported: ms-piggyback-first-notify\r\n"
3313 "Contact: %s\r\n", tmp
);
3316 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
3321 /** Subscription for provisioning information to help with initial
3322 * configuration. This subscription is a one-time query (denoted by the Expires header,
3323 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
3324 * configuration, meeting policies, and policy settings that Communicator must enforce.
3325 * TODO: for what we need this information.
3328 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
)
3330 gchar
*to
= sip_uri_self(sip
);
3331 gchar
*tmp
= get_contact(sip
);
3332 gchar
*hdr
= g_strdup_printf(
3333 "Event: vnd-microsoft-provisioning-v2\r\n"
3334 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
3335 "Supported: com.microsoft.autoextend\r\n"
3336 "Supported: ms-benotify\r\n"
3337 "Proxy-Require: ms-benotify\r\n"
3338 "Supported: ms-piggyback-first-notify\r\n"
3341 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
3342 gchar
*body
= g_strdup(
3343 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
3344 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
3345 "<provisioningGroup name=\"ucPolicy\"/>"
3346 "</provisioningGroupList>");
3349 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3356 sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key
,
3357 gpointer value
, gpointer user_data
)
3359 struct sip_subscription
*subscription
= value
;
3360 struct sip_dialog
*dialog
= &subscription
->dialog
;
3361 struct sipe_account_data
*sip
= user_data
;
3362 gchar
*tmp
= get_contact(sip
);
3363 gchar
*hdr
= g_strdup_printf(
3366 "Contact: %s\r\n", subscription
->event
, tmp
);
3369 /* Rate limit to max. 25 requests per seconds */
3370 g_usleep(1000000 / 25);
3372 send_sip_request(sip
->gc
, "SUBSCRIBE", dialog
->with
, dialog
->with
, hdr
, NULL
, dialog
, NULL
);
3376 /* IM Session (INVITE and MESSAGE methods) */
3378 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
3380 get_end_points (struct sipe_account_data
*sip
,
3381 struct sip_session
*session
)
3385 if (session
== NULL
) {
3389 res
= g_strdup_printf("<sip:%s>", sip
->username
);
3391 SIPE_DIALOG_FOREACH
{
3393 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
3396 if (dialog
->theirepid
) {
3398 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
3401 } SIPE_DIALOG_FOREACH_END
;
3407 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_account_data
*sip
,
3409 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3411 gboolean ret
= TRUE
;
3413 if (msg
->response
!= 200) {
3414 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
3418 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
3424 * Asks UA/proxy about its capabilities.
3426 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
3428 gchar
*to
= sip_uri(who
);
3429 gchar
*contact
= get_contact(sip
);
3430 gchar
*request
= g_strdup_printf(
3431 "Accept: application/sdp\r\n"
3432 "Contact: %s\r\n", contact
);
3435 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
3442 sipe_notify_user(struct sipe_account_data
*sip
,
3443 struct sip_session
*session
,
3444 PurpleMessageFlags flags
,
3445 const gchar
*message
)
3447 PurpleConversation
*conv
;
3449 if (!session
->conv
) {
3450 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
3452 conv
= session
->conv
;
3454 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
3458 sipe_present_info(struct sipe_account_data
*sip
,
3459 struct sip_session
*session
,
3460 const gchar
*message
)
3462 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
3466 sipe_present_err(struct sipe_account_data
*sip
,
3467 struct sip_session
*session
,
3468 const gchar
*message
)
3470 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_ERROR
, message
);
3474 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
3475 struct sip_session
*session
,
3478 const gchar
*message
)
3480 char *msg
, *msg_tmp
, *msg_tmp2
;
3483 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
3484 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
3486 /* Service unavailable; Server Internal Error; Server Time-out */
3487 if (sip_error
== 503 || sip_error
== 500 || sip_error
== 504) {
3488 label
= _("This message was not delivered to %s because the service is not available");
3489 } else if (sip_error
== 486) { /* Busy Here */
3490 label
= _("This message was not delivered to %s because one or more recipients do not want to be disturbed");
3492 label
= _("This message was not delivered to %s because one or more recipients are offline");
3495 msg_tmp
= g_strdup_printf( "%s:\n%s" ,
3496 msg_tmp2
= g_strdup_printf(label
, who
? who
: ""), msg
? msg
: "");
3497 sipe_present_err(sip
, session
, msg_tmp
);
3504 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
);
3507 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
3508 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3510 gboolean ret
= TRUE
;
3511 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3512 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
3513 struct sip_dialog
*dialog
;
3519 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
3524 dialog
= sipe_dialog_find(session
, with
);
3526 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
3531 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3532 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
3534 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
3536 if (msg
->response
>= 400) {
3537 PurpleBuddy
*pbuddy
;
3538 gchar
*alias
= with
;
3540 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
3542 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
3543 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
3546 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, alias
, message
);
3549 gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
3551 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
));
3552 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
3553 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
3556 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3557 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
3558 key
, g_hash_table_size(session
->unconfirmed_messages
));
3564 if (ret
) sipe_im_process_queue(sip
, session
);
3569 sipe_is_election_finished(struct sip_session
*session
);
3572 sipe_election_result(struct sipe_account_data
*sip
,
3576 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
3577 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3579 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
3580 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3581 struct sip_dialog
*dialog
;
3582 struct sip_session
*session
;
3584 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3586 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
3590 if (msg
->response
== 200 && !strncmp(contenttype
, "application/x-ms-mim", 20)) {
3591 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3592 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
3593 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
3595 if (xn_request_rm_response
) {
3596 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
3597 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
3599 dialog
= sipe_dialog_find(session
, with
);
3601 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
3605 if (allow
&& !g_strcasecmp(allow
, "true")) {
3606 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
3607 dialog
->election_vote
= 1;
3608 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
3609 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
3610 dialog
->election_vote
= -1;
3613 if (sipe_is_election_finished(session
)) {
3614 sipe_election_result(sip
, session
);
3617 } else if (xn_set_rm_response
) {
3620 xmlnode_free(xn_action
);
3627 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
)
3636 sipe_parse_html(msg
, &msgformat
, &msgtext
);
3637 purple_debug_info("sipe", "sipe_send_message: msgformat=%s\n", msgformat
);
3639 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3642 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
3645 msgr
= g_strdup("");
3648 tmp
= get_contact(sip
);
3649 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
3650 //hdr = g_strdup("Content-Type: text/rtf\r\n");
3651 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
3652 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n", tmp
, msgr
);
3656 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
3663 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
3665 GSList
*entry2
= session
->outgoing_message_queue
;
3667 char *queued_msg
= entry2
->data
;
3669 /* for multiparty chat or conference */
3670 if (session
->is_multiparty
|| session
->focus_uri
) {
3671 gchar
*who
= sip_uri_self(sip
);
3672 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
3673 PURPLE_MESSAGE_SEND
, queued_msg
, time(NULL
));
3677 SIPE_DIALOG_FOREACH
{
3680 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
3682 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
3683 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
3684 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
3685 key
, g_hash_table_size(session
->unconfirmed_messages
));
3688 sipe_send_message(sip
, dialog
, queued_msg
);
3689 } SIPE_DIALOG_FOREACH_END
;
3691 entry2
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
3697 sipe_refer_notify(struct sipe_account_data
*sip
,
3698 struct sip_session
*session
,
3705 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3707 hdr
= g_strdup_printf(
3709 "Subscription-State: %s\r\n"
3710 "Content-Type: message/sipfrag\r\n",
3711 status
>= 200 ? "terminated" : "active");
3713 body
= g_strdup_printf(
3714 "SIP/2.0 %d %s\r\n",
3717 send_sip_request(sip
->gc
, "NOTIFY", who
, who
, hdr
, body
, dialog
, NULL
);
3724 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
3726 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3727 struct sip_session
*session
;
3728 struct sip_dialog
*dialog
;
3732 struct sipmsg
*request_msg
= trans
->msg
;
3734 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3737 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3739 session
= sipe_session_find_im(sip
, with
);
3742 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
3747 dialog
= sipe_dialog_find(session
, with
);
3749 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
3754 sipe_dialog_parse(dialog
, msg
, TRUE
);
3756 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3757 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
3759 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
3761 if (msg
->response
!= 200) {
3762 PurpleBuddy
*pbuddy
;
3763 gchar
*alias
= with
;
3765 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
3767 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
3768 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
3772 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, alias
, message
);
3774 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
3775 sipe_present_err(sip
, session
, tmp_msg
);
3779 sipe_dialog_remove(session
, with
);
3787 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
3788 dialog
->outgoing_invite
= NULL
;
3789 dialog
->is_established
= TRUE
;
3791 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
3793 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
3794 g_free(referred_by
);
3797 /* add user to chat if it is a multiparty session */
3798 if (session
->is_multiparty
) {
3799 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3801 PURPLE_CBFLAGS_NONE
, TRUE
);
3804 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
3805 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
3806 if (session
->outgoing_message_queue
) {
3807 char *queued_msg
= session
->outgoing_message_queue
->data
;
3808 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
3813 sipe_im_process_queue(sip
, session
);
3815 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3816 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
3817 key
, g_hash_table_size(session
->unconfirmed_messages
));
3826 sipe_invite(struct sipe_account_data
*sip
,
3827 struct sip_session
*session
,
3829 const gchar
*msg_body
,
3830 const gchar
*referred_by
,
3831 const gboolean is_triggered
)
3838 char *ms_text_format
= NULL
;
3839 gchar
*roster_manager
;
3841 gchar
*referred_by_str
;
3842 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3844 if (dialog
&& dialog
->is_established
) {
3845 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
3850 dialog
= sipe_dialog_add(session
);
3851 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
3852 dialog
->with
= g_strdup(who
);
3855 if (!(dialog
->ourtag
)) {
3856 dialog
->ourtag
= gentag();
3869 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
3870 purple_debug_info("sipe", "sipe_invite: msgformat=%s\n", msgformat
);
3872 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3876 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
3880 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
3881 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
3886 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
3887 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
3888 purple_debug_info("sipe", "sipe_invite: added message %s to unconfirmed_messages(count=%d)\n",
3889 key
, g_hash_table_size(session
->unconfirmed_messages
));
3893 contact
= get_contact(sip
);
3894 end_points
= get_end_points(sip
, session
);
3895 self
= sip_uri_self(sip
);
3896 roster_manager
= g_strdup_printf(
3897 "Roster-Manager: %s\r\n"
3898 "EndPoints: %s\r\n",
3901 referred_by_str
= referred_by
?
3903 "Referred-By: %s\r\n",
3906 hdr
= g_strdup_printf(
3907 "Supported: ms-sender\r\n"
3913 "Content-Type: application/sdp\r\n",
3914 (session
->roster_manager
&& !strcmp(session
->roster_manager
, self
)) ? roster_manager
: "",
3916 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
3917 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
3919 ms_text_format
? ms_text_format
: "");
3920 g_free(ms_text_format
);
3923 body
= g_strdup_printf(
3925 "o=- 0 0 IN IP4 %s\r\n"
3929 "m=%s %d sip null\r\n"
3930 "a=accept-types:text/plain text/html image/gif "
3931 "multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3932 purple_network_get_my_ip(-1),
3933 purple_network_get_my_ip(-1),
3934 sip
->ocs2007
? "message" : "x-ms-message",
3937 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
3938 to
, to
, hdr
, body
, dialog
, process_invite_response
);
3941 g_free(roster_manager
);
3943 g_free(referred_by_str
);
3950 sipe_refer(struct sipe_account_data
*sip
,
3951 struct sip_session
*session
,
3956 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
3957 session
->roster_manager
);
3959 contact
= get_contact(sip
);
3960 hdr
= g_strdup_printf(
3962 "Refer-to: <%s>\r\n"
3963 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
3964 "Require: com.microsoft.rtc-multiparty\r\n",
3968 dialog
->ourtag
? ";tag=" : "",
3969 dialog
->ourtag
? dialog
->ourtag
: "",
3972 send_sip_request(sip
->gc
, "REFER",
3973 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
3980 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
3981 struct sip_dialog
*dialog
,
3984 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
3986 gchar
*body
= g_strdup_printf(
3987 "<?xml version=\"1.0\"?>\r\n"
3988 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3989 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
3990 sip
->username
, bid
);
3992 send_sip_request(sip
->gc
, "INFO",
3993 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
3999 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
4000 struct sip_dialog
*dialog
)
4002 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4004 gchar
*body
= g_strdup_printf(
4005 "<?xml version=\"1.0\"?>\r\n"
4006 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4007 "<SetRM uri=\"sip:%s\"/></action>\r\n",
4010 send_sip_request(sip
->gc
, "INFO",
4011 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4017 sipe_session_close(struct sipe_account_data
*sip
,
4018 struct sip_session
* session
)
4020 if (session
&& session
->focus_uri
) {
4021 sipe_conf_immcu_closed(sip
, session
);
4022 conf_session_close(sip
, session
);
4026 SIPE_DIALOG_FOREACH
{
4027 /* @TODO slow down BYE message sending rate */
4028 /* @see single subscription code */
4029 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4030 } SIPE_DIALOG_FOREACH_END
;
4032 sipe_session_remove(sip
, session
);
4037 sipe_session_close_all(struct sipe_account_data
*sip
)
4040 while ((entry
= sip
->sessions
) != NULL
) {
4041 sipe_session_close(sip
, entry
->data
);
4046 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
4048 struct sipe_account_data
*sip
= gc
->proto_data
;
4050 purple_debug_info("sipe", "conversation with %s closed\n", who
);
4051 sipe_session_close(sip
, sipe_session_find_im(sip
, who
));
4055 sipe_chat_leave (PurpleConnection
*gc
, int id
)
4057 struct sipe_account_data
*sip
= gc
->proto_data
;
4058 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
4060 sipe_session_close(sip
, session
);
4063 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
4064 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4066 struct sipe_account_data
*sip
= gc
->proto_data
;
4067 struct sip_session
*session
;
4068 struct sip_dialog
*dialog
;
4069 gchar
*uri
= sip_uri(who
);
4071 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
4073 session
= sipe_session_find_or_add_im(sip
, uri
);
4074 dialog
= sipe_dialog_find(session
, uri
);
4076 // Queue the message
4077 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
4079 if (dialog
&& !dialog
->outgoing_invite
) {
4080 sipe_im_process_queue(sip
, session
);
4081 } else if (!dialog
|| !dialog
->outgoing_invite
) {
4082 // Need to send the INVITE to get the outgoing dialog setup
4083 sipe_invite(sip
, session
, uri
, what
, NULL
, FALSE
);
4090 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
4091 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4093 struct sipe_account_data
*sip
= gc
->proto_data
;
4094 struct sip_session
*session
;
4096 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
4098 session
= sipe_session_find_chat_by_id(sip
, id
);
4100 // Queue the message
4101 if (session
&& session
->dialogs
) {
4102 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
4104 sipe_im_process_queue(sip
, session
);
4106 gchar
*chat_name
= purple_find_chat(sip
->gc
, id
)->name
;
4107 const gchar
*proto_chat_id
= sipe_chat_find_name(chat_name
);
4109 purple_debug_info("sipe", "sipe_chat_send: chat_name='%s'\n", chat_name
? chat_name
: "NULL");
4110 purple_debug_info("sipe", "sipe_chat_send: proto_chat_id='%s'\n", proto_chat_id
? proto_chat_id
: "NULL");
4113 struct sip_session
*session
= sipe_session_add_chat(sip
);
4115 session
->is_multiparty
= FALSE
;
4116 session
->focus_uri
= g_strdup(proto_chat_id
);
4117 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
4119 sipe_invite_conf_focus(sip
, session
);
4126 /* End IM Session (INVITE and MESSAGE methods) */
4128 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4130 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4131 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4133 struct sip_session
*session
;
4135 purple_debug_info("sipe", "process_incoming_info: \n%s\n", msg
->body
? msg
->body
: "");
4137 /* Call Control protocol */
4138 if (g_str_has_prefix(contenttype
, "application/csta+xml"))
4140 process_incoming_info_csta(sip
, msg
);
4144 from
= parse_from(sipmsg_find_header(msg
, "From"));
4145 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4147 session
= sipe_session_find_im(sip
, from
);
4154 if (g_str_has_prefix(contenttype
, "application/x-ms-mim"))
4156 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4157 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
4158 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
4160 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
4162 if (xn_request_rm
) {
4163 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
4164 int bid
= atoi(xmlnode_get_attrib(xn_request_rm
, "bid"));
4165 gchar
*body
= g_strdup_printf(
4166 "<?xml version=\"1.0\"?>\r\n"
4167 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4168 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
4170 session
->bid
< bid
? "true" : "false");
4171 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4173 } else if (xn_set_rm
) {
4175 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
4176 g_free(session
->roster_manager
);
4177 session
->roster_manager
= g_strdup(rm
);
4179 body
= g_strdup_printf(
4180 "<?xml version=\"1.0\"?>\r\n"
4181 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4182 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
4184 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4187 xmlnode_free(xn_action
);
4192 /* looks like purple lacks typing notification for chat */
4193 if (!session
->is_multiparty
&& !session
->focus_uri
) {
4194 xmlnode
*xn_keyboard_activity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4195 const char *status
= xmlnode_get_attrib(xmlnode_get_child(xn_keyboard_activity
, "status"),
4197 if (status
&& !strcmp(status
, "type")) {
4198 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
4199 } else if (status
&& !strcmp(status
, "idle")) {
4200 serv_got_typing_stopped(sip
->gc
, from
);
4202 xmlnode_free(xn_keyboard_activity
);
4205 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4210 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4212 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4213 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4214 struct sip_session
*session
;
4215 struct sip_dialog
*dialog
;
4217 /* collect dialog identification
4218 * we need callid, ourtag and theirtag to unambiguously identify dialog
4220 /* take data before 'msg' will be modified by send_sip_response */
4221 dialog
= g_new0(struct sip_dialog
, 1);
4222 dialog
->callid
= g_strdup(callid
);
4223 dialog
->cseq
= parse_cseq(sipmsg_find_header(msg
, "CSeq"));
4224 dialog
->with
= g_strdup(from
);
4225 sipe_dialog_parse(dialog
, msg
, FALSE
);
4227 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4229 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4231 session
= sipe_session_find_im(sip
, from
);
4238 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
4239 g_free(session
->roster_manager
);
4240 session
->roster_manager
= NULL
;
4243 /* This what BYE is essentially for - terminating dialog */
4244 sipe_dialog_remove_3(session
, dialog
);
4245 sipe_dialog_free(dialog
);
4246 if (session
->focus_uri
&& !g_strcasecmp(from
, session
->im_mcu_uri
)) {
4247 sipe_conf_immcu_closed(sip
, session
);
4248 } else if (session
->is_multiparty
) {
4249 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
4255 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4257 gchar
*self
= sip_uri_self(sip
);
4258 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4259 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4260 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
4261 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
4262 struct sip_session
*session
;
4263 struct sip_dialog
*dialog
;
4265 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4266 dialog
= sipe_dialog_find(session
, from
);
4268 if (!session
|| !dialog
|| !session
->roster_manager
|| strcmp(session
->roster_manager
, self
)) {
4269 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
4271 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
4273 sipe_invite(sip
, session
, refer_to
, NULL
, referred_by
, FALSE
);
4279 g_free(referred_by
);
4283 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
4285 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
4286 struct sip_session
*session
;
4287 struct sip_dialog
*dialog
;
4289 if (state
== PURPLE_NOT_TYPING
)
4292 session
= sipe_session_find_im(sip
, who
);
4293 dialog
= sipe_dialog_find(session
, who
);
4295 if (session
&& dialog
&& dialog
->is_established
) {
4296 send_sip_request(gc
, "INFO", who
, who
,
4297 "Content-Type: application/xml\r\n",
4298 SIPE_SEND_TYPING
, dialog
, NULL
);
4300 return SIPE_TYPING_SEND_TIMEOUT
;
4303 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
4305 GSList
*tmp
= sip
->transactions
;
4306 time_t currtime
= time(NULL
);
4308 struct transaction
*trans
= tmp
->data
;
4310 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
4311 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
4314 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
4316 sendout_sipmsg(sip
, trans
->msg
);
4323 static void do_reauthenticate_cb(struct sipe_account_data
*sip
,
4324 SIPE_UNUSED_PARAMETER
void *unused
)
4326 /* register again when security token expires */
4327 /* we have to start a new authentication as the security token
4328 * is almost expired by sending a not signed REGISTER message */
4329 purple_debug_info("sipe", "do a full reauthentication\n");
4330 sipe_auth_free(&sip
->registrar
);
4331 sipe_auth_free(&sip
->proxy
);
4332 sip
->registerstatus
= 0;
4334 sip
->reauthenticate_set
= FALSE
;
4337 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4341 gboolean found
= FALSE
;
4343 from
= parse_from(sipmsg_find_header(msg
, "From"));
4347 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
4349 contenttype
= sipmsg_find_header(msg
, "Content-Type");
4350 if (!strncmp(contenttype
, "text/plain", 10)
4351 || !strncmp(contenttype
, "text/html", 9)
4352 || !strncmp(contenttype
, "multipart/related", 17)
4353 || !strncmp(contenttype
, "multipart/alternative", 21))
4355 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4356 gchar
*html
= get_html_message(contenttype
, msg
->body
);
4358 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
4360 session
= sipe_session_find_im(sip
, from
);
4363 if (session
&& session
->focus_uri
) { /* a conference */
4364 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
4365 gchar
*sender
= parse_from(tmp
);
4367 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
4368 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4370 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
4371 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
4372 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4374 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
4377 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4380 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
4381 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4386 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
4390 state
= xmlnode_get_child(isc
, "state");
4393 purple_debug_info("sipe", "process_incoming_message: no state found\n");
4398 statedata
= xmlnode_get_data(state
);
4400 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
4401 else serv_got_typing_stopped(sip
->gc
, from
);
4406 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4410 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4411 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
4413 session
= sipe_session_find_im(sip
, from
);
4416 gchar
*msg
= g_strdup_printf(_("Received a message with unrecognized contents from %s"),
4418 sipe_present_err(sip
, session
, msg
);
4422 purple_debug_info("sipe", "got unknown mime-type '%s'\n", contenttype
);
4423 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
4428 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4434 gboolean is_multiparty
= FALSE
;
4435 gboolean is_triggered
= FALSE
;
4436 gboolean was_multiparty
= TRUE
;
4437 gboolean just_joined
= FALSE
;
4439 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4440 gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
4441 gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
4442 gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
4443 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4444 GSList
*end_points
= NULL
;
4446 struct sip_session
*session
;
4448 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? tmp
= fix_newlines(msg
->body
) : "");
4451 /* Invitation to join conference */
4452 if (!strncmp(content_type
, "application/ms-conf-invite+xml", 30)) {
4453 process_incoming_invite_conf(sip
, msg
);
4457 /* Only accept text invitations */
4458 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
4459 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
4463 // TODO There *must* be a better way to clean up the To header to add a tag...
4464 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
4465 oldHeader
= sipmsg_find_header(msg
, "To");
4467 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
4468 sipmsg_remove_header_now(msg
, "To");
4469 sipmsg_add_header_now(msg
, "To", newHeader
);
4472 if (end_points_hdr
) {
4473 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
4475 if (g_slist_length(end_points
) > 2) {
4476 is_multiparty
= TRUE
;
4479 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
4480 is_triggered
= TRUE
;
4481 is_multiparty
= TRUE
;
4484 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4485 /* Convert to multiparty */
4486 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
4487 g_free(session
->with
);
4488 session
->with
= NULL
;
4489 was_multiparty
= FALSE
;
4490 session
->is_multiparty
= TRUE
;
4491 session
->chat_id
= rand();
4494 if (!session
&& is_multiparty
) {
4495 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
4498 from
= parse_from(sipmsg_find_header(msg
, "From"));
4500 session
= sipe_session_find_or_add_im(sip
, from
);
4503 g_free(session
->callid
);
4504 session
->callid
= g_strdup(callid
);
4506 session
->is_multiparty
= is_multiparty
;
4507 if (roster_manager
) {
4508 session
->roster_manager
= g_strdup(roster_manager
);
4511 if (is_multiparty
&& end_points
) {
4512 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
4513 GSList
*entry
= end_points
;
4515 struct sip_dialog
*dialog
;
4516 struct sipendpoint
*end_point
= entry
->data
;
4517 entry
= entry
->next
;
4519 if (!g_strcasecmp(from
, end_point
->contact
) ||
4520 !g_strcasecmp(to
, end_point
->contact
))
4523 dialog
= sipe_dialog_find(session
, end_point
->contact
);
4525 g_free(dialog
->theirepid
);
4526 dialog
->theirepid
= end_point
->epid
;
4527 end_point
->epid
= NULL
;
4529 dialog
= sipe_dialog_add(session
);
4531 dialog
->callid
= g_strdup(session
->callid
);
4532 dialog
->with
= end_point
->contact
;
4533 end_point
->contact
= NULL
;
4534 dialog
->theirepid
= end_point
->epid
;
4535 end_point
->epid
= NULL
;
4539 /* send triggered INVITE */
4540 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, TRUE
);
4547 GSList
*entry
= end_points
;
4549 struct sipendpoint
*end_point
= entry
->data
;
4550 entry
= entry
->next
;
4551 g_free(end_point
->contact
);
4552 g_free(end_point
->epid
);
4555 g_slist_free(end_points
);
4559 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
4561 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
4563 dialog
= sipe_dialog_add(session
);
4565 dialog
->callid
= g_strdup(session
->callid
);
4566 dialog
->with
= g_strdup(from
);
4567 sipe_dialog_parse(dialog
, msg
, FALSE
);
4569 if (!dialog
->ourtag
) {
4570 dialog
->ourtag
= newTag
;
4577 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
4581 if (is_multiparty
&& !session
->conv
) {
4582 gchar
*chat_title
= sipe_chat_get_name(callid
);
4583 gchar
*self
= sip_uri_self(sip
);
4584 /* create prpl chat */
4585 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_title
);
4586 session
->chat_title
= g_strdup(chat_title
);
4587 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
4589 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4591 PURPLE_CBFLAGS_NONE
, FALSE
);
4596 if (is_multiparty
&& !was_multiparty
) {
4597 /* add current IM counterparty to chat */
4598 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4599 sipe_dialog_first(session
)->with
, NULL
,
4600 PURPLE_CBFLAGS_NONE
, FALSE
);
4603 /* add inviting party to chat */
4604 if (just_joined
&& session
->conv
) {
4605 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4607 PURPLE_CBFLAGS_NONE
, TRUE
);
4610 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
4612 /* This used only in 2005 official client, not 2007 or Reuters.
4613 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
4614 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
4616 if (is_multiparty
) {
4617 /* please do not optimize logic inside as this code may be re-enabled for other cases */
4618 gchar
*ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
4619 if (ms_text_format
) {
4620 if (g_str_has_prefix(ms_text_format
, "text/plain") || g_str_has_prefix(ms_text_format
, "text/html")) {
4622 gchar
*html
= get_html_message(ms_text_format
, NULL
);
4624 if (is_multiparty
) {
4625 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
4626 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4628 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
4631 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
4640 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
4641 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
4642 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
4644 body
= g_strdup_printf(
4646 "o=- 0 0 IN IP4 %s\r\n"
4650 "m=%s %d sip sip:%s\r\n"
4651 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
4652 purple_network_get_my_ip(-1),
4653 purple_network_get_my_ip(-1),
4654 sip
->ocs2007
? "message" : "x-ms-message",
4657 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4661 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4665 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
4666 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
4667 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
4669 body
= g_strdup_printf(
4671 "o=- 0 0 IN IP4 0.0.0.0\r\n"
4673 "c=IN IP4 0.0.0.0\r\n"
4675 "m=%s %d sip sip:%s\r\n"
4676 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
4677 sip
->ocs2007
? "message" : "x-ms-message",
4680 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4684 static void sipe_connection_cleanup(struct sipe_account_data
*);
4685 static void create_connection(struct sipe_account_data
*, gchar
*, int);
4687 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4688 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
4691 const gchar
*expires_header
;
4693 GSList
*hdr
= msg
->headers
;
4694 struct siphdrelement
*elem
;
4696 expires_header
= sipmsg_find_header(msg
, "Expires");
4697 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
4698 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
4700 switch (msg
->response
) {
4703 sip
->registerstatus
= 0;
4705 gchar
*contact_hdr
= NULL
;
4710 gchar
*server_hdr
= sipmsg_find_header(msg
, "Server");
4712 if (!sip
->reregister_set
) {
4713 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
4714 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
4715 g_free(action_name
);
4716 sip
->reregister_set
= TRUE
;
4719 sip
->registerstatus
= 3;
4721 if (server_hdr
&& !sip
->server_version
) {
4722 sip
->server_version
= g_strdup(server_hdr
);
4728 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4730 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
4733 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4737 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
4738 fill_auth(tmp
, &sip
->registrar
);
4741 if (!sip
->reauthenticate_set
) {
4742 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
4743 guint reauth_timeout
;
4744 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
4745 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
4746 reauth_timeout
= sip
->registrar
.expires
- 300;
4748 /* NTLM: we have to reauthenticate as our security token expires
4749 after eight hours (be five minutes early) */
4750 reauth_timeout
= (8 * 3600) - 300;
4752 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
4753 g_free(action_name
);
4754 sip
->reauthenticate_set
= TRUE
;
4757 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
4759 epid
= get_epid(sip
);
4760 uuid
= generateUUIDfromEPID(epid
);
4763 // There can be multiple Contact headers (one per location where the user is logged in) so
4764 // make sure to only get the one for this uuid
4765 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
4766 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
4767 if (valid_contact
) {
4768 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
4769 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
4770 g_free(valid_contact
);
4773 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
4778 g_free(sip
->contact
);
4780 sip
->contact
= g_strdup_printf("<%s>", gruu
);
4783 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
4784 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
);
4786 sip
->ocs2007
= FALSE
;
4787 sip
->batched_support
= FALSE
;
4792 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
4793 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
4794 /* We interpret this as OCS2007+ indicator */
4795 sip
->ocs2007
= TRUE
;
4796 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s (indicates OCS2007+)\n", elem
->value
);
4798 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
4799 sip
->batched_support
= TRUE
;
4800 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s\n", elem
->value
);
4803 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
4804 gchar
**caps
= g_strsplit(elem
->value
,",",0);
4807 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
4808 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
4813 hdr
= g_slist_next(hdr
);
4816 /* rejoin open chats to be able to use them by continue to send messages */
4817 purple_conversation_foreach(sipe_rejoin_chat
);
4820 if (!sip
->subscribed
) { //do it just once, not every re-register
4822 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
4823 (GCompareFunc
)g_ascii_strcasecmp
)) {
4824 sipe_subscribe_roaming_contacts(sip
);
4827 /* For 2007+ it does not make sence to subscribe to:
4828 * vnd-microsoft-roaming-ACL
4829 * vnd-microsoft-provisioning (not v2)
4831 * These are for backward compatibility.
4835 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
4836 (GCompareFunc
)g_ascii_strcasecmp
)) {
4837 sipe_subscribe_roaming_self(sip
);
4839 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
4840 (GCompareFunc
)g_ascii_strcasecmp
)) {
4841 sipe_subscribe_roaming_provisioning_v2(sip
);
4844 /* For 2005- servers */
4847 //sipe_options_request(sip, sip->sipdomain);
4849 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
4850 (GCompareFunc
)g_ascii_strcasecmp
)) {
4851 sipe_subscribe_roaming_acl(sip
);
4853 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
4854 (GCompareFunc
)g_ascii_strcasecmp
)) {
4855 sipe_subscribe_roaming_provisioning(sip
);
4857 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
4858 (GCompareFunc
)g_ascii_strcasecmp
)) {
4859 sipe_subscribe_presence_wpending(sip
, msg
);
4862 /* For 2007+ we publish our initial statuses and calendar data only after
4863 * received our existing publications in sipe_process_roaming_self()
4864 * Only in this case we know versions of current publications made
4867 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
4869 sipe_schedule_action("<+update-calendar>",
4870 UPDATE_CALENDAR_DELAY
,
4871 (Action
)sipe_update_calendar
,
4877 sip
->subscribed
= TRUE
;
4880 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
4881 "timeout=", ";", NULL
);
4882 if (timeout
!= NULL
) {
4883 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
4884 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
4885 sip
->keepalive_timeout
);
4889 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\n", sip
->cseq
);
4894 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
4896 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
4897 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
4901 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
4904 tmp
= g_strsplit(parts
[0], ":", 0);
4905 hostname
= g_strdup(tmp
[0]);
4906 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
4910 tmp
= g_strsplit(parts
[i
], "=", 0);
4912 if (g_strcasecmp("transport", tmp
[0]) == 0) {
4913 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
4914 transport
= SIPE_TRANSPORT_TCP
;
4915 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
4916 transport
= SIPE_TRANSPORT_UDP
;
4925 /* Close old connection */
4926 sipe_connection_cleanup(sip
);
4928 /* Create new connection */
4929 sip
->transport
= transport
;
4930 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
4931 hostname
, port
, TRANSPORT_DESCRIPTOR
);
4932 create_connection(sip
, hostname
, port
);
4938 if (sip
->registerstatus
!= 2) {
4939 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
4940 if (sip
->registrar
.retries
> 3) {
4941 sip
->gc
->wants_to_die
= TRUE
;
4942 purple_connection_error(sip
->gc
, _("Wrong password"));
4946 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4948 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
4951 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4954 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
4955 fill_auth(tmp
, &sip
->registrar
);
4956 sip
->registerstatus
= 2;
4957 if (sip
->account
->disconnecting
) {
4958 do_register_exp(sip
, 0);
4966 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
4967 gchar
**reason
= NULL
;
4968 if (warning
!= NULL
) {
4970 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
4972 reason
= g_strsplit(warning
, "\"", 0);
4974 warning
= g_strdup_printf(_("You have been rejected by the server: %s"),
4975 (reason
&& reason
[1]) ? reason
[1] : _("no reason given"));
4978 sip
->gc
->wants_to_die
= TRUE
;
4979 purple_connection_error(sip
->gc
, warning
);
4986 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
4987 gchar
*reason
= NULL
;
4988 if (warning
!= NULL
) {
4989 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
4991 warning
= g_strdup_printf(_("Not found: %s. Please contact your Administrator"),
4992 warning
? (reason
? reason
: _("no reason given")) :
4993 _("SIP is either not enabled for the destination URI or it does not exist"));
4996 sip
->gc
->wants_to_die
= TRUE
;
4997 purple_connection_error(sip
->gc
, warning
);
5003 case 504: /* Server time-out */
5005 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
5006 gchar
*reason
= NULL
;
5007 if (warning
!= NULL
) {
5008 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
5010 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
5013 sip
->gc
->wants_to_die
= TRUE
;
5014 purple_connection_error(sip
->gc
, warning
);
5024 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
5027 sipe_get_status_by_availability(int avail
)
5032 status
= SIPE_STATUS_ID_OFFLINE
;
5033 else if (avail
< 4500)
5034 status
= SIPE_STATUS_ID_AVAILABLE
;
5035 else if (avail
< 6000)
5036 status
= SIPE_STATUS_ID_IDLE
;
5037 else if (avail
< 7500)
5038 status
= SIPE_STATUS_ID_BUSY
;
5039 else if (avail
< 9000)
5040 status
= SIPE_STATUS_ID_BUSYIDLE
;
5041 else if (avail
< 12000)
5042 status
= SIPE_STATUS_ID_DND
;
5043 else if (avail
< 15000)
5044 status
= SIPE_STATUS_ID_BRB
;
5045 else if (avail
< 18000)
5046 status
= SIPE_STATUS_ID_AWAY
;
5048 status
= SIPE_STATUS_ID_OFFLINE
;
5053 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5056 xmlnode
*xn_categories
;
5057 xmlnode
*xn_category
;
5060 xn_categories
= xmlnode_from_str(data
, len
);
5061 uri
= xmlnode_get_attrib(xn_categories
, "uri"); /* with 'sip:' prefix */
5063 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
5065 xn_category
= xmlnode_get_next_twin(xn_category
) )
5067 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
5070 if (!strcmp(attrVar
, "contactCard"))
5073 /* identity - Display Name and email */
5074 node
= xmlnode_get_descendant(xn_category
, "contactCard", "identity", NULL
);
5076 char* display_name
= xmlnode_get_data(
5077 xmlnode_get_descendant(node
, "name", "displayName", NULL
));
5078 char* email
= xmlnode_get_data(
5079 xmlnode_get_child(node
, "email"));
5081 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5082 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
5084 g_free(display_name
);
5088 node
= xmlnode_get_descendant(xn_category
, "contactCard", "company", NULL
);
5090 char* company
= xmlnode_get_data(node
);
5091 sipe_update_user_info(sip
, uri
, COMPANY_PROP
, company
);
5095 node
= xmlnode_get_descendant(xn_category
, "contactCard", "department", NULL
);
5097 char* department
= xmlnode_get_data(node
);
5098 sipe_update_user_info(sip
, uri
, DEPARTMENT_PROP
, department
);
5102 node
= xmlnode_get_descendant(xn_category
, "contactCard", "title", NULL
);
5104 char* title
= xmlnode_get_data(node
);
5105 sipe_update_user_info(sip
, uri
, TITLE_PROP
, title
);
5109 node
= xmlnode_get_descendant(xn_category
, "contactCard", "office", NULL
);
5111 char* office
= xmlnode_get_data(node
);
5112 sipe_update_user_info(sip
, uri
, OFFICE_PROP
, office
);
5116 node
= xmlnode_get_descendant(xn_category
, "contactCard", "url", NULL
);
5118 char* site
= xmlnode_get_data(node
);
5119 sipe_update_user_info(sip
, uri
, SITE_PROP
, site
);
5123 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "phone", NULL
);
5125 node
= xmlnode_get_next_twin(node
))
5127 const char *phone_type
= xmlnode_get_attrib(node
, "type");
5128 char* phone
= xmlnode_get_data(xmlnode_get_child(node
, "uri"));
5129 char* phone_display_string
= xmlnode_get_data(xmlnode_get_child(node
, "displayString"));
5131 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, phone_display_string
);
5134 g_free(phone_display_string
);
5137 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "address", NULL
);
5139 node
= xmlnode_get_next_twin(node
))
5141 if (!strcmp(xmlnode_get_attrib(node
, "type"), "work")) {
5142 char* street
= xmlnode_get_data(xmlnode_get_child(node
, "street"));
5143 char* city
= xmlnode_get_data(xmlnode_get_child(node
, "city"));
5144 char* state
= xmlnode_get_data(xmlnode_get_child(node
, "state"));
5145 char* zipcode
= xmlnode_get_data(xmlnode_get_child(node
, "zipcode"));
5146 char* country_code
= xmlnode_get_data(xmlnode_get_child(node
, "countryCode"));
5148 sipe_update_user_info(sip
, uri
, ADDRESS_STREET_PROP
, street
);
5149 sipe_update_user_info(sip
, uri
, ADDRESS_CITY_PROP
, city
);
5150 sipe_update_user_info(sip
, uri
, ADDRESS_STATE_PROP
, state
);
5151 sipe_update_user_info(sip
, uri
, ADDRESS_ZIPCODE_PROP
, zipcode
);
5152 sipe_update_user_info(sip
, uri
, ADDRESS_COUNTRYCODE_PROP
, country_code
);
5158 g_free(country_code
);
5165 else if (!strcmp(attrVar
, "note"))
5168 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
5173 xn_node
= xmlnode_get_child(xn_category
, "note");
5174 if (!xn_node
) continue;
5175 xn_node
= xmlnode_get_child(xn_node
, "body");
5176 if (!xn_node
) continue;
5177 note
= xmlnode_get_data(xn_node
);
5178 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s),note(%s)\n",uri
,note
? note
: "");
5179 g_free(sbuddy
->annotation
);
5180 sbuddy
->annotation
= NULL
;
5181 if (note
) sbuddy
->annotation
= g_strdup(note
);
5188 else if(!strcmp(attrVar
, "state"))
5193 xmlnode
*xn_availability
;
5194 xmlnode
*xn_activity
;
5195 xmlnode
*xn_meeting_subject
;
5196 xmlnode
*xn_meeting_location
;
5197 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
5199 xn_node
= xmlnode_get_child(xn_category
, "state");
5200 if (!xn_node
) continue;
5201 xn_availability
= xmlnode_get_child(xn_node
, "availability");
5202 if (!xn_availability
) continue;
5203 xn_activity
= xmlnode_get_child(xn_node
, "activity");
5204 xn_meeting_subject
= xmlnode_get_child(xn_node
, "meetingSubject");
5205 xn_meeting_location
= xmlnode_get_child(xn_node
, "meetingLocation");
5207 data
= xmlnode_get_data(xn_availability
);
5208 availability
= atoi(data
);
5211 /* activity, meeting_subject, meeting_location */
5214 g_free(sbuddy
->activity
);
5215 sbuddy
->activity
= NULL
;
5217 const char *token
= xmlnode_get_attrib(xn_activity
, "token");
5218 xmlnode
*xn_custom
= xmlnode_get_child(xn_activity
, "custom");
5221 if (!is_empty(token
)) {
5222 if (!strcmp(token
, "on-the-phone")) {
5223 sbuddy
->activity
= g_strdup(_("On the phone"));
5224 } else if (!strcmp(token
, "in-a-conference")) {
5225 sbuddy
->activity
= g_strdup(_("In a conference"));
5226 } else if (!strcmp(token
, "in-a-meeting")) {
5227 sbuddy
->activity
= g_strdup(_("In a meeting"));
5228 } else if (!strcmp(token
, "out-of-office")) {
5229 sbuddy
->activity
= g_strdup(_("Out of office"));
5230 } else if (!strcmp(token
, "urgent-interruptions-only")) {
5231 sbuddy
->activity
= g_strdup(_("Urgent interruptions only"));
5234 /* form custom element */
5236 char *custom
= xmlnode_get_data(xn_custom
);
5238 if (!is_empty(custom
)) {
5239 sbuddy
->activity
= custom
;
5245 /* meeting_subject */
5246 g_free(sbuddy
->meeting_subject
);
5247 sbuddy
->meeting_subject
= NULL
;
5248 if (xn_meeting_subject
) {
5249 char *meeting_subject
= xmlnode_get_data(xn_meeting_subject
);
5251 if (!is_empty(meeting_subject
)) {
5252 sbuddy
->meeting_subject
= meeting_subject
;
5253 meeting_subject
= NULL
;
5255 g_free(meeting_subject
);
5257 /* meeting_location */
5258 g_free(sbuddy
->meeting_location
);
5259 sbuddy
->meeting_location
= NULL
;
5260 if (xn_meeting_location
) {
5261 char *meeting_location
= xmlnode_get_data(xn_meeting_location
);
5263 if (!is_empty(meeting_location
)) {
5264 sbuddy
->meeting_location
= meeting_location
;
5265 meeting_location
= NULL
;
5267 g_free(meeting_location
);
5271 status
= sipe_get_status_by_availability(availability
);
5273 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", status
);
5274 sipe_got_user_status(sip
, uri
, status
);
5278 else if(!strcmp(attrVar
, "calendarData"))
5280 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
5281 xmlnode
*xn_free_busy
= xmlnode_get_descendant(xn_category
, "calendarData", "freeBusy", NULL
);
5282 xmlnode
*xn_working_hours
= xmlnode_get_descendant(xn_category
, "calendarData", "WorkingHours", NULL
);
5284 if (sbuddy
&& xn_free_busy
) {
5285 g_free(sbuddy
->cal_start_time
);
5286 sbuddy
->cal_start_time
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
5288 sbuddy
->cal_granularity
= !g_ascii_strcasecmp(xmlnode_get_attrib(xn_free_busy
, "granularity"), "PT15M") ?
5291 g_free(sbuddy
->cal_free_busy_base64
);
5292 sbuddy
->cal_free_busy_base64
= xmlnode_get_data(xn_free_busy
);
5294 g_free(sbuddy
->cal_free_busy
);
5295 sbuddy
->cal_free_busy
= NULL
;
5297 purple_debug_info("sipe", "process_incoming_notify_rlmi: startTime=%s granularity=%d cal_free_busy_base64=\n%s\n", sbuddy
->cal_start_time
, sbuddy
->cal_granularity
, sbuddy
->cal_free_busy_base64
);
5300 if (sbuddy
&& xn_working_hours
) {
5301 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
5306 xmlnode_free(xn_categories
);
5309 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
5311 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
5312 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
5313 payload
->host
= g_strdup(host
);
5314 payload
->buddies
= server
;
5315 sipe_subscribe_presence_batched_routed(sip
, payload
);
5316 sipe_subscribe_presence_batched_routed_free(payload
);
5319 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5322 xmlnode
*xn_resource
;
5323 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
5328 xn_list
= xmlnode_from_str(data
, len
);
5330 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
5332 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
5334 const char *uri
, *state
;
5335 xmlnode
*xn_instance
;
5337 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
5338 if (!xn_instance
) continue;
5340 uri
= xmlnode_get_attrib(xn_resource
, "uri");
5341 state
= xmlnode_get_attrib(xn_instance
, "state");
5342 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
5344 if (strstr(state
, "resubscribe")) {
5345 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
5347 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
5348 gchar
*user
= g_strdup(uri
);
5349 host
= g_strdup(poolFqdn
);
5350 server
= g_hash_table_lookup(servers
, host
);
5351 server
= g_slist_append(server
, user
);
5352 g_hash_table_insert(servers
, host
, server
);
5354 sipe_subscribe_presence_single(sip
, (void *) uri
);
5359 /* Send out any deferred poolFqdn subscriptions */
5360 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
5361 g_hash_table_destroy(servers
);
5363 xmlnode_free(xn_list
);
5366 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5370 gchar
*activity
= NULL
;
5372 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
5373 gboolean isonline
= FALSE
;
5374 xmlnode
*display_name_node
;
5376 pidf
= xmlnode_from_str(data
, len
);
5378 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data
);
5382 uri
= sip_uri(xmlnode_get_attrib(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
5384 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
5386 if ((status
= xmlnode_get_child(tuple
, "status"))) {
5387 basicstatus
= xmlnode_get_child(status
, "basic");
5392 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
5397 getbasic
= xmlnode_get_data(basicstatus
);
5399 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
5404 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
5405 if (strstr(getbasic
, "open")) {
5410 display_name_node
= xmlnode_get_child(pidf
, "display-name");
5411 if (display_name_node
) {
5412 char * display_name
= xmlnode_get_data(display_name_node
);
5414 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5415 g_free(display_name
);
5418 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
5419 if ((status
= xmlnode_get_child(tuple
, "status"))) {
5420 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
5421 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
5422 activity
= xmlnode_get_data(basicstatus
);
5423 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
5430 const gchar
* status_id
= NULL
;
5432 if (strstr(activity
, "busy")) {
5433 status_id
= SIPE_STATUS_ID_BUSY
;
5434 } else if (strstr(activity
, "away")) {
5435 status_id
= SIPE_STATUS_ID_AWAY
;
5440 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5443 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
5444 sipe_got_user_status(sip
, uri
, status_id
);
5446 sipe_got_user_status(sip
, uri
, SIPE_STATUS_ID_OFFLINE
);
5454 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5456 const char *activity
= NULL
;
5458 const char *status_id
= NULL
;
5463 const char *device_name
= NULL
;
5464 const char *cal_start_time
= NULL
;
5465 const char *cal_granularity
= NULL
;
5466 char *cal_free_busy_base64
= NULL
;
5467 struct sipe_buddy
*sbuddy
;
5469 xmlnode
*xn_presentity
;
5470 xmlnode
*xn_availability
;
5471 xmlnode
*xn_activity
;
5472 xmlnode
*xn_display_name
;
5474 xmlnode
*xn_phone_number
;
5475 xmlnode
*xn_userinfo
;
5477 xmlnode
*xn_contact
;
5480 char *free_activity
;
5482 /* fix for Reuters environment on Linux */
5483 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
5485 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
5486 xn_presentity
= xmlnode_from_str(tmp_data
, strlen(tmp_data
));
5489 xn_presentity
= xmlnode_from_str(data
, len
);
5492 xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
5493 xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
5494 xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
5495 xn_email
= xmlnode_get_child(xn_presentity
, "email");
5496 xn_phone_number
= xmlnode_get_child(xn_presentity
, "phoneNumber");
5497 xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
5498 xn_oof
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "oof") : NULL
;
5500 xn_contact
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "contact") : NULL
;
5501 xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
5502 note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
5504 free_activity
= NULL
;
5506 name
= xmlnode_get_attrib(xn_presentity
, "uri"); /* without 'sip:' prefix */
5507 uri
= sip_uri_from_name(name
);
5508 avl
= atoi(xmlnode_get_attrib(xn_availability
, "aggregate"));
5509 epid
= xmlnode_get_attrib(xn_availability
, "epid");
5510 act
= atoi(xmlnode_get_attrib(xn_activity
, "aggregate"));
5512 if (xn_display_name
) {
5513 char *display_name
= g_strdup(xmlnode_get_attrib(xn_display_name
, "displayName"));
5514 char *email
= xn_email
? g_strdup(xmlnode_get_attrib(xn_email
, "email")) : NULL
;
5515 char *phone_label
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "label")) : NULL
;
5516 char *phone_number
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "number")) : NULL
;
5517 char *tel_uri
= sip_to_tel_uri(phone_number
);
5519 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5520 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
5521 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
5522 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, !is_empty(phone_label
) ? phone_label
: phone_number
);
5525 g_free(phone_label
);
5526 g_free(phone_number
);
5528 g_free(display_name
);
5533 for (node
= xmlnode_get_child(xn_contact
, "tel"); node
; node
= xmlnode_get_next_twin(node
))
5535 /* Ex.: <tel type="work">tel:+3222220000</tel> */
5536 const char *phone_type
= xmlnode_get_attrib(node
, "type");
5537 char* phone
= xmlnode_get_data(node
);
5539 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, NULL
);
5547 activity
= _("Out of office");
5550 /* devicePresence */
5551 for (node
= xmlnode_get_descendant(xn_presentity
, "devices", "devicePresence", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
5552 xmlnode
*xn_device_name
;
5553 xmlnode
*xn_calendar_info
;
5557 if (strcmp(xmlnode_get_attrib(node
, "epid"), epid
)) continue;
5559 xn_device_name
= xmlnode_get_child(node
, "deviceName");
5560 device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
5562 xn_calendar_info
= xmlnode_get_child(node
, "calendarInfo");
5563 if (xn_calendar_info
) {
5564 cal_start_time
= xmlnode_get_attrib(xn_calendar_info
, "startTime");
5565 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
5566 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
5568 purple_debug_info("sipe", "process_incoming_notify_msrtc: startTime=%s granularity=%s cal_free_busy_base64=\n%s\n", cal_start_time
, cal_granularity
, cal_free_busy_base64
);
5572 xn_state
= xmlnode_get_descendant(node
, "states", "state", NULL
);
5573 state
= xn_state
? xmlnode_get_data(xn_state
) : NULL
;
5575 if (!is_empty(state
)) {
5576 if (!strcmp(state
, "on-the-phone")) {
5577 activity
= _("On the phone");
5578 } else if (!strcmp(state
, "presenting")) {
5579 activity
= _("In a conference");
5581 activity
= free_activity
= state
;
5589 /* [MS-SIP] 2.2.1 */
5591 status_id
= SIPE_STATUS_ID_AWAY
;
5593 status_id
= SIPE_STATUS_ID_LUNCH
;
5595 status_id
= SIPE_STATUS_ID_IDLE
;
5597 status_id
= SIPE_STATUS_ID_BRB
;
5599 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5601 status_id
= SIPE_STATUS_ID_ONPHONE
;
5603 status_id
= SIPE_STATUS_ID_BUSY
;
5605 status_id
= SIPE_STATUS_ID_AWAY
;
5607 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5610 status_id
= SIPE_STATUS_ID_OFFLINE
;
5613 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
5616 g_free(sbuddy
->activity
);
5617 sbuddy
->activity
= NULL
;
5618 if (!is_empty(activity
)) { sbuddy
->activity
= g_strdup(activity
); }
5620 g_free(sbuddy
->annotation
);
5621 sbuddy
->annotation
= NULL
;
5622 if (!is_empty(note
)) { sbuddy
->annotation
= g_strdup(note
); }
5624 g_free(sbuddy
->device_name
);
5625 sbuddy
->device_name
= NULL
;
5626 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
5628 if (!is_empty(cal_free_busy_base64
)) {
5629 g_free(sbuddy
->cal_start_time
);
5630 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
5632 sbuddy
->cal_granularity
= !g_ascii_strcasecmp(cal_granularity
, "PT15M") ? 15 : 0;
5634 g_free(sbuddy
->cal_free_busy_base64
);
5635 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
5637 g_free(sbuddy
->cal_free_busy
);
5638 sbuddy
->cal_free_busy
= NULL
;
5642 if (free_activity
) g_free(free_activity
);
5644 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", status_id
);
5645 sipe_got_user_status(sip
, uri
, status_id
);
5647 xmlnode_free(xn_presentity
);
5651 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5653 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
5655 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
5657 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
5658 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
5660 const char *content
= msg
->body
;
5661 unsigned length
= msg
->bodylen
;
5662 PurpleMimeDocument
*mime
= NULL
;
5664 if (strstr(ctype
, "multipart"))
5666 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
5667 const char *content_type
;
5669 mime
= purple_mime_document_parse(doc
);
5670 parts
= purple_mime_document_get_parts(mime
);
5672 content
= purple_mime_part_get_data(parts
->data
);
5673 length
= purple_mime_part_get_length(parts
->data
);
5674 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
5675 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
5677 process_incoming_notify_rlmi_resub(sip
, content
, length
);
5679 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
5681 process_incoming_notify_msrtc(sip
, content
, length
);
5685 process_incoming_notify_rlmi(sip
, content
, length
);
5687 parts
= parts
->next
;
5693 purple_mime_document_free(mime
);
5696 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
5698 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
5700 else if(strstr(ctype
, "application/rlmi+xml"))
5702 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
5705 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
5707 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
5711 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
5715 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
5717 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
5718 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
5720 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
5723 strstr(ctype
, "multipart") &&
5724 (strstr(ctype
, "application/rlmi+xml") ||
5725 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
5726 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
5727 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
5728 GList
*parts
= purple_mime_document_get_parts(mime
);
5729 GSList
*buddies
= NULL
;
5730 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
5733 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
5734 purple_mime_part_get_length(parts
->data
));
5736 if (strcmp(xml
->name
, "list")) {
5737 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
5739 buddies
= g_slist_append(buddies
, uri
);
5743 parts
= parts
->next
;
5746 if (mime
) purple_mime_document_free(mime
);
5748 payload
->host
= g_strdup(who
);
5749 payload
->buddies
= buddies
;
5750 sipe_schedule_action(action_name
, timeout
,
5751 sipe_subscribe_presence_batched_routed
,
5752 sipe_subscribe_presence_batched_routed_free
,
5754 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
5757 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
5758 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
5760 g_free(action_name
);
5764 * Dispatcher for all incoming subscription information
5765 * whether it comes from NOTIFY, BENOTIFY requests or
5766 * piggy-backed to subscription's OK responce.
5768 * @param request whether initiated from BE/NOTIFY request or OK-response message.
5769 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
5771 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
5773 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
5774 gchar
*event
= sipmsg_find_header(msg
, "Event");
5775 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
5779 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n",
5781 tmp
= fix_newlines(msg
->body
));
5783 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n", subscription_state
? subscription_state
: "");
5785 /* implicit subscriptions */
5786 if (content_type
&& purple_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
5787 sipe_process_imdn(sip
, msg
);
5792 const gchar
*expires_header
;
5793 expires_header
= sipmsg_find_header(msg
, "Expires");
5794 timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
5795 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n", timeout
);
5796 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
; // 2 min ahead of expiration
5799 /* for one off subscriptions (send with Expire: 0) */
5800 if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning-v2"))
5802 sipe_process_provisioning_v2(sip
, msg
);
5804 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning"))
5806 sipe_process_provisioning(sip
, msg
);
5809 if (!subscription_state
|| strstr(subscription_state
, "active"))
5811 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
5813 sipe_process_presence(sip
, msg
);
5815 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
5817 sipe_process_roaming_contacts(sip
, msg
);
5819 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self"))
5821 sipe_process_roaming_self(sip
, msg
);
5823 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
5825 sipe_process_roaming_acl(sip
, msg
);
5827 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
5829 sipe_process_presence_wpending(sip
, msg
);
5831 else if (event
&& !g_ascii_strcasecmp(event
, "conference"))
5833 sipe_process_conference(sip
, msg
);
5837 /* The server sends status 'terminated' */
5838 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
5839 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
5840 gchar
*key
= sipe_get_subscription_key(event
, who
);
5842 purple_debug_info("sipe", "process_incoming_notify: server says that subscription to %s was terminated.\n", who
);
5845 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
5846 g_hash_table_remove(sip
->subscriptions
, key
);
5847 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
5853 if (timeout
&& event
) {// For LSC 2005 and OCS 2007
5854 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
5855 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
5857 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
5858 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_contacts, NULL, sip, msg);
5859 g_free(action_name);
5861 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
5862 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
5864 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
5865 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_acl, NULL, sip, msg);
5866 g_free(action_name);
5869 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
5870 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
5872 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
5873 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
5874 g_free(action_name
);
5876 else if (!g_ascii_strcasecmp(event
, "presence") &&
5877 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
5879 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
5880 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
5881 if (sip
->batched_support
) {
5882 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
5885 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
5886 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
, timeout
);
5888 g_free(action_name
);
5893 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
5895 sipe_process_registration_notify(sip
, msg
);
5898 /* The client responses on received a NOTIFY message */
5899 if (request
&& !benotify
)
5901 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5906 send_presence_soap(struct sipe_account_data
*sip
,
5908 gboolean do_publish_calendar
)
5910 struct sipe_ews
* ews
= sip
->ews
;
5911 int availability
= 300; /* online */
5912 int activity
= 400; /* Available */
5916 const gchar
*note_pub
= NULL
;
5917 gchar
*calendar_data
= NULL
;
5919 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
)) {
5921 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
5923 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
5925 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
5927 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
5929 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
5931 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_INVISIBLE
) ||
5932 !strcmp(sip
->status
, SIPE_STATUS_ID_OFFLINE
)) {
5933 availability
= 0; /* offline */
5936 activity
= 400; /* available */
5939 if (do_publish_calendar
&&
5940 ews
&& (!is_empty(ews
->legacy_dn
) || !is_empty(ews
->email
)) && ews
->fb_start
&& !is_empty(ews
->free_busy
))
5942 char *fb_start_str
= g_strdup(purple_utf8_strftime(SIPE_XML_DATE_PATTERN
, gmtime(&ews
->fb_start
)));
5943 char *free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
5944 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
5945 !is_empty(ews
->legacy_dn
) ? ews
->legacy_dn
: ews
->email
,
5948 g_free(fb_start_str
);
5949 g_free(free_busy_base64
);
5952 if (ews
&& ews
->oof_note
) {
5953 note_pub
= ews
->oof_note
;
5958 //@TODO: send user data - state;
5959 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
5963 (tmp
= g_ascii_strup(sipe_get_host_name(), -1)),
5964 note_pub
? (tmp2
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, note_pub
)) : "",
5965 ews
&& ews
->oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "",
5966 calendar_data
? calendar_data
: "");
5969 send_soap_request(sip
, body
);
5974 process_send_presence_category_publish_response(struct sipe_account_data
*sip
,
5976 struct transaction
*trans
)
5978 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
5980 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
5986 gboolean has_device_publication
= FALSE
;
5988 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5990 /* test if version mismatch fault */
5991 fault_code
= xmlnode_get_data(xmlnode_get_child(xml
, "Faultcode"));
5992 if (strcmp(fault_code
, "Client.BadCall.WrongDelta")) {
5993 purple_debug_info("sipe", "process_send_presence_category_publish_response: unsupported fault code:%s returning.\n", fault_code
);
6000 /* accumulating information about faulty versions */
6001 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
6002 for (node
= xmlnode_get_descendant(xml
, "details", "operation", NULL
);
6004 node
= xmlnode_get_next_twin(node
))
6006 const gchar
*index
= xmlnode_get_attrib(node
, "index");
6007 const gchar
*curVersion
= xmlnode_get_attrib(node
, "curVersion");
6009 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
6010 purple_debug_info("sipe", "fault added: index:%s curVersion:%s\n", index
, curVersion
);
6014 /* here we are parsing own request to figure out what publication
6015 * referensed here only by index went wrong
6017 xml
= xmlnode_from_str(trans
->msg
->body
, trans
->msg
->bodylen
);
6020 for (node
= xmlnode_get_descendant(xml
, "publications", "publication", NULL
),
6021 index_our
= 1; /* starts with 1 - our first publication */
6023 node
= xmlnode_get_next_twin(node
), index_our
++)
6025 gchar
*idx
= g_strdup_printf("%d", index_our
);
6026 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
6027 const gchar
*categoryName
= xmlnode_get_attrib(node
, "categoryName");
6030 if (!strcmp("device", categoryName
)) {
6031 has_device_publication
= TRUE
;
6034 if (curVersion
) { /* fault exist on this index */
6035 const gchar
*container
= xmlnode_get_attrib(node
, "container");
6036 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
6037 /* key is <category><instance><container> */
6038 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
6039 struct sipe_publication
*publication
=
6040 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, categoryName
), key
);
6042 purple_debug_info("sipe", "key is %s\n", key
);
6045 purple_debug_info("sipe", "Updating %s with version %s. Was %d before.\n",
6046 key
, curVersion
, publication
->version
);
6047 /* updating publication's version to the correct one */
6048 publication
->version
= atoi(curVersion
);
6054 g_hash_table_destroy(faults
);
6056 /* rebublishing with right versions */
6057 if (has_device_publication
) {
6058 send_publish_category_initial(sip
);
6060 send_presence_status(sip
);
6067 * Returns 'device' XML part for publication.
6068 * Must be g_free'd after use.
6071 sipe_publish_get_category_device(struct sipe_account_data
*sip
)
6075 gchar
*epid
= get_epid(sip
);
6076 gchar
*uuid
= generateUUIDfromEPID(epid
);
6077 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
6078 /* key is <category><instance><container> */
6079 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
6080 struct sipe_publication
*publication
=
6081 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
6086 uri
= sip_uri_self(sip
);
6087 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
6089 publication
? publication
->version
: 0,
6092 "00:00:00+01:00", /* @TODO make timezone real*/
6093 sipe_get_host_name()
6103 * A service method - use
6104 * - send_publish_get_category_state_machine and
6105 * - send_publish_get_category_state_user instead.
6106 * Must be g_free'd after use.
6109 sipe_publish_get_category_state(struct sipe_account_data
*sip
,
6110 gboolean is_user_state
)
6113 guint instance
= is_user_state
? sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
) :
6114 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
6115 /* key is <category><instance><container> */
6116 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
6117 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
6118 struct sipe_publication
*publication_2
=
6119 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
6120 struct sipe_publication
*publication_3
=
6121 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
6126 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
) ||
6127 !strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
6128 availability
= 15500;
6129 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
6130 availability
= 12500;
6131 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_DND
)) {
6132 availability
= 9500;
6133 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
) ||
6134 !strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
6135 availability
= 6500;
6136 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
6137 availability
= 3500;
6138 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_UNKNOWN
)) {
6141 // Offline or invisible
6142 availability
= 18500;
6145 if (publication_2
&& (publication_2
->availability
== availability
))
6147 purple_debug_info("sipe", "sipe_publish_get_category_state: state has NOT changed. Exiting.\n");
6148 return NULL
; /* nothing to update */
6151 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
6153 publication_2
? publication_2
->version
: 0,
6156 publication_3
? publication_3
->version
: 0,
6161 * Only Busy and OOF calendar event are published.
6162 * Different instances are used for that.
6164 * Must be g_free'd after use.
6167 sipe_publish_get_category_state_calendar(struct sipe_account_data
*sip
,
6168 struct sipe_cal_event
*event
,
6172 gchar
*start_time_str
;
6173 int availability
= 0;
6175 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
6176 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
) :
6177 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
6179 /* key is <category><instance><container> */
6180 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
6181 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
6182 struct sipe_publication
*publication_2
=
6183 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
6184 struct sipe_publication
*publication_3
=
6185 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
6190 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
6191 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
6192 "Exiting as no publication and no event for cal_satus:%d\n", cal_satus
);
6198 (publication_3
->availability
== availability
) &&
6199 !strcmp(publication_3
->cal_event_hash
, sipe_cal_event_hash(event
)))
6201 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
6202 "cal state has NOT changed for cal_satus:%d. Exiting.\n", cal_satus
);
6203 return NULL
; /* nothing to update */
6207 (event
->cal_status
== SIPE_CAL_BUSY
||
6208 event
->cal_status
== SIPE_CAL_OOF
))
6210 gchar
*availability_xml_str
= NULL
;
6211 gchar
*activity_xml_str
= NULL
;
6213 if (event
->cal_status
== SIPE_CAL_BUSY
) {
6214 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
6217 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
6218 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
6220 "minAvailability=\"6500\"",
6221 "maxAvailability=\"8999\"");
6222 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
6223 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
6225 "minAvailability=\"12000\"",
6228 start_time_str
= g_strdup(purple_utf8_strftime(SIPE_XML_DATE_PATTERN
, gmtime(&event
->start_time
)));
6230 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
6232 publication_2
? publication_2
->version
: 0,
6235 availability_xml_str
? availability_xml_str
: "",
6236 activity_xml_str
? activity_xml_str
: "",
6237 event
->subject
? event
->subject
: "",
6238 event
->location
? event
->location
: "",
6241 publication_3
? publication_3
->version
: 0,
6244 availability_xml_str
? availability_xml_str
: "",
6245 activity_xml_str
? activity_xml_str
: "",
6246 event
->subject
? event
->subject
: "",
6247 event
->location
? event
->location
: ""
6249 g_free(start_time_str
);
6250 g_free(availability_xml_str
);
6251 g_free(activity_xml_str
);
6254 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
6256 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
6258 publication_2
? publication_2
->version
: 0,
6261 publication_3
? publication_3
->version
: 0
6269 * Returns 'machineState' XML part for publication.
6270 * Must be g_free'd after use.
6273 sipe_publish_get_category_state_machine(struct sipe_account_data
*sip
)
6275 return sipe_publish_get_category_state(sip
, FALSE
);
6279 * Returns 'userState' XML part for publication.
6280 * Must be g_free'd after use.
6283 sipe_publish_get_category_state_user(struct sipe_account_data
*sip
)
6285 return sipe_publish_get_category_state(sip
, TRUE
);
6289 * Compares two strings even in case both are NULL/empty
6292 sipe_is_equal(const char* n1
, const char* n2
) {
6293 return ((!n1
|| !strlen(n1
)) && (!n2
|| !strlen(n2
))) /* both empty */
6294 || (n1
&& n2
&& !strcmp(n1
, n2
)); /* or not empty and equal */
6298 * Returns 'note' XML part for publication.
6299 * Must be g_free'd after use.
6301 * @param note_type either personal or OOF
6304 sipe_publish_get_category_note(struct sipe_account_data
*sip
,
6306 const char *note_type
)
6308 guint instance
= !strcmp("OOF", note_type
) ? sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
) : 0;
6309 /* key is <category><instance><container> */
6310 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
6311 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
6312 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
6314 struct sipe_publication
*publication_note_200
=
6315 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
6316 struct sipe_publication
*publication_note_300
=
6317 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
6318 struct sipe_publication
*publication_note_400
=
6319 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
6321 const char *n1
= note
;
6322 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
6324 g_free(key_note_200
);
6325 g_free(key_note_300
);
6326 g_free(key_note_400
);
6328 if (sipe_is_equal(n1
, n2
))
6330 purple_debug_info("sipe", "sipe_publish_get_category_note: note has NOT changed. Exiting.\n");
6331 return NULL
; /* nothing to update */
6334 return g_markup_printf_escaped(SIPE_PUB_XML_NOTE
,
6336 publication_note_200
? publication_note_200
->version
: 0,
6341 publication_note_300
? publication_note_300
->version
: 0,
6346 publication_note_400
? publication_note_400
->version
: 0,
6352 * Returns 'calendarData' XML part with WorkingHours for publication.
6353 * Must be g_free'd after use.
6356 sipe_publish_get_category_cal_working_hours(struct sipe_account_data
*sip
)
6358 struct sipe_ews
* ews
= sip
->ews
;
6360 /* key is <category><instance><container> */
6361 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
6362 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
6363 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
6364 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
6365 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
6366 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
6368 struct sipe_publication
*publication_cal_1
=
6369 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
6370 struct sipe_publication
*publication_cal_100
=
6371 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
6372 struct sipe_publication
*publication_cal_200
=
6373 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
6374 struct sipe_publication
*publication_cal_300
=
6375 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
6376 struct sipe_publication
*publication_cal_400
=
6377 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
6378 struct sipe_publication
*publication_cal_32000
=
6379 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
6381 const char *n1
= ews
->working_hours_xml_str
;
6382 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
6385 g_free(key_cal_100
);
6386 g_free(key_cal_200
);
6387 g_free(key_cal_300
);
6388 g_free(key_cal_400
);
6389 g_free(key_cal_32000
);
6391 if (!ews
|| is_empty(ews
->email
) || is_empty(ews
->working_hours_xml_str
)) {
6392 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: no data to publish, exiting\n");
6396 if (sipe_is_equal(n1
, n2
))
6398 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.\n");
6399 return NULL
; /* nothing to update */
6402 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
6404 publication_cal_1
? publication_cal_1
->version
: 0,
6406 ews
->working_hours_xml_str
,
6408 publication_cal_100
? publication_cal_100
->version
: 0,
6410 publication_cal_200
? publication_cal_200
->version
: 0,
6412 ews
->working_hours_xml_str
,
6414 publication_cal_300
? publication_cal_300
->version
: 0,
6416 ews
->working_hours_xml_str
,
6417 /* 400 - Personal */
6418 publication_cal_400
? publication_cal_400
->version
: 0,
6420 ews
->working_hours_xml_str
,
6421 /* 32000 - Blocked */
6422 publication_cal_32000
? publication_cal_32000
->version
: 0
6427 * Returns 'calendarData' XML part with FreeBusy for publication.
6428 * Must be g_free'd after use.
6431 sipe_publish_get_category_cal_free_busy(struct sipe_account_data
*sip
)
6433 struct sipe_ews
* ews
= sip
->ews
;
6434 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
6436 char *free_busy_base64
;
6441 /* key is <category><instance><container> */
6442 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
6443 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
6444 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
6445 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
6446 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
6447 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
6449 struct sipe_publication
*publication_cal_1
=
6450 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
6451 struct sipe_publication
*publication_cal_100
=
6452 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
6453 struct sipe_publication
*publication_cal_200
=
6454 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
6455 struct sipe_publication
*publication_cal_300
=
6456 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
6457 struct sipe_publication
*publication_cal_400
=
6458 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
6459 struct sipe_publication
*publication_cal_32000
=
6460 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
6463 g_free(key_cal_100
);
6464 g_free(key_cal_200
);
6465 g_free(key_cal_300
);
6466 g_free(key_cal_400
);
6467 g_free(key_cal_32000
);
6469 if (!ews
|| is_empty(ews
->email
) || !ews
->fb_start
|| is_empty(ews
->free_busy
)) {
6470 purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: no data to publish, exiting\n");
6474 fb_start_str
= g_strdup(purple_utf8_strftime(SIPE_XML_DATE_PATTERN
, gmtime(&ews
->fb_start
)));
6475 free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
6477 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
6478 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
6480 if (sipe_is_equal(st
, fb_start_str
) && sipe_is_equal(fb
, free_busy_base64
))
6482 purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.\n");
6483 return NULL
; /* nothing to update */
6486 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
6489 publication_cal_1
? publication_cal_1
->version
: 0,
6492 publication_cal_100
? publication_cal_100
->version
: 0,
6495 publication_cal_200
? publication_cal_200
->version
: 0,
6501 publication_cal_300
? publication_cal_300
->version
: 0,
6505 /* 400 - Personal */
6507 publication_cal_400
? publication_cal_400
->version
: 0,
6511 /* 32000 - Blocked */
6513 publication_cal_32000
? publication_cal_32000
->version
: 0
6516 g_free(fb_start_str
);
6517 g_free(free_busy_base64
);
6521 static void send_presence_publish(struct sipe_account_data
*sip
, const char *publications
)
6528 uri
= sip_uri_self(sip
);
6529 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
6533 tmp
= get_contact(sip
);
6534 hdr
= g_strdup_printf("Contact: %s\r\n"
6535 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
6537 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
6546 send_publish_category_initial(struct sipe_account_data
*sip
)
6548 gchar
*pub_device
= sipe_publish_get_category_device(sip
);
6549 gchar
*pub_machine
= sipe_publish_get_category_state_machine(sip
);
6550 gchar
*publications
= g_strdup_printf("%s%s",
6552 pub_machine
? pub_machine
: "");
6554 g_free(pub_machine
);
6556 send_presence_publish(sip
, publications
);
6557 g_free(publications
);
6561 send_presence_category_publish(struct sipe_account_data
*sip
,
6565 * Whether user manually changed status or
6566 * it was changed automatically due to user
6567 * became inactive/active again
6569 gboolean is_machine
= (sip
->was_idle
&& !sip
->is_idle
) || (!sip
->was_idle
&& sip
->is_idle
);
6570 gchar
*pub_state
= is_machine
? sipe_publish_get_category_state_machine(sip
) :
6571 sipe_publish_get_category_state_user(sip
);
6572 gchar
*pub_note
= sipe_publish_get_category_note(sip
, note
, "personal");
6573 gchar
*publications
;
6575 if (!pub_state
&& !pub_note
) {
6576 purple_debug_info("sipe", "send_presence_category_publish: nothing has changed. Exiting.\n");
6580 publications
= g_strdup_printf("%s%s",
6581 pub_state
? pub_state
: "",
6582 pub_note
? pub_note
: "");
6584 purple_debug_info("sipe", "send_presence_category_publish: sip->status: %s sip->is_idle:%s sip->was_idle:%s\n",
6585 sip
->status
, sip
->is_idle
? "Y" : "N", sip
->was_idle
? "Y" : "N");
6590 send_presence_publish(sip
, publications
);
6591 g_free(publications
);
6595 * Publishes self status
6596 * based on own calendar information.
6601 publish_calendar_status_self(struct sipe_account_data
*sip
)
6603 struct sipe_cal_event
* event
= NULL
;
6604 gchar
*pub_cal_working_hours
= NULL
;
6605 gchar
*pub_cal_free_busy
= NULL
;
6606 gchar
*pub_calendar
= NULL
;
6607 gchar
*pub_calendar2
= NULL
;
6608 gchar
*pub_oof_note
= NULL
;
6609 purple_debug_info("sipe", "publish_calendar_status_self() started.\n");
6611 if (sip
->ews
&& sip
->ews
->cal_events
) {
6612 event
= sipe_cal_get_event(sip
->ews
->cal_events
, time(NULL
));
6616 purple_debug_info("sipe", "publish_calendar_status_self: current event is NULL\n");
6618 char *desc
= sipe_cal_event_describe(event
);
6619 purple_debug_info("sipe", "publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
6625 OOF publish, Busy clean
6627 OOF clean, Busy publish
6629 OOF clean, Busy clean
6631 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
6632 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_OOF
);
6633 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
6634 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
6635 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
6636 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_BUSY
);
6638 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
6639 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
6642 if (sip
->ews
&& sip
->ews
->oof_note
) {
6643 pub_oof_note
= NULL
;//sipe_publish_get_category_note(sip, sip->ews->oof_note, "OOF");
6646 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sip
);
6647 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sip
);
6649 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
6650 purple_debug_info("sipe", "publish_calendar_status_self: nothing has changed.\n");
6652 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
6653 pub_cal_working_hours
? pub_cal_working_hours
: "",
6654 pub_cal_free_busy
? pub_cal_free_busy
: "",
6655 pub_calendar
? pub_calendar
: "",
6656 pub_calendar2
? pub_calendar2
: "",
6657 pub_oof_note
? pub_oof_note
: "");
6659 send_presence_publish(sip
, publications
);
6660 g_free(publications
);
6663 g_free(pub_cal_working_hours
);
6664 g_free(pub_cal_free_busy
);
6665 g_free(pub_calendar
);
6666 g_free(pub_calendar2
);
6667 g_free(pub_oof_note
);
6669 /* repeat scheduling */
6670 sipe_sched_calendar_status_self_publish(sip
, time(NULL
));
6673 static void send_presence_status(struct sipe_account_data
*sip
)
6675 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
6677 if (!status
) return;
6679 note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
6680 purple_debug_info("sipe", "send_presence_status: note: '%s'\n", note
? note
: "");
6683 send_presence_category_publish(sip
, note
);
6685 send_presence_soap(sip
, note
, FALSE
);
6689 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
6691 gboolean found
= FALSE
;
6692 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
6693 if (msg
->response
== 0) { /* request */
6694 if (!strcmp(msg
->method
, "MESSAGE")) {
6695 process_incoming_message(sip
, msg
);
6697 } else if (!strcmp(msg
->method
, "NOTIFY")) {
6698 purple_debug_info("sipe","send->process_incoming_notify\n");
6699 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
6701 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
6702 purple_debug_info("sipe","send->process_incoming_benotify\n");
6703 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
6705 } else if (!strcmp(msg
->method
, "INVITE")) {
6706 process_incoming_invite(sip
, msg
);
6708 } else if (!strcmp(msg
->method
, "REFER")) {
6709 process_incoming_refer(sip
, msg
);
6711 } else if (!strcmp(msg
->method
, "OPTIONS")) {
6712 process_incoming_options(sip
, msg
);
6714 } else if (!strcmp(msg
->method
, "INFO")) {
6715 process_incoming_info(sip
, msg
);
6717 } else if (!strcmp(msg
->method
, "ACK")) {
6718 // ACK's don't need any response
6720 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
6721 // LCS 2005 sends us these - just respond 200 OK
6723 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
6724 } else if (!strcmp(msg
->method
, "BYE")) {
6725 process_incoming_bye(sip
, msg
);
6728 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
6730 } else { /* response */
6731 struct transaction
*trans
= transactions_find(sip
, msg
);
6733 if (msg
->response
== 407) {
6734 gchar
*resend
, *auth
, *ptmp
;
6736 if (sip
->proxy
.retries
> 30) return;
6737 sip
->proxy
.retries
++;
6738 /* do proxy authentication */
6740 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
6742 fill_auth(ptmp
, &sip
->proxy
);
6743 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
6744 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
6745 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
6747 resend
= sipmsg_to_string(trans
->msg
);
6748 /* resend request */
6749 sendout_pkt(sip
->gc
, resend
);
6752 if (msg
->response
< 200) {
6753 /* ignore provisional response */
6754 purple_debug_info("sipe", "got provisional (%d) response, ignoring\n", msg
->response
);
6756 sip
->proxy
.retries
= 0;
6757 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
6758 if (msg
->response
== 401)
6760 sip
->registrar
.retries
++;
6764 sip
->registrar
.retries
= 0;
6766 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\n", sip
->cseq
);
6768 if (msg
->response
== 401) {
6769 gchar
*resend
, *auth
, *ptmp
;
6771 if (sip
->registrar
.retries
> 4) return;
6772 sip
->registrar
.retries
++;
6775 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
6777 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
6780 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
6784 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\n", ptmp
);
6786 fill_auth(ptmp
, &sip
->registrar
);
6787 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
6788 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
6789 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
6791 //sipmsg_remove_header_now(trans->msg, "Authorization");
6792 //sipmsg_add_header(trans->msg, "Authorization", auth);
6794 resend
= sipmsg_to_string(trans
->msg
);
6795 /* resend request */
6796 sendout_pkt(sip
->gc
, resend
);
6801 if (trans
->callback
) {
6802 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\n");
6803 /* call the callback to process response*/
6804 (trans
->callback
)(sip
, msg
, trans
);
6807 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\n", sip
->cseq
);
6808 transactions_remove(sip
, trans
);
6814 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
6818 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
6822 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
6831 /* according to the RFC remove CRLF at the beginning */
6832 while (*cur
== '\r' || *cur
== '\n') {
6835 if (cur
!= conn
->inbuf
) {
6836 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
6837 conn
->inbufused
= strlen(conn
->inbuf
);
6840 /* Received a full Header? */
6841 sip
->processing_input
= TRUE
;
6842 while (sip
->processing_input
&&
6843 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
6844 time_t currtime
= time(NULL
);
6847 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), tmp
= fix_newlines(conn
->inbuf
));
6849 msg
= sipmsg_parse_header(conn
->inbuf
);
6852 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
6853 if (msg
&& restlen
>= msg
->bodylen
) {
6854 dummy
= g_malloc(msg
->bodylen
+ 1);
6855 memcpy(dummy
, cur
, msg
->bodylen
);
6856 dummy
[msg
->bodylen
] = '\0';
6858 cur
+= msg
->bodylen
;
6859 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
6860 conn
->inbufused
= strlen(conn
->inbuf
);
6863 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n", restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
6870 purple_debug_info("sipe", "body:\n%s", msg->body);
6873 // Verify the signature before processing it
6874 if (sip
->registrar
.gssapi_context
) {
6875 struct sipmsg_breakdown msgbd
;
6876 gchar
*signature_input_str
;
6879 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
6880 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
6882 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
6884 if (rspauth
!= NULL
) {
6885 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
6886 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
6887 process_input_message(sip
, msg
);
6889 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
6890 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
6891 sip
->gc
->wants_to_die
= TRUE
;
6893 } else if (msg
->response
== 401) {
6894 purple_connection_error(sip
->gc
, _("Wrong password"));
6895 sip
->gc
->wants_to_die
= TRUE
;
6897 g_free(signature_input_str
);
6900 sipmsg_breakdown_free(&msgbd
);
6902 process_input_message(sip
, msg
);
6909 static void sipe_udp_process(gpointer data
, gint source
,
6910 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
6912 PurpleConnection
*gc
= data
;
6913 struct sipe_account_data
*sip
= gc
->proto_data
;
6918 static char buffer
[65536];
6919 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
6921 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), buffer
);
6922 msg
= sipmsg_parse_msg(buffer
);
6923 if (msg
) process_input_message(sip
, msg
);
6927 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
6929 struct sipe_account_data
*sip
= gc
->proto_data
;
6930 PurpleSslConnection
*gsc
= sip
->gsc
;
6932 purple_debug_error("sipe", "%s",debug
);
6933 purple_connection_error(gc
, msg
);
6935 /* Invalidate this connection. Next send will open a new one */
6937 connection_remove(sip
, gsc
->fd
);
6938 purple_ssl_close(gsc
);
6944 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
6945 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
6947 PurpleConnection
*gc
= data
;
6948 struct sipe_account_data
*sip
;
6949 struct sip_connection
*conn
;
6951 gboolean firstread
= TRUE
;
6953 /* NOTE: This check *IS* necessary */
6954 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
6955 purple_ssl_close(gsc
);
6959 sip
= gc
->proto_data
;
6960 conn
= connection_find(sip
, gsc
->fd
);
6962 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
6963 gc
->wants_to_die
= TRUE
;
6964 purple_connection_error(gc
, _("Connection not found. Please try to connect again"));
6968 /* Read all available data from the SSL connection */
6970 /* Increase input buffer size as needed */
6971 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
6972 conn
->inbuflen
+= SIMPLE_BUF_INC
;
6973 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
6974 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
6977 /* Try to read as much as there is space left in the buffer */
6978 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
6979 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
6981 if (len
< 0 && errno
== EAGAIN
) {
6982 /* Try again later */
6984 } else if (len
< 0) {
6985 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
6987 } else if (firstread
&& (len
== 0)) {
6988 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
6992 conn
->inbufused
+= len
;
6995 /* Equivalence indicates that there is possibly more data to read */
6996 } while (len
== readlen
);
6998 conn
->inbuf
[conn
->inbufused
] = '\0';
6999 process_input(sip
, conn
);
7003 static void sipe_input_cb(gpointer data
, gint source
,
7004 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
7006 PurpleConnection
*gc
= data
;
7007 struct sipe_account_data
*sip
= gc
->proto_data
;
7009 struct sip_connection
*conn
= connection_find(sip
, source
);
7011 purple_debug_error("sipe", "Connection not found!\n");
7015 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
7016 conn
->inbuflen
+= SIMPLE_BUF_INC
;
7017 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
7020 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
7022 if (len
< 0 && errno
== EAGAIN
)
7024 else if (len
<= 0) {
7025 purple_debug_info("sipe", "sipe_input_cb: read error\n");
7026 connection_remove(sip
, source
);
7027 if (sip
->fd
== source
) sip
->fd
= -1;
7031 conn
->inbufused
+= len
;
7032 conn
->inbuf
[conn
->inbufused
] = '\0';
7034 process_input(sip
, conn
);
7037 /* Callback for new connections on incoming TCP port */
7038 static void sipe_newconn_cb(gpointer data
, gint source
,
7039 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
7041 PurpleConnection
*gc
= data
;
7042 struct sipe_account_data
*sip
= gc
->proto_data
;
7043 struct sip_connection
*conn
;
7045 int newfd
= accept(source
, NULL
, NULL
);
7047 conn
= connection_create(sip
, newfd
);
7049 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
7052 static void login_cb(gpointer data
, gint source
,
7053 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
7055 PurpleConnection
*gc
= data
;
7056 struct sipe_account_data
*sip
;
7057 struct sip_connection
*conn
;
7059 if (!PURPLE_CONNECTION_IS_VALID(gc
))
7067 purple_connection_error(gc
, _("Could not connect"));
7071 sip
= gc
->proto_data
;
7073 sip
->last_keepalive
= time(NULL
);
7075 conn
= connection_create(sip
, source
);
7079 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
7082 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
7083 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
7085 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
7086 if (sip
== NULL
) return;
7091 static guint
sipe_ht_hash_nick(const char *nick
)
7093 char *lc
= g_utf8_strdown(nick
, -1);
7094 guint bucket
= g_str_hash(lc
);
7100 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
7102 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
7105 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
7107 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
7109 sip
->listen_data
= NULL
;
7111 if (listenfd
== -1) {
7112 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
7118 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
7119 sip
->listenfd
= sip
->fd
;
7121 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
7123 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
7127 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
7128 SIPE_UNUSED_PARAMETER
const char *error_message
)
7130 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
7132 sip
->query_data
= NULL
;
7134 if (!hosts
|| !hosts
->data
) {
7135 purple_connection_error(sip
->gc
, _("Could not resolve hostname"));
7139 hosts
= g_slist_remove(hosts
, hosts
->data
);
7140 g_free(sip
->serveraddr
);
7141 sip
->serveraddr
= hosts
->data
;
7142 hosts
= g_slist_remove(hosts
, hosts
->data
);
7144 hosts
= g_slist_remove(hosts
, hosts
->data
);
7145 g_free(hosts
->data
);
7146 hosts
= g_slist_remove(hosts
, hosts
->data
);
7149 /* create socket for incoming connections */
7150 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
7151 sipe_udp_host_resolved_listen_cb
, sip
);
7152 if (sip
->listen_data
== NULL
) {
7153 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
7158 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
7159 PurpleSslErrorType error
,
7162 PurpleConnection
*gc
= data
;
7163 struct sipe_account_data
*sip
;
7165 /* If the connection is already disconnected, we don't need to do anything else */
7166 if (!PURPLE_CONNECTION_IS_VALID(gc
))
7169 sip
= gc
->proto_data
;
7174 case PURPLE_SSL_CONNECT_FAILED
:
7175 purple_connection_error(gc
, _("Connection failed"));
7177 case PURPLE_SSL_HANDSHAKE_FAILED
:
7178 purple_connection_error(gc
, _("SSL handshake failed"));
7180 case PURPLE_SSL_CERTIFICATE_INVALID
:
7181 purple_connection_error(gc
, _("SSL certificate invalid"));
7187 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
7189 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
7190 PurpleProxyConnectData
*connect_data
;
7192 sip
->listen_data
= NULL
;
7194 sip
->listenfd
= listenfd
;
7195 if (sip
->listenfd
== -1) {
7196 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
7200 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
7201 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
7202 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
7203 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
7204 sipe_newconn_cb
, sip
->gc
);
7205 purple_debug_info("sipe", "connecting to %s port %d\n",
7206 sip
->realhostname
, sip
->realport
);
7207 /* open tcp connection to the server */
7208 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
7209 sip
->realport
, login_cb
, sip
->gc
);
7211 if (connect_data
== NULL
) {
7212 purple_connection_error(sip
->gc
, _("Could not create socket"));
7216 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
7218 PurpleAccount
*account
= sip
->account
;
7219 PurpleConnection
*gc
= sip
->gc
;
7222 port
= (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
7225 sip
->realhostname
= hostname
;
7226 sip
->realport
= port
;
7228 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
7231 /* TODO: is there a good default grow size? */
7232 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
7233 sip
->txbuf
= purple_circ_buffer_new(0);
7235 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
7237 if (!purple_ssl_is_supported()) {
7238 gc
->wants_to_die
= TRUE
;
7239 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor"));
7243 purple_debug_info("sipe", "using SSL\n");
7245 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
7246 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
7247 if (sip
->gsc
== NULL
) {
7248 purple_connection_error(gc
, _("Could not create SSL context"));
7251 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
7253 purple_debug_info("sipe", "using UDP\n");
7255 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
7256 if (sip
->query_data
== NULL
) {
7257 purple_connection_error(gc
, _("Could not resolve hostname"));
7261 purple_debug_info("sipe", "using TCP\n");
7262 /* create socket for incoming connections */
7263 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
7264 sipe_tcp_connect_listen_cb
, sip
);
7265 if (sip
->listen_data
== NULL
) {
7266 purple_connection_error(gc
, _("Could not create listen socket"));
7272 /* Service list for autodection */
7273 static const struct sipe_service_data service_autodetect
[] = {
7274 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
7275 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
7276 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
7277 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
7281 /* Service list for SSL/TLS */
7282 static const struct sipe_service_data service_tls
[] = {
7283 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
7284 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
7288 /* Service list for TCP */
7289 static const struct sipe_service_data service_tcp
[] = {
7290 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
7291 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
7295 /* Service list for UDP */
7296 static const struct sipe_service_data service_udp
[] = {
7297 { "sip", "udp", SIPE_TRANSPORT_UDP
},
7301 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
7302 static void resolve_next_service(struct sipe_account_data
*sip
,
7303 const struct sipe_service_data
*start
)
7306 sip
->service_data
= start
;
7308 sip
->service_data
++;
7309 if (sip
->service_data
->service
== NULL
) {
7311 /* Try connecting to the SIP hostname directly */
7312 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
7313 if (sip
->auto_transport
) {
7314 // If SSL is supported, default to using it; OCS servers aren't configured
7315 // by default to accept TCP
7316 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
7317 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
7318 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
7321 hostname
= g_strdup(sip
->sipdomain
);
7322 create_connection(sip
, hostname
, 0);
7327 /* Try to resolve next service */
7328 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
7329 sip
->service_data
->transport
,
7334 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
7336 struct sipe_account_data
*sip
= data
;
7338 sip
->srv_query_data
= NULL
;
7340 /* find the host to connect to */
7342 gchar
*hostname
= g_strdup(resp
->hostname
);
7343 int port
= resp
->port
;
7344 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
7348 sip
->transport
= sip
->service_data
->type
;
7350 create_connection(sip
, hostname
, port
);
7352 resolve_next_service(sip
, NULL
);
7356 static void sipe_login(PurpleAccount
*account
)
7358 PurpleConnection
*gc
;
7359 struct sipe_account_data
*sip
;
7360 gchar
**signinname_login
, **userserver
;
7361 const char *transport
;
7364 const char *username
= purple_account_get_username(account
);
7365 gc
= purple_account_get_connection(account
);
7367 purple_debug_info("sipe", "sipe_login: username '%s'\n", username
);
7369 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
7370 gc
->wants_to_die
= TRUE
;
7371 purple_connection_error(gc
, _("SIP Exchange user name contains invalid characters"));
7375 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
7376 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
7377 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
7379 sip
->account
= account
;
7380 sip
->reregister_set
= FALSE
;
7381 sip
->reauthenticate_set
= FALSE
;
7382 sip
->subscribed
= FALSE
;
7383 sip
->subscribed_buddies
= FALSE
;
7384 sip
->initial_state_published
= FALSE
;
7386 /* username format: <username>,[<optional login>] */
7387 signinname_login
= g_strsplit(username
, ",", 2);
7388 purple_debug_info("sipe", "sipe_login: signinname[0] '%s'\n", signinname_login
[0]);
7390 /* ensure that username format is name@domain */
7391 if (!strchr(signinname_login
[0], '@') || g_str_has_prefix(signinname_login
[0], "@") || g_str_has_suffix(signinname_login
[0], "@")) {
7392 g_strfreev(signinname_login
);
7393 gc
->wants_to_die
= TRUE
;
7394 purple_connection_error(gc
, _("User name should be a valid SIP URI\nExample: user@company.com"));
7397 sip
->username
= g_strdup(signinname_login
[0]);
7399 /* ensure that email format is name@domain if provided */
7400 email
= purple_account_get_string(sip
->account
, "email", NULL
);
7401 if (!is_empty(email
) &&
7402 (!strchr(email
, '@') || g_str_has_prefix(email
, "@") || g_str_has_suffix(email
, "@")))
7404 gc
->wants_to_die
= TRUE
;
7405 purple_connection_error(gc
, _("Email address should be valid if provided\nExample: user@company.com"));
7408 sip
->email
= !is_empty(email
) ? g_strdup(email
) : g_strdup(sip
->username
);
7410 /* login name specified? */
7411 if (signinname_login
[1] && strlen(signinname_login
[1])) {
7412 gchar
**domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
7413 gboolean has_domain
= domain_user
[1] != NULL
;
7414 purple_debug_info("sipe", "sipe_login: signinname[1] '%s'\n", signinname_login
[1]);
7415 sip
->authdomain
= has_domain
? g_strdup(domain_user
[0]) : NULL
;
7416 sip
->authuser
= g_strdup(domain_user
[has_domain
? 1 : 0]);
7417 purple_debug_info("sipe", "sipe_login: auth domain '%s' user '%s'\n",
7418 sip
->authdomain
? sip
->authdomain
: "", sip
->authuser
);
7419 g_strfreev(domain_user
);
7422 userserver
= g_strsplit(signinname_login
[0], "@", 2);
7423 purple_debug_info("sipe", "sipe_login: user '%s' server '%s'\n", userserver
[0], userserver
[1]);
7424 purple_connection_set_display_name(gc
, userserver
[0]);
7425 sip
->sipdomain
= g_strdup(userserver
[1]);
7426 g_strfreev(userserver
);
7427 g_strfreev(signinname_login
);
7429 if (strchr(sip
->username
, ' ') != NULL
) {
7430 gc
->wants_to_die
= TRUE
;
7431 purple_connection_error(gc
, _("SIP Exchange user name contains whitespace"));
7435 sip
->password
= g_strdup(purple_connection_get_password(gc
));
7437 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
7438 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
7439 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
7440 sip
->subscriptions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
7441 g_free
, (GDestroyNotify
)sipe_subscription_free
);
7443 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
7445 sip
->status
= g_strdup(purple_status_get_id(purple_account_get_active_status(account
)));
7447 sip
->auto_transport
= FALSE
;
7448 transport
= purple_account_get_string(account
, "transport", "auto");
7449 userserver
= g_strsplit(purple_account_get_string(account
, "server", ""), ":", 2);
7450 if (userserver
[0]) {
7451 /* Use user specified server[:port] */
7455 port
= atoi(userserver
[1]);
7457 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login: user specified SIP server %s:%d\n",
7458 userserver
[0], port
);
7460 if (strcmp(transport
, "auto") == 0) {
7461 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
7462 } else if (strcmp(transport
, "tls") == 0) {
7463 sip
->transport
= SIPE_TRANSPORT_TLS
;
7464 } else if (strcmp(transport
, "tcp") == 0) {
7465 sip
->transport
= SIPE_TRANSPORT_TCP
;
7467 sip
->transport
= SIPE_TRANSPORT_UDP
;
7470 create_connection(sip
, g_strdup(userserver
[0]), port
);
7472 /* Server auto-discovery */
7473 if (strcmp(transport
, "auto") == 0) {
7474 sip
->auto_transport
= TRUE
;
7475 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
7476 } else if (strcmp(transport
, "tls") == 0) {
7477 resolve_next_service(sip
, service_tls
);
7478 } else if (strcmp(transport
, "tcp") == 0) {
7479 resolve_next_service(sip
, service_tcp
);
7481 resolve_next_service(sip
, service_udp
);
7484 g_strfreev(userserver
);
7487 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
7489 connection_free_all(sip
);
7494 if (sip
->query_data
!= NULL
)
7495 purple_dnsquery_destroy(sip
->query_data
);
7496 sip
->query_data
= NULL
;
7498 if (sip
->srv_query_data
!= NULL
)
7499 purple_srv_cancel(sip
->srv_query_data
);
7500 sip
->srv_query_data
= NULL
;
7502 if (sip
->listen_data
!= NULL
)
7503 purple_network_listen_cancel(sip
->listen_data
);
7504 sip
->listen_data
= NULL
;
7506 if (sip
->gsc
!= NULL
)
7507 purple_ssl_close(sip
->gsc
);
7510 sipe_auth_free(&sip
->registrar
);
7511 sipe_auth_free(&sip
->proxy
);
7514 purple_circ_buffer_destroy(sip
->txbuf
);
7517 g_free(sip
->realhostname
);
7518 sip
->realhostname
= NULL
;
7520 g_free(sip
->server_version
);
7521 sip
->server_version
= NULL
;
7524 purple_input_remove(sip
->listenpa
);
7526 if (sip
->tx_handler
)
7527 purple_input_remove(sip
->tx_handler
);
7528 sip
->tx_handler
= 0;
7529 if (sip
->resendtimeout
)
7530 purple_timeout_remove(sip
->resendtimeout
);
7531 sip
->resendtimeout
= 0;
7532 if (sip
->timeouts
) {
7533 GSList
*entry
= sip
->timeouts
;
7535 struct scheduled_action
*sched_action
= entry
->data
;
7536 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
7537 purple_timeout_remove(sched_action
->timeout_handler
);
7538 if (sched_action
->destroy
) {
7539 (*sched_action
->destroy
)(sched_action
->payload
);
7541 g_free(sched_action
->name
);
7542 g_free(sched_action
);
7543 entry
= entry
->next
;
7546 g_slist_free(sip
->timeouts
);
7548 if (sip
->allow_events
) {
7549 GSList
*entry
= sip
->allow_events
;
7551 g_free(entry
->data
);
7552 entry
= entry
->next
;
7555 g_slist_free(sip
->allow_events
);
7557 if (sip
->containers
) {
7558 GSList
*entry
= sip
->containers
;
7560 free_container((struct sipe_container
*)entry
->data
);
7561 entry
= entry
->next
;
7564 g_slist_free(sip
->containers
);
7567 g_free(sip
->contact
);
7568 sip
->contact
= NULL
;
7570 g_free(sip
->regcallid
);
7571 sip
->regcallid
= NULL
;
7573 if (sip
->serveraddr
)
7574 g_free(sip
->serveraddr
);
7575 sip
->serveraddr
= NULL
;
7577 if (sip
->focus_factory_uri
)
7578 g_free(sip
->focus_factory_uri
);
7579 sip
->focus_factory_uri
= NULL
;
7582 sip
->processing_input
= FALSE
;
7585 sipe_ews_free(sip
->ews
);
7595 * A callback for g_hash_table_foreach_remove
7597 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
7598 SIPE_UNUSED_PARAMETER gpointer user_data
)
7600 sipe_free_buddy((struct sipe_buddy
*) buddy
);
7602 /* We must return TRUE as the key/value have already been deleted */
7606 static void sipe_close(PurpleConnection
*gc
)
7608 struct sipe_account_data
*sip
= gc
->proto_data
;
7611 /* leave all conversations */
7612 sipe_session_close_all(sip
);
7613 sipe_session_remove_all(sip
);
7616 sip_csta_close(sip
);
7619 if (PURPLE_CONNECTION_IS_CONNECTED(sip
->gc
)) {
7620 /* unsubscribe all */
7621 g_hash_table_foreach(sip
->subscriptions
, sipe_unsubscribe_cb
, sip
);
7624 do_register_exp(sip
, 0);
7627 sipe_connection_cleanup(sip
);
7628 g_free(sip
->sipdomain
);
7629 g_free(sip
->username
);
7630 g_free(sip
->password
);
7631 g_free(sip
->authdomain
);
7632 g_free(sip
->authuser
);
7633 g_free(sip
->status
);
7635 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
7636 g_hash_table_destroy(sip
->buddies
);
7637 g_hash_table_destroy(sip
->our_publications
);
7638 g_hash_table_destroy(sip
->subscriptions
);
7641 GSList
*entry
= sip
->groups
;
7643 struct sipe_group
*group
= entry
->data
;
7644 g_free(group
->name
);
7646 entry
= entry
->next
;
7649 g_slist_free(sip
->groups
);
7651 if (sip
->our_publication_keys
) {
7652 GSList
*entry
= sip
->our_publication_keys
;
7654 g_free(entry
->data
);
7655 entry
= entry
->next
;
7658 g_slist_free(sip
->our_publication_keys
);
7660 while (sip
->transactions
)
7661 transactions_remove(sip
, sip
->transactions
->data
);
7663 g_free(gc
->proto_data
);
7664 gc
->proto_data
= NULL
;
7667 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
7668 SIPE_UNUSED_PARAMETER
void *user_data
)
7670 PurpleAccount
*acct
= purple_connection_get_account(gc
);
7671 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
7672 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
7674 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
7675 purple_conversation_present(conv
);
7679 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
7680 SIPE_UNUSED_PARAMETER
void *user_data
)
7683 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
7684 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
7687 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
7688 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
7690 PurpleNotifySearchResults
*results
;
7691 PurpleNotifySearchColumn
*column
;
7692 xmlnode
*searchResults
;
7694 int match_count
= 0;
7695 gboolean more
= FALSE
;
7698 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
7700 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
7701 if (!searchResults
) {
7702 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
7706 results
= purple_notify_searchresults_new();
7708 if (results
== NULL
) {
7709 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
7710 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
7712 xmlnode_free(searchResults
);
7716 column
= purple_notify_searchresults_column_new(_("User name"));
7717 purple_notify_searchresults_column_add(results
, column
);
7719 column
= purple_notify_searchresults_column_new(_("Name"));
7720 purple_notify_searchresults_column_add(results
, column
);
7722 column
= purple_notify_searchresults_column_new(_("Company"));
7723 purple_notify_searchresults_column_add(results
, column
);
7725 column
= purple_notify_searchresults_column_new(_("Country"));
7726 purple_notify_searchresults_column_add(results
, column
);
7728 column
= purple_notify_searchresults_column_new(_("Email"));
7729 purple_notify_searchresults_column_add(results
, column
);
7731 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
7734 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
7735 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
7736 g_strfreev(uri_parts
);
7738 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
7739 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
7740 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
7741 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
7743 purple_notify_searchresults_row_add(results
, row
);
7747 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
7748 char *data
= xmlnode_get_data_unescaped(mrow
);
7749 more
= (g_strcasecmp(data
, "true") == 0);
7753 secondary
= g_strdup_printf(
7754 dngettext(GETTEXT_PACKAGE
,
7755 "Found %d contact%s:",
7756 "Found %d contacts%s:", match_count
),
7757 match_count
, more
? _(" (more matched your query)") : "");
7759 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
7760 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
7761 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
7764 xmlnode_free(searchResults
);
7768 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
7770 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
7771 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
7775 PurpleRequestField
*field
= entries
->data
;
7776 const char *id
= purple_request_field_get_id(field
);
7777 const char *value
= purple_request_field_string_get_value(field
);
7779 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
7781 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
7782 } while ((entries
= g_list_next(entries
)) != NULL
);
7786 struct sipe_account_data
*sip
= gc
->proto_data
;
7787 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
7788 gchar
*query
= g_strjoinv(NULL
, attrs
);
7789 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
7790 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
7791 send_soap_request_with_cb(sip
, domain_uri
, body
,
7792 (TransCallback
) process_search_contact_response
, NULL
);
7801 static void sipe_show_find_contact(PurplePluginAction
*action
)
7803 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
7804 PurpleRequestFields
*fields
;
7805 PurpleRequestFieldGroup
*group
;
7806 PurpleRequestField
*field
;
7808 fields
= purple_request_fields_new();
7809 group
= purple_request_field_group_new(NULL
);
7810 purple_request_fields_add_group(fields
, group
);
7812 field
= purple_request_field_string_new("givenName", _("First name"), NULL
, FALSE
);
7813 purple_request_field_group_add_field(group
, field
);
7814 field
= purple_request_field_string_new("sn", _("Last name"), NULL
, FALSE
);
7815 purple_request_field_group_add_field(group
, field
);
7816 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
7817 purple_request_field_group_add_field(group
, field
);
7818 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
7819 purple_request_field_group_add_field(group
, field
);
7821 purple_request_fields(gc
,
7823 _("Search for a contact"),
7824 _("Enter the information for the person you wish to find. Empty fields will be ignored."),
7826 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
7828 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
7831 static void sipe_show_about_plugin(PurplePluginAction
*action
)
7833 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
7835 "<b><font size=\"+1\">Sipe " SIPE_VERSION
"</font></b><br/>"
7837 "A third-party plugin implementing extended version of SIP/SIMPLE used by various products:<br/>"
7838 "<li> - MS Office Communications Server 2007 (R2)</li><br/>"
7839 "<li> - MS Live Communications Server 2005/2003</li><br/>"
7840 "<li> - Reuters Messaging</li><br/>"
7842 "Home: <a href=\"http://sipe.sourceforge.net\">http://sipe.sourceforge.net</a><br/>"
7843 "Support: <a href=\"http://sourceforge.net/projects/sipe/forums/forum/688534\">Help Forum</a><br/>"
7844 "License: GPLv2<br/>"
7846 "We support users in the following organizations to mention a few:<br/>"
7848 " - Reuters Messaging network<br/>"
7849 " - Deutsche Bank<br/>"
7850 " - Merrill Lynch<br/>"
7853 " - Alcatel-Lucent<br/>"
7858 "<b>Authors:</b><br/>"
7859 " - Anibal Avelar<br/>"
7860 " - Gabriel Burt<br/>"
7861 " - Stefan Becker<br/>"
7864 purple_notify_formatted(gc
, NULL
, " ", NULL
, txt
, NULL
, NULL
);
7867 static void sipe_republish_calendar(PurplePluginAction
*action
)
7869 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
7870 struct sipe_account_data
*sip
= gc
->proto_data
;
7872 sipe_update_calendar(sip
);
7875 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
7878 PurpleConnection
*gc
= (PurpleConnection
*)context
;
7879 struct sipe_account_data
*sip
= gc
->proto_data
;
7881 PurplePluginAction
*act
;
7882 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
7884 act
= purple_plugin_action_new(_("About SIPE plugin"), sipe_show_about_plugin
);
7885 menu
= g_list_prepend(menu
, act
);
7887 act
= purple_plugin_action_new(_("Contact search..."), sipe_show_find_contact
);
7888 menu
= g_list_prepend(menu
, act
);
7890 if (!strcmp(calendar
, "EXCH")) {
7891 act
= purple_plugin_action_new(_("Republish Calendar"), sipe_republish_calendar
);
7892 menu
= g_list_prepend(menu
, act
);
7895 menu
= g_list_reverse(menu
);
7900 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
7904 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
7910 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
7916 static char *sipe_status_text(PurpleBuddy
*buddy
)
7918 struct sipe_account_data
*sip
;
7919 struct sipe_buddy
*sbuddy
;
7922 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
7923 if (sip
) //happens on pidgin exit
7925 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
7927 if (!is_empty(sbuddy
->activity
) && !is_empty(sbuddy
->annotation
))
7929 text
= g_strdup_printf("%s. %s", sbuddy
->activity
, sbuddy
->annotation
);
7931 else if (!is_empty(sbuddy
->activity
))
7933 text
= g_strdup(sbuddy
->activity
);
7937 text
= g_strdup(sbuddy
->annotation
);
7945 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
7947 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
7948 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
7949 struct sipe_account_data
*sip
;
7950 struct sipe_buddy
*sbuddy
;
7951 char *annotation
= NULL
;
7952 char *activity
= NULL
;
7953 char *calendar
= NULL
;
7954 char *meeting_subject
= NULL
;
7955 char *meeting_location
= NULL
;
7957 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
7958 if (sip
) //happens on pidgin exit
7960 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
7963 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
7964 activity
= sbuddy
->activity
;
7965 calendar
= sipe_cal_get_description(sbuddy
);
7966 meeting_subject
= sbuddy
->meeting_subject
;
7967 meeting_location
= sbuddy
->meeting_location
;
7972 if (purple_presence_is_online(presence
))
7975 const char *status_str
= activity
&& status
&& strcmp(purple_status_get_id(status
), SIPE_STATUS_ID_ONPHONE
) ?
7976 (tmp
= g_strdup_printf("%s (%s)", purple_status_get_name(status
), activity
)) :
7977 purple_status_get_name(status
);
7979 purple_notify_user_info_add_pair(user_info
, _("Status"), status_str
);
7982 if (purple_presence_is_online(presence
) &&
7983 !is_empty(calendar
))
7985 purple_notify_user_info_add_pair(user_info
, _("Calendar"), calendar
);
7988 if (!is_empty(meeting_location
))
7990 purple_notify_user_info_add_pair(user_info
, _("Meeting in"), meeting_location
);
7992 if (!is_empty(meeting_subject
))
7994 purple_notify_user_info_add_pair(user_info
, _("Meeting about"), meeting_subject
);
7999 /* Tooltip does not know how to handle markup like <br> */
8000 gchar
*s
= annotation
;
8001 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, annotation
);
8002 while ((s
= strchr(s
, '<')) != NULL
) {
8003 if (!g_ascii_strncasecmp(s
, "<br>", 4)) {
8005 strcpy(s
+ 1, s
+ 4);
8009 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, annotation
);
8011 purple_notify_user_info_add_pair(user_info
, _("Note"), annotation
);
8017 #if PURPLE_VERSION_CHECK(2,5,0)
8019 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
8022 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
8023 g_hash_table_insert(table
, "login_label", (gpointer
)_("user@company.com"));
8028 static PurpleBuddy
*
8029 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
8032 const gchar
*server_alias
, *email
;
8033 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
8035 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
8037 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
8039 server_alias
= purple_buddy_get_server_alias(buddy
);
8041 purple_blist_server_alias_buddy(clone
, server_alias
);
8044 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
8046 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
8049 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
8051 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
8056 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
8058 PurpleBuddy
*buddy
, *b
;
8059 PurpleConnection
*gc
;
8060 PurpleGroup
* group
= purple_find_group(group_name
);
8062 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
8064 buddy
= (PurpleBuddy
*)node
;
8066 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
8067 gc
= purple_account_get_connection(buddy
->account
);
8069 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
8071 b
= purple_blist_add_buddy_clone(group
, buddy
);
8074 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
8078 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
8080 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
8082 purple_debug_info("sipe", "sipe_buddy_menu_chat_new_cb: buddy->name=%s\n", buddy
->name
);
8084 /* 2007+ conference */
8087 sipe_conf_add(sip
, buddy
->name
);
8089 else /* 2005- multiparty chat */
8091 gchar
*self
= sip_uri_self(sip
);
8092 struct sip_session
*session
;
8094 session
= sipe_session_add_chat(sip
);
8095 session
->chat_title
= sipe_chat_get_name(session
->callid
);
8096 session
->roster_manager
= g_strdup(self
);
8098 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, session
->chat_title
);
8099 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
8100 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
8101 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, FALSE
);
8108 sipe_is_election_finished(struct sip_session
*session
)
8110 gboolean res
= TRUE
;
8112 SIPE_DIALOG_FOREACH
{
8113 if (dialog
->election_vote
== 0) {
8117 } SIPE_DIALOG_FOREACH_END
;
8120 session
->is_voting_in_progress
= FALSE
;
8126 sipe_election_start(struct sipe_account_data
*sip
,
8127 struct sip_session
*session
)
8129 int election_timeout
;
8131 if (session
->is_voting_in_progress
) {
8132 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
8135 session
->is_voting_in_progress
= TRUE
;
8137 session
->bid
= rand();
8139 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
8141 SIPE_DIALOG_FOREACH
{
8142 /* reset election_vote for each chat participant */
8143 dialog
->election_vote
= 0;
8145 /* send RequestRM to each chat participant*/
8146 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
8147 } SIPE_DIALOG_FOREACH_END
;
8149 election_timeout
= 15; /* sec */
8150 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
8154 * @param who a URI to whom to invite to chat
8157 sipe_invite_to_chat(struct sipe_account_data
*sip
,
8158 struct sip_session
*session
,
8162 if (session
->focus_uri
)
8164 sipe_invite_conf(sip
, session
, who
);
8166 else /* a multi-party chat */
8168 gchar
*self
= sip_uri_self(sip
);
8169 if (session
->roster_manager
) {
8170 if (!strcmp(session
->roster_manager
, self
)) {
8171 sipe_invite(sip
, session
, who
, NULL
, NULL
, FALSE
);
8173 sipe_refer(sip
, session
, who
);
8176 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite: no RM available\n");
8178 session
->pending_invite_queue
= slist_insert_unique_sorted(
8179 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
8181 sipe_election_start(sip
, session
);
8188 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
8189 struct sip_session
*session
)
8192 GSList
*entry
= session
->pending_invite_queue
;
8195 invitee
= entry
->data
;
8196 sipe_invite_to_chat(sip
, session
, invitee
);
8197 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
8203 sipe_election_result(struct sipe_account_data
*sip
,
8206 struct sip_session
*session
= (struct sip_session
*)sess
;
8208 gboolean has_won
= TRUE
;
8210 if (session
->roster_manager
) {
8211 purple_debug_info("sipe",
8212 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
8216 session
->is_voting_in_progress
= FALSE
;
8218 SIPE_DIALOG_FOREACH
{
8219 if (dialog
->election_vote
< 0) {
8221 rival
= dialog
->with
;
8224 } SIPE_DIALOG_FOREACH_END
;
8227 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
8229 session
->roster_manager
= sip_uri_self(sip
);
8231 SIPE_DIALOG_FOREACH
{
8232 /* send SetRM to each chat participant*/
8233 sipe_send_election_set_rm(sip
, dialog
);
8234 } SIPE_DIALOG_FOREACH_END
;
8236 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
8240 sipe_process_pending_invite_queue(sip
, session
);
8244 * For 2007+ conference only.
8247 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_title
)
8249 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
8250 struct sip_session
*session
;
8252 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
8253 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_title=%s\n", chat_title
);
8255 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
8257 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
8261 * For 2007+ conference only.
8264 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_title
)
8266 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
8267 struct sip_session
*session
;
8269 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: buddy->name=%s\n", buddy
->name
);
8270 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: chat_title=%s\n", chat_title
);
8272 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
8274 sipe_conf_delete_user(sip
, session
, buddy
->name
);
8278 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, char *chat_title
)
8280 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
8281 struct sip_session
*session
;
8283 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: buddy->name=%s\n", buddy
->name
);
8284 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: chat_title=%s\n", chat_title
);
8286 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
8288 sipe_invite_to_chat(sip
, session
, buddy
->name
);
8292 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
8294 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
8296 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: buddy->name=%s\n", buddy
->name
);
8298 char *tel_uri
= sip_to_tel_uri(phone
);
8300 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: going to call number: %s\n", tel_uri
? tel_uri
: "");
8301 sip_csta_make_call(sip
, tel_uri
);
8308 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
8311 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
8313 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
8316 char *mailto
= g_strdup_printf("mailto:%s", email
);
8317 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
8321 char *const parmList
[] = {"xdg-email", mailto
, NULL
};
8322 if ((pid
= fork()) == -1)
8324 purple_debug_info("sipe", "fork() error\n");
8328 execvp(parmList
[0], parmList
);
8329 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
8337 //@TODO resolve env variable %WINDIR% first
8338 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
8341 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
8350 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
8355 * A menu which appear when right-clicking on buddy in contact list.
8358 sipe_buddy_menu(PurpleBuddy
*buddy
)
8360 PurpleBlistNode
*g_node
;
8361 PurpleGroup
*group
, *gr_parent
;
8362 PurpleMenuAction
*act
;
8364 GList
*menu_groups
= NULL
;
8365 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
8368 const char *phone_disp_str
;
8369 gchar
*self
= sip_uri_self(sip
);
8371 SIPE_SESSION_FOREACH
{
8372 if (g_ascii_strcasecmp(self
, buddy
->name
) && session
->chat_title
&& session
->conv
)
8374 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
8376 PurpleConvChatBuddyFlags flags
;
8377 PurpleConvChatBuddyFlags flags_us
;
8379 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
8380 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
8381 if (session
->focus_uri
8382 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
8383 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
8385 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"), session
->chat_title
);
8386 act
= purple_menu_action_new(label
,
8387 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
8388 session
->chat_title
, NULL
);
8390 menu
= g_list_prepend(menu
, act
);
8393 if (session
->focus_uri
8394 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
8396 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_title
);
8397 act
= purple_menu_action_new(label
,
8398 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
8399 session
->chat_title
, NULL
);
8401 menu
= g_list_prepend(menu
, act
);
8406 if (!session
->focus_uri
8407 || (session
->focus_uri
&& !session
->locked
))
8409 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_title
);
8410 act
= purple_menu_action_new(label
,
8411 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
8412 session
->chat_title
, NULL
);
8414 menu
= g_list_prepend(menu
, act
);
8418 } SIPE_SESSION_FOREACH_END
;
8420 act
= purple_menu_action_new(_("New chat"),
8421 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
8423 menu
= g_list_prepend(menu
, act
);
8425 if (sip
->csta
&& !sip
->csta
->line_status
) {
8428 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
8429 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
8431 gchar
*label
= g_strdup_printf(_("Work %s"),
8432 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
8433 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
8437 menu
= g_list_prepend(menu
, act
);
8441 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
8442 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
8444 gchar
*label
= g_strdup_printf(_("Mobile %s"),
8445 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
8446 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
8450 menu
= g_list_prepend(menu
, act
);
8454 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
8455 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
8457 gchar
*label
= g_strdup_printf(_("Home %s"),
8458 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
8459 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
8463 menu
= g_list_prepend(menu
, act
);
8467 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
8468 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
8470 gchar
*label
= g_strdup_printf(_("Other %s"),
8471 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
8472 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
8476 menu
= g_list_prepend(menu
, act
);
8480 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
8481 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
8483 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
8484 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
8485 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
8489 menu
= g_list_prepend(menu
, act
);
8493 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
8495 act
= purple_menu_action_new(_("Send email..."),
8496 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
8498 menu
= g_list_prepend(menu
, act
);
8501 gr_parent
= purple_buddy_get_group(buddy
);
8502 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
8503 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
8506 group
= (PurpleGroup
*)g_node
;
8507 if (group
== gr_parent
)
8510 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
8513 act
= purple_menu_action_new(purple_group_get_name(group
),
8514 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
8516 menu_groups
= g_list_prepend(menu_groups
, act
);
8518 menu_groups
= g_list_reverse(menu_groups
);
8520 act
= purple_menu_action_new(_("Copy to"),
8523 menu
= g_list_prepend(menu
, act
);
8524 menu
= g_list_reverse(menu
);
8531 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
8533 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
8534 struct sip_session
*session
;
8536 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
8537 sipe_conf_modify_conference_lock(sip
, session
, locked
);
8541 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
8543 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
8544 sipe_conf_modify_lock(chat
, FALSE
);
8548 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
8550 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
8551 sipe_conf_modify_lock(chat
, TRUE
);
8555 sipe_chat_menu(PurpleChat
*chat
)
8557 PurpleMenuAction
*act
;
8558 PurpleConvChatBuddyFlags flags_us
;
8560 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
8561 struct sip_session
*session
;
8564 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
8565 if (!session
) return NULL
;
8567 self
= sip_uri_self(sip
);
8568 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
8570 if (session
->focus_uri
8571 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
8573 if (session
->locked
) {
8574 act
= purple_menu_action_new(_("Unlock"),
8575 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
8577 menu
= g_list_prepend(menu
, act
);
8579 act
= purple_menu_action_new(_("Lock"),
8580 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
8582 menu
= g_list_prepend(menu
, act
);
8586 menu
= g_list_reverse(menu
);
8593 sipe_blist_node_menu(PurpleBlistNode
*node
)
8595 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
8596 return sipe_buddy_menu((PurpleBuddy
*) node
);
8597 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
8598 return sipe_chat_menu((PurpleChat
*)node
);
8605 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
8607 gboolean ret
= TRUE
;
8608 char *uri
= trans
->payload
->data
;
8610 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
8611 PurpleBuddy
*pbuddy
;
8612 struct sipe_buddy
*sbuddy
;
8614 char *device_name
= NULL
;
8615 char *server_alias
= NULL
;
8616 char *phone_number
= NULL
;
8620 purple_debug_info("sipe", "Fetching %s's user info for %s\n", uri
, sip
->username
);
8622 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
8623 alias
= purple_buddy_get_local_alias(pbuddy
);
8627 //will query buddy UA's capabilities and send answer to log
8628 sipe_options_request(sip
, uri
);
8630 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
8633 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
8637 if (msg
->response
!= 200) {
8638 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
8640 xmlnode
*searchResults
;
8643 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
8644 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
8645 if (!searchResults
) {
8646 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
8647 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
8649 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
8650 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
8651 phone_number
= g_strdup(xmlnode_get_attrib(mrow
, "phone"));
8653 /* For 2007 system we will take this from ContactCard -
8654 * it has cleaner tel: URIs at least
8656 if (!sip
->ocs2007
) {
8657 char *tel_uri
= sip_to_tel_uri(phone_number
);
8658 /* trims its parameters, so call first */
8659 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, server_alias
);
8660 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
8661 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
8662 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, phone_number
);
8666 if (server_alias
&& strlen(server_alias
) > 0) {
8667 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
8669 if ((value
= xmlnode_get_attrib(mrow
, "title")) && strlen(value
) > 0) {
8670 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
8672 if ((value
= xmlnode_get_attrib(mrow
, "office")) && strlen(value
) > 0) {
8673 purple_notify_user_info_add_pair(info
, _("Office"), value
);
8675 if (phone_number
&& strlen(phone_number
) > 0) {
8676 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
8678 if ((value
= xmlnode_get_attrib(mrow
, "company")) && strlen(value
) > 0) {
8679 purple_notify_user_info_add_pair(info
, _("Company"), value
);
8681 if ((value
= xmlnode_get_attrib(mrow
, "city")) && strlen(value
) > 0) {
8682 purple_notify_user_info_add_pair(info
, _("City"), value
);
8684 if ((value
= xmlnode_get_attrib(mrow
, "state")) && strlen(value
) > 0) {
8685 purple_notify_user_info_add_pair(info
, _("State"), value
);
8687 if ((value
= xmlnode_get_attrib(mrow
, "country")) && strlen(value
) > 0) {
8688 purple_notify_user_info_add_pair(info
, _("Country"), value
);
8690 if (email
&& strlen(email
) > 0) {
8691 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
8695 xmlnode_free(searchResults
);
8698 purple_notify_user_info_add_section_break(info
);
8700 if (!server_alias
|| !strcmp("", server_alias
)) {
8701 g_free(server_alias
);
8702 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
8704 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
8708 /* present alias if it differs from server alias */
8709 if (alias
&& (!server_alias
|| strcmp(alias
, server_alias
)))
8711 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
8714 if (!email
|| !strcmp("", email
)) {
8716 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
8718 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
8722 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
8724 purple_notify_user_info_add_pair(info
, _("Site"), site
);
8728 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
8731 /* show a buddy's user info in a nice dialog box */
8732 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
8733 uri
, /* buddy's URI */
8735 NULL
, /* callback called when dialog closed */
8736 NULL
); /* userdata for callback */
8738 g_free(phone_number
);
8739 g_free(server_alias
);
8741 g_free(device_name
);
8747 * AD search first, LDAP based
8749 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
8751 struct sipe_account_data
*sip
= gc
->proto_data
;
8752 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
8753 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
8754 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
8755 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
8757 payload
->destroy
= g_free
;
8758 payload
->data
= g_strdup(username
);
8760 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
8761 send_soap_request_with_cb(sip
, domain_uri
, body
,
8762 (TransCallback
) process_get_info_response
, payload
);
8768 static PurplePlugin
*my_protocol
= NULL
;
8770 static PurplePluginProtocolInfo prpl_info
=
8772 OPT_PROTO_CHAT_TOPIC
,
8773 NULL
, /* user_splits */
8774 NULL
, /* protocol_options */
8775 NO_BUDDY_ICONS
, /* icon_spec */
8776 sipe_list_icon
, /* list_icon */
8777 NULL
, /* list_emblems */
8778 sipe_status_text
, /* status_text */
8779 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
8780 sipe_status_types
, /* away_states */
8781 sipe_blist_node_menu
, /* blist_node_menu */
8782 NULL
, /* chat_info */
8783 NULL
, /* chat_info_defaults */
8784 sipe_login
, /* login */
8785 sipe_close
, /* close */
8786 sipe_im_send
, /* send_im */
8787 NULL
, /* set_info */ // TODO maybe
8788 sipe_send_typing
, /* send_typing */
8789 sipe_get_info
, /* get_info */
8790 sipe_set_status
, /* set_status */
8791 sipe_set_idle
, /* set_idle */
8792 NULL
, /* change_passwd */
8793 sipe_add_buddy
, /* add_buddy */
8794 NULL
, /* add_buddies */
8795 sipe_remove_buddy
, /* remove_buddy */
8796 NULL
, /* remove_buddies */
8797 sipe_add_permit
, /* add_permit */
8798 sipe_add_deny
, /* add_deny */
8799 sipe_add_deny
, /* rem_permit */
8800 sipe_add_permit
, /* rem_deny */
8801 dummy_permit_deny
, /* set_permit_deny */
8802 NULL
, /* join_chat */
8803 NULL
, /* reject_chat */
8804 NULL
, /* get_chat_name */
8805 sipe_chat_invite
, /* chat_invite */
8806 sipe_chat_leave
, /* chat_leave */
8807 NULL
, /* chat_whisper */
8808 sipe_chat_send
, /* chat_send */
8809 sipe_keep_alive
, /* keepalive */
8810 NULL
, /* register_user */
8811 NULL
, /* get_cb_info */ // deprecated
8812 NULL
, /* get_cb_away */ // deprecated
8813 sipe_alias_buddy
, /* alias_buddy */
8814 sipe_group_buddy
, /* group_buddy */
8815 sipe_rename_group
, /* rename_group */
8816 NULL
, /* buddy_free */
8817 sipe_convo_closed
, /* convo_closed */
8818 purple_normalize_nocase
, /* normalize */
8819 NULL
, /* set_buddy_icon */
8820 sipe_remove_group
, /* remove_group */
8821 NULL
, /* get_cb_real_name */ // TODO?
8822 NULL
, /* set_chat_topic */
8823 NULL
, /* find_blist_chat */
8824 NULL
, /* roomlist_get_list */
8825 NULL
, /* roomlist_cancel */
8826 NULL
, /* roomlist_expand_category */
8827 NULL
, /* can_receive_file */
8828 NULL
, /* send_file */
8829 NULL
, /* new_xfer */
8830 NULL
, /* offline_message */
8831 NULL
, /* whiteboard_prpl_ops */
8832 sipe_send_raw
, /* send_raw */
8833 NULL
, /* roomlist_room_serialize */
8834 NULL
, /* unregister_user */
8835 NULL
, /* send_attention */
8836 NULL
, /* get_attention_types */
8837 #if !PURPLE_VERSION_CHECK(2,5,0)
8838 /* Backward compatibility when compiling against 2.4.x API */
8839 (void (*)(void)) /* _purple_reserved4 */
8841 sizeof(PurplePluginProtocolInfo
), /* struct_size */
8842 #if PURPLE_VERSION_CHECK(2,5,0)
8843 sipe_get_account_text_table
, /* get_account_text_table */
8844 #if PURPLE_VERSION_CHECK(2,6,0)
8845 NULL
, /* initiate_media */
8846 NULL
, /* get_media_caps */
8852 static PurplePluginInfo info
= {
8853 PURPLE_PLUGIN_MAGIC
,
8854 PURPLE_MAJOR_VERSION
,
8855 PURPLE_MINOR_VERSION
,
8856 PURPLE_PLUGIN_PROTOCOL
, /**< type */
8857 NULL
, /**< ui_requirement */
8859 NULL
, /**< dependencies */
8860 PURPLE_PRIORITY_DEFAULT
, /**< priority */
8861 "prpl-sipe", /**< id */
8862 "Office Communicator", /**< name */
8863 SIPE_VERSION
, /**< version */
8864 "Microsoft Office Communicator Protocol Plugin", /**< summary */
8865 "A plugin for the extended SIP/SIMPLE protocol used by " /**< description */
8866 "Microsoft Live/Office Communications Server (LCS2005/OCS2007+)", /**< description */
8867 "Anibal Avelar <avelar@gmail.com>, " /**< author */
8868 "Gabriel Burt <gburt@novell.com>, " /**< author */
8869 "Stefan Becker <stefan.becker@nokia.com>, " /**< author */
8870 "pier11 <pier11@operamail.com>", /**< author */
8871 "http://sipe.sourceforge.net/", /**< homepage */
8872 sipe_plugin_load
, /**< load */
8873 sipe_plugin_unload
, /**< unload */
8874 sipe_plugin_destroy
, /**< destroy */
8875 NULL
, /**< ui_info */
8876 &prpl_info
, /**< extra_info */
8885 static void sipe_plugin_destroy(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
8889 entry
= prpl_info
.protocol_options
;
8891 purple_account_option_destroy(entry
->data
);
8892 entry
= g_list_delete_link(entry
, entry
);
8894 prpl_info
.protocol_options
= NULL
;
8896 entry
= prpl_info
.user_splits
;
8898 purple_account_user_split_destroy(entry
->data
);
8899 entry
= g_list_delete_link(entry
, entry
);
8901 prpl_info
.user_splits
= NULL
;
8904 static void init_plugin(PurplePlugin
*plugin
)
8906 PurpleAccountUserSplit
*split
;
8907 PurpleAccountOption
*option
;
8912 purple_debug_info(PACKAGE
, "bindtextdomain = %s\n", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
8913 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s\n",
8914 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
8915 textdomain(GETTEXT_PACKAGE
);
8918 purple_plugin_register(plugin
);
8920 split
= purple_account_user_split_new(_("Login\n user or DOMAIN\\user or\n user@company.com"), NULL
, ',');
8921 purple_account_user_split_set_reverse(split
, FALSE
);
8922 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
8924 option
= purple_account_option_string_new(_("Server[:Port]\n(leave empty for auto-discovery)"), "server", "");
8925 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8927 option
= purple_account_option_list_new(_("Connection type"), "transport", NULL
);
8928 purple_account_option_add_list_item(option
, _("Auto"), "auto");
8929 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
8930 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
8931 purple_account_option_add_list_item(option
, _("UDP"), "udp");
8932 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8934 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
8935 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
8937 option
= purple_account_option_string_new(_("User Agent"), "useragent", "");
8938 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8941 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
8942 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8944 /* Suitable for sspi/NTLM, sspi/Kerberos and krb5 security mechanisms
8945 * No login/password is taken into account if this option present,
8946 * instead used default credentials stored in OS.
8948 option
= purple_account_option_bool_new(_("Use Single Sign-On"), "sso", TRUE
);
8949 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8952 option
= purple_account_option_list_new(_("Calendar source"), "calendar", NULL
);
8953 purple_account_option_add_list_item(option
, _("Exchange 2007/2010"), "EXCH");
8954 purple_account_option_add_list_item(option
, _("None"), "NONE");
8955 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8957 /** Example: https://server.company.com/EWS/Exchange.asmx */
8958 option
= purple_account_option_string_new(_("Email services URL\n(leave empty for auto-discovery)"), "email_url", "");
8959 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8961 option
= purple_account_option_string_new(_("Email address\n(if different from Username)"), "email", "");
8962 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8964 /** Example: DOMAIN\user or user@company.com */
8965 option
= purple_account_option_string_new(_("Email login\n(if different from Login)"), "email_login", "");
8966 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8968 option
= purple_account_option_string_new(_("Email password\n(if different from Password)"), "email_password", "");
8969 purple_account_option_set_masked(option
, TRUE
);
8970 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8972 my_protocol
= plugin
;
8975 PURPLE_INIT_PLUGIN(sipe
, init_plugin
, info
);