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"
64 #include "savedstatuses.h"
78 #include "sipe-chat.h"
79 #include "sipe-conf.h"
81 #include "sipe-dialog.h"
83 #include "sipe-session.h"
84 #include "sipe-utils.h"
86 #include "sipe-sign.h"
90 /* Backward compatibility when compiling against 2.4.x API */
91 #if !PURPLE_VERSION_CHECK(2,5,0)
92 #define PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY 0x0100
95 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
96 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
98 /* Keep in sync with sipe_transport_type! */
99 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
100 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
102 /* Status identifiers (see also: sipe_status_types()) */
103 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
104 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
105 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
106 /* PURPLE_STATUS_UNAVAILABLE: */
107 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
108 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
109 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
110 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
111 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
112 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
113 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
114 /* PURPLE_STATUS_AWAY: */
115 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
116 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
117 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
118 /** Reuters status (user settable) */
119 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
120 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
121 /* ??? PURPLE_STATUS_MOBILE */
122 /* ??? PURPLE_STATUS_TUNE */
124 /* Status attributes (see also sipe_status_types() */
125 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
127 static struct sipe_activity_map_struct
132 const char *status_id
;
134 } const sipe_activity_map
[] =
136 /* This has nothing to do with Availability numbers, like 3500 (online).
137 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
139 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
140 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
141 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , SIPE_STATUS_ID_IDLE
},
142 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
143 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("BusyIdle") , SIPE_STATUS_ID_BUSYIDLE
},
144 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
145 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
146 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
147 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , SIPE_STATUS_ID_LUNCH
},
148 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
149 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , SIPE_STATUS_ID_ON_PHONE
},
150 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , SIPE_STATUS_ID_IN_CONF
},
151 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , SIPE_STATUS_ID_IN_MEETING
},
152 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
153 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
155 /** @param x is sipe_activity */
156 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
159 /* Action name templates */
160 #define ACTION_NAME_PRESENCE "<presence><%s>"
163 sipe_get_activity_by_token(const char *token
)
167 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
169 if (!strcmp(token
, sipe_activity_map
[i
].token
))
170 return sipe_activity_map
[i
].type
;
173 return sipe_activity_map
[0].type
;
177 sipe_get_activity_desc_by_token(const char *token
)
179 if (!token
) return NULL
;
181 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token
));
184 /** Allows to send typed messages from chat window again after account reinstantiation. */
186 sipe_rejoin_chat(PurpleConversation
*conv
)
188 if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
&&
189 PURPLE_CONV_CHAT(conv
)->left
)
191 PURPLE_CONV_CHAT(conv
)->left
= FALSE
;
192 purple_conversation_update(conv
, PURPLE_CONV_UPDATE_CHATLEFT
);
196 static char *genbranch()
198 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
199 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
200 rand() & 0xFFFF, rand() & 0xFFFF);
204 static char *default_ua
= NULL
;
206 sipe_get_useragent(struct sipe_account_data
*sip
)
208 const char *useragent
= purple_account_get_string(sip
->account
, "useragent", "");
209 if (is_empty(useragent
)) {
211 /*@TODO: better approach to define _user_ OS, it's version and host architecture */
213 #if defined(__linux__) || defined(__linux) || defined(__LINUX__)
214 #define SIPE_TARGET_PLATFORM "linux"
215 #elif defined(__NetBSD__) ||defined( __OpenBSD__) || defined(__FreeBSD__)
216 #define SIPE_TARGET_PLATFORM "bsd"
217 # elif defined(__APPLE__) || defined(__MACOS__)
218 #define SIPE_TARGET_PLATFORM "macosx"
219 #elif defined(__solaris__) || defined(__sun)
220 #define SIPE_TARGET_PLATFORM "sun"
221 #elif defined(_WIN32)
222 #define SIPE_TARGET_PLATFORM "win"
224 #define SIPE_TARGET_PLATFORM "generic"
227 #if defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)
228 #define SIPE_TARGET_ARCH "x86_64"
229 #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386)
230 #define SIPE_TARGET_ARCH "i386"
231 #elif defined(__ppc64__)
232 #define SIPE_TARGET_ARCH "ppc64"
233 #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR)
234 #define SIPE_TARGET_ARCH "ppc"
236 #define SIPE_TARGET_ARCH "other"
239 default_ua
= g_strdup_printf("Purple/%s Sipe/%s (%s-%s; %s)",
240 purple_core_get_version(),
242 SIPE_TARGET_PLATFORM
,
244 sip
->server_version
? sip
->server_version
: "");
246 useragent
= default_ua
;
251 static const char *sipe_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount
*a
,
252 SIPE_UNUSED_PARAMETER PurpleBuddy
*b
)
257 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
259 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
);
261 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
262 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
265 static void sipe_close(PurpleConnection
*gc
);
267 static void send_presence_status(struct sipe_account_data
*sip
);
269 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
271 static void sipe_keep_alive(PurpleConnection
*gc
)
273 struct sipe_account_data
*sip
= gc
->proto_data
;
274 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
275 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
276 gchar buf
[2] = {0, 0};
277 purple_debug_info("sipe", "sending keep alive\n");
278 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
280 time_t now
= time(NULL
);
281 if ((sip
->keepalive_timeout
> 0) &&
282 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
283 #if PURPLE_VERSION_CHECK(2,4,0)
284 && ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
287 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
288 sendout_pkt(gc
, "\r\n\r\n");
289 sip
->last_keepalive
= now
;
294 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
296 struct sip_connection
*ret
= NULL
;
297 GSList
*entry
= sip
->openconns
;
300 if (ret
->fd
== fd
) return ret
;
306 static void sipe_auth_free(struct sip_auth
*auth
)
308 g_free(auth
->opaque
);
312 g_free(auth
->target
);
314 auth
->type
= AUTH_TYPE_UNSET
;
317 g_free(auth
->gssapi_data
);
318 auth
->gssapi_data
= NULL
;
319 sip_sec_destroy_context(auth
->gssapi_context
);
320 auth
->gssapi_context
= NULL
;
323 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
325 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
327 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
331 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
333 struct sip_connection
*conn
= connection_find(sip
, fd
);
335 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
336 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
342 static void connection_free_all(struct sipe_account_data
*sip
)
344 struct sip_connection
*ret
= NULL
;
345 GSList
*entry
= sip
->openconns
;
348 connection_remove(sip
, ret
->fd
);
349 entry
= sip
->openconns
;
353 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
356 const char *authuser
= sip
->authuser
;
360 if (!authuser
|| strlen(authuser
) < 1) {
361 authuser
= sip
->username
;
364 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
365 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
367 // If we have a signature for the message, include that
368 if (msg
->signature
) {
369 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
);
372 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
373 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
377 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
380 purple_account_get_bool(sip
->account
, "sso", TRUE
),
381 sip
->authdomain
? sip
->authdomain
: "",
386 if (!gssapi_data
|| !auth
->gssapi_context
) {
387 sip
->gc
->wants_to_die
= TRUE
;
388 purple_connection_error(sip
->gc
, _("Failed to authenticate to server"));
392 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
393 ret
= g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth_protocol
, opaque
, auth
->realm
, auth
->target
, gssapi_data
);
399 return g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth_protocol
, auth
->realm
, auth
->target
);
401 } else { /* Digest */
403 /* Calculate new session key */
405 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
406 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
407 authuser
, auth
->realm
, sip
->password
,
408 auth
->gssapi_data
, NULL
);
411 sprintf(noncecount
, "%08d", auth
->nc
++);
412 response
= purple_cipher_http_digest_calculate_response("md5",
413 msg
->method
, msg
->target
, NULL
, NULL
,
414 auth
->gssapi_data
, noncecount
, NULL
,
416 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
418 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
);
424 static char *parse_attribute(const char *attrname
, const char *source
)
426 const char *tmp
, *tmp2
;
428 int len
= strlen(attrname
);
430 if (!strncmp(source
, attrname
, len
)) {
432 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
434 retval
= g_strndup(tmp
, tmp2
- tmp
);
436 retval
= g_strdup(tmp
);
442 static void fill_auth(gchar
*hdr
, struct sip_auth
*auth
)
448 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
452 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
453 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
454 auth
->type
= AUTH_TYPE_NTLM
;
457 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
458 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
459 auth
->type
= AUTH_TYPE_KERBEROS
;
463 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
464 auth
->type
= AUTH_TYPE_DIGEST
;
468 parts
= g_strsplit(hdr
, "\", ", 0);
469 for (i
= 0; parts
[i
]; i
++) {
472 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
474 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
475 g_free(auth
->gssapi_data
);
476 auth
->gssapi_data
= tmp
;
478 if (auth
->type
== AUTH_TYPE_NTLM
) {
479 /* NTLM module extracts nonce from gssapi-data */
483 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
484 /* Only used with AUTH_TYPE_DIGEST */
485 g_free(auth
->gssapi_data
);
486 auth
->gssapi_data
= tmp
;
487 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
488 g_free(auth
->opaque
);
490 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
494 if (auth
->type
== AUTH_TYPE_DIGEST
) {
495 /* Throw away old session key */
496 g_free(auth
->opaque
);
501 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
502 g_free(auth
->target
);
511 static void sipe_canwrite_cb(gpointer data
,
512 SIPE_UNUSED_PARAMETER gint source
,
513 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
515 PurpleConnection
*gc
= data
;
516 struct sipe_account_data
*sip
= gc
->proto_data
;
520 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
522 if (max_write
== 0) {
523 if (sip
->tx_handler
!= 0){
524 purple_input_remove(sip
->tx_handler
);
530 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
532 if (written
< 0 && errno
== EAGAIN
)
534 else if (written
<= 0) {
535 /*TODO: do we really want to disconnect on a failure to write?*/
536 purple_connection_error(gc
, _("Could not write"));
540 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
543 static void sipe_canwrite_cb_ssl(gpointer data
,
544 SIPE_UNUSED_PARAMETER gint src
,
545 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
547 PurpleConnection
*gc
= data
;
548 struct sipe_account_data
*sip
= gc
->proto_data
;
552 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
554 if (max_write
== 0) {
555 if (sip
->tx_handler
!= 0) {
556 purple_input_remove(sip
->tx_handler
);
562 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
564 if (written
< 0 && errno
== EAGAIN
)
566 else if (written
<= 0) {
567 /*TODO: do we really want to disconnect on a failure to write?*/
568 purple_connection_error(gc
, _("Could not write"));
572 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
575 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
577 static void send_later_cb(gpointer data
, gint source
,
578 SIPE_UNUSED_PARAMETER
const gchar
*error
)
580 PurpleConnection
*gc
= data
;
581 struct sipe_account_data
*sip
;
582 struct sip_connection
*conn
;
584 if (!PURPLE_CONNECTION_IS_VALID(gc
))
592 purple_connection_error(gc
, _("Could not connect"));
596 sip
= gc
->proto_data
;
598 sip
->connecting
= FALSE
;
599 sip
->last_keepalive
= time(NULL
);
601 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
603 /* If there is more to write now, we need to register a handler */
604 if (sip
->txbuf
->bufused
> 0)
605 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
607 conn
= connection_create(sip
, source
);
608 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
611 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
613 struct sipe_account_data
*sip
;
615 if (!PURPLE_CONNECTION_IS_VALID(gc
))
617 if (gsc
) purple_ssl_close(gsc
);
621 sip
= gc
->proto_data
;
624 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
625 sip
->connecting
= FALSE
;
626 sip
->last_keepalive
= time(NULL
);
628 connection_create(sip
, gsc
->fd
);
630 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
635 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
636 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
638 PurpleConnection
*gc
= data
;
639 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
640 if (sip
== NULL
) return;
642 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
644 /* If there is more to write now */
645 if (sip
->txbuf
->bufused
> 0) {
646 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
651 static void sendlater(PurpleConnection
*gc
, const char *buf
)
653 struct sipe_account_data
*sip
= gc
->proto_data
;
655 if (!sip
->connecting
) {
656 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
657 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
658 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
660 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
661 purple_connection_error(gc
, _("Could not create socket"));
664 sip
->connecting
= TRUE
;
667 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
668 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
670 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
673 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
675 struct sipe_account_data
*sip
= gc
->proto_data
;
676 time_t currtime
= time(NULL
);
677 int writelen
= strlen(buf
);
680 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sending - %s######\n%s######\n", ctime(&currtime
), tmp
= fix_newlines(buf
));
682 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
683 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
684 purple_debug_info("sipe", "could not send packet\n");
693 if (sip
->tx_handler
) {
698 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
700 ret
= write(sip
->fd
, buf
, writelen
);
704 if (ret
< 0 && errno
== EAGAIN
)
706 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
711 if (ret
< writelen
) {
712 if (!sip
->tx_handler
){
714 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
717 sip
->tx_handler
= purple_input_add(sip
->fd
,
718 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
723 /* XXX: is it OK to do this? You might get part of a request sent
724 with part of another. */
725 if (sip
->txbuf
->bufused
> 0)
726 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
728 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
734 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
736 sendout_pkt(gc
, buf
);
740 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
742 GSList
*tmp
= msg
->headers
;
745 GString
*outstr
= g_string_new("");
746 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
748 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
749 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
750 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
751 tmp
= g_slist_next(tmp
);
753 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
754 sendout_pkt(sip
->gc
, outstr
->str
);
755 g_string_free(outstr
, TRUE
);
758 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
762 if (sip
->registrar
.type
== AUTH_TYPE_UNSET
) {
766 if (sip
->registrar
.gssapi_context
) {
767 struct sipmsg_breakdown msgbd
;
768 gchar
*signature_input_str
;
770 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
771 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
772 sip
->registrar
.ntlm_num
++;
773 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
774 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
775 if (signature_input_str
!= NULL
) {
776 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
777 msg
->signature
= signature_hex
;
778 msg
->rand
= g_strdup(msgbd
.rand
);
779 msg
->num
= g_strdup(msgbd
.num
);
780 g_free(signature_input_str
);
782 sipmsg_breakdown_free(&msgbd
);
785 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
786 buf
= auth_header(sip
, &sip
->registrar
, msg
);
788 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
791 } 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")) {
792 sip
->registrar
.nc
= 3;
794 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
796 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
799 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
804 buf
= auth_header(sip
, &sip
->registrar
, msg
);
805 sipmsg_add_header_now_pos(msg
, "Proxy-Authorization", buf
, 5);
808 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
812 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
813 const char *text
, const char *body
)
817 GString
*outstr
= g_string_new("");
818 struct sipe_account_data
*sip
= gc
->proto_data
;
821 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
823 contact
= get_contact(sip
);
824 sipmsg_add_header(msg
, "Contact", contact
);
829 sprintf(len
, "%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
830 sipmsg_add_header(msg
, "Content-Length", len
);
832 sipmsg_add_header(msg
, "Content-Length", "0");
835 msg
->response
= code
;
837 sipmsg_strip_headers(msg
, keepers
);
838 sipmsg_merge_new_headers(msg
);
839 sign_outgoing_message(msg
, sip
, msg
->method
);
841 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
844 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
845 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
847 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
848 tmp
= g_slist_next(tmp
);
850 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
851 sendout_pkt(gc
, outstr
->str
);
852 g_string_free(outstr
, TRUE
);
855 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
857 if (sip
->transactions
) {
858 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
859 purple_debug_info("sipe", "sip->transactions count:%d after removal\n", g_slist_length(sip
->transactions
));
861 if (trans
->msg
) sipmsg_free(trans
->msg
);
862 if (trans
->payload
) {
863 (*trans
->payload
->destroy
)(trans
->payload
->data
);
864 g_free(trans
->payload
);
871 static struct transaction
*
872 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
874 gchar
*call_id
= NULL
;
876 struct transaction
*trans
= g_new0(struct transaction
, 1);
878 trans
->time
= time(NULL
);
879 trans
->msg
= (struct sipmsg
*)msg
;
880 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
881 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
882 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
883 trans
->callback
= callback
;
884 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
885 purple_debug_info("sipe", "sip->transactions count:%d after addition\n", g_slist_length(sip
->transactions
));
889 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
891 struct transaction
*trans
;
892 GSList
*transactions
= sip
->transactions
;
893 gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
894 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
895 gchar
*key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
897 while (transactions
) {
898 trans
= transactions
->data
;
899 if (!g_strcasecmp(trans
->key
, key
)) {
903 transactions
= transactions
->next
;
911 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
912 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
913 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
915 struct sipe_account_data
*sip
= gc
->proto_data
;
916 const char *addh
= "";
919 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
920 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
921 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
922 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
923 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
924 gchar
*route
= g_strdup("");
925 gchar
*epid
= get_epid(sip
);
926 int cseq
= dialog
? ++dialog
->cseq
: 1 /* as Call-Id is new in this case */;
927 struct transaction
*trans
= NULL
;
929 if (dialog
&& dialog
->routes
)
931 GSList
*iter
= dialog
->routes
;
936 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
938 iter
= g_slist_next(iter
);
942 if (!ourtag
&& !dialog
) {
946 if (!strcmp(method
, "REGISTER")) {
947 if (sip
->regcallid
) {
949 callid
= g_strdup(sip
->regcallid
);
951 sip
->regcallid
= g_strdup(callid
);
956 if (addheaders
) addh
= addheaders
;
958 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
959 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
960 "From: <sip:%s>%s%s;epid=%s\r\n"
961 "To: <%s>%s%s%s%s\r\n"
962 "Max-Forwards: 70\r\n"
967 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
969 dialog
&& dialog
->request
? dialog
->request
: url
,
970 TRANSPORT_DESCRIPTOR
,
971 purple_network_get_my_ip(-1),
973 branch
? ";branch=" : "",
974 branch
? branch
: "",
976 ourtag
? ";tag=" : "",
977 ourtag
? ourtag
: "",
980 theirtag
? ";tag=" : "",
981 theirtag
? theirtag
: "",
982 theirepid
? ";epid=" : "",
983 theirepid
? theirepid
: "",
986 sipe_get_useragent(sip
),
990 body
? (gsize
) strlen(body
) : 0,
994 //printf ("parsing msg buf:\n%s\n\n", buf);
995 msg
= sipmsg_parse_msg(buf
);
1006 sign_outgoing_message (msg
, sip
, method
);
1008 buf
= sipmsg_to_string (msg
);
1010 /* add to ongoing transactions */
1011 /* ACK isn't supposed to be answered ever. So we do not keep transaction for it. */
1012 if (strcmp(method
, "ACK")) {
1013 trans
= transactions_add_buf(sip
, msg
, tc
);
1017 sendout_pkt(gc
, buf
);
1024 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
1027 send_soap_request_with_cb(struct sipe_account_data
*sip
,
1030 TransCallback callback
,
1031 struct transaction_payload
*payload
)
1033 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sip
);
1034 gchar
*contact
= get_contact(sip
);
1035 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
1036 "Content-Type: application/SOAP+xml\r\n",contact
);
1038 struct transaction
*trans
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1039 trans
->payload
= payload
;
1046 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1048 send_soap_request_with_cb(sip
, NULL
, body
, NULL
, NULL
);
1051 static char *get_contact_register(struct sipe_account_data
*sip
)
1053 char *epid
= get_epid(sip
);
1054 char *uuid
= generateUUIDfromEPID(epid
);
1055 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
);
1061 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1069 if (!sip
->sipdomain
) return;
1071 uri
= sip_uri_from_name(sip
->sipdomain
);
1072 expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1073 to
= sip_uri_self(sip
);
1074 contact
= get_contact_register(sip
);
1075 hdr
= g_strdup_printf("Contact: %s\r\n"
1076 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1077 "Event: registration\r\n"
1078 "Allow-Events: presence\r\n"
1079 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1080 "%s", contact
, expires
);
1084 sip
->registerstatus
= 1;
1086 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1087 process_register_response
);
1094 static void do_register_cb(struct sipe_account_data
*sip
,
1095 SIPE_UNUSED_PARAMETER
void *unused
)
1097 do_register_exp(sip
, -1);
1098 sip
->reregister_set
= FALSE
;
1101 static void do_register(struct sipe_account_data
*sip
)
1103 do_register_exp(sip
, -1);
1107 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1109 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1110 send_soap_request(sip
, body
);
1115 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1118 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1120 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1123 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1127 void sipe_auth_user_cb(void * data
)
1129 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1132 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1137 void sipe_deny_user_cb(void * data
)
1139 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1142 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1147 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1149 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1150 sipe_contact_allow_deny(sip
, name
, TRUE
);
1154 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1156 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1157 sipe_contact_allow_deny(sip
, name
, FALSE
);
1161 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1163 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1164 sipe_contact_set_acl(sip, name, "");
1168 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1172 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1173 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1175 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1177 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1178 if (!watchers
) return;
1180 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1181 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1182 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1183 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1185 // TODO pull out optional displayName to pass as alias
1187 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1188 job
->who
= remote_user
;
1190 purple_account_request_authorization(
1204 xmlnode_free(watchers
);
1209 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1211 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1212 if (!purple_group
) {
1213 purple_group
= purple_group_new(group
->name
);
1214 purple_blist_add_group(purple_group
, NULL
);
1218 group
->purple_group
= purple_group
;
1219 sip
->groups
= g_slist_append(sip
->groups
, group
);
1220 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1222 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1226 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1228 struct sipe_group
*group
;
1234 entry
= sip
->groups
;
1236 group
= entry
->data
;
1237 if (group
->id
== id
) {
1240 entry
= entry
->next
;
1245 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1247 struct sipe_group
*group
;
1249 if (!sip
|| !name
) {
1253 entry
= sip
->groups
;
1255 group
= entry
->data
;
1256 if (!strcmp(group
->name
, name
)) {
1259 entry
= entry
->next
;
1265 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1268 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1269 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1270 send_soap_request(sip
, body
);
1272 g_free(group
->name
);
1273 group
->name
= g_strdup(name
);
1277 * Only appends if no such value already stored.
1280 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1281 GSList
* res
= list
;
1282 if (!g_slist_find_custom(list
, data
, func
)) {
1283 res
= g_slist_insert_sorted(list
, data
, func
);
1289 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1290 return group1
->id
- group2
->id
;
1294 * Returns string like "2 4 7 8" - group ids buddy belong to.
1297 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1300 //creating array from GList, converting int to gchar*
1301 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1302 GSList
*entry
= buddy
->groups
;
1304 struct sipe_group
* group
= entry
->data
;
1305 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1306 entry
= entry
->next
;
1310 res
= g_strjoinv(" ", ids_arr
);
1311 g_strfreev(ids_arr
);
1316 * Sends buddy update to server
1319 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1321 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1322 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1324 if (buddy
&& purple_buddy
) {
1325 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1327 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1328 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1330 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1331 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1333 send_soap_request(sip
, body
);
1339 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1341 if (msg
->response
== 200) {
1342 struct sipe_group
*group
;
1343 struct group_user_context
*ctx
= trans
->payload
->data
;
1347 struct sipe_buddy
*buddy
;
1349 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1354 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1360 group_id
= xmlnode_get_data(node
);
1366 group
= g_new0(struct sipe_group
, 1);
1367 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1369 group
->name
= g_strdup(ctx
->group_name
);
1371 sipe_group_add(sip
, group
);
1373 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1375 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1378 sipe_group_set_user(sip
, ctx
->user_name
);
1386 static void sipe_group_context_destroy(gpointer data
)
1388 struct group_user_context
*ctx
= data
;
1389 g_free(ctx
->group_name
);
1390 g_free(ctx
->user_name
);
1394 static void sipe_group_create (struct sipe_account_data
*sip
, const gchar
*name
, const gchar
* who
)
1396 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
1397 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
1399 ctx
->group_name
= g_strdup(name
);
1400 ctx
->user_name
= g_strdup(who
);
1401 payload
->destroy
= sipe_group_context_destroy
;
1402 payload
->data
= ctx
;
1404 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1405 send_soap_request_with_cb(sip
, NULL
, body
, process_add_group_response
, payload
);
1410 * Data structure for scheduled actions
1413 struct scheduled_action
{
1416 * Format is <Event>[<Data>...]
1417 * Example: <presence><sip:user@domain.com> or <registration>
1420 guint timeout_handler
;
1421 gboolean repetitive
;
1423 GDestroyNotify destroy
;
1424 struct sipe_account_data
*sip
;
1430 * Should return FALSE if repetitive action is not needed
1432 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1435 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1436 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1437 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1438 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1439 ret
= sched_action
->repetitive
;
1440 if (sched_action
->destroy
) {
1441 (*sched_action
->destroy
)(sched_action
->payload
);
1443 g_free(sched_action
->name
);
1444 g_free(sched_action
);
1449 * Kills action timer effectively cancelling
1452 * @param name of action
1454 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1458 if (!sip
->timeouts
|| !name
) return;
1460 entry
= sip
->timeouts
;
1462 struct scheduled_action
*sched_action
= entry
->data
;
1463 if(!strcmp(sched_action
->name
, name
)) {
1464 GSList
*to_delete
= entry
;
1465 entry
= entry
->next
;
1466 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1467 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1468 purple_timeout_remove(sched_action
->timeout_handler
);
1469 if (sched_action
->destroy
) {
1470 (*sched_action
->destroy
)(sched_action
->payload
);
1472 g_free(sched_action
->name
);
1473 g_free(sched_action
);
1475 entry
= entry
->next
;
1481 sipe_schedule_action0(const gchar
*name
,
1485 GDestroyNotify destroy
,
1486 struct sipe_account_data
*sip
,
1489 struct scheduled_action
*sched_action
;
1491 /* Make sure each action only exists once */
1492 sipe_cancel_scheduled_action(sip
, name
);
1494 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1495 sched_action
= g_new0(struct scheduled_action
, 1);
1496 sched_action
->repetitive
= FALSE
;
1497 sched_action
->name
= g_strdup(name
);
1498 sched_action
->action
= action
;
1499 sched_action
->destroy
= destroy
;
1500 sched_action
->sip
= sip
;
1501 sched_action
->payload
= payload
;
1502 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1503 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1504 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1505 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1509 sipe_schedule_action(const gchar
*name
,
1512 GDestroyNotify destroy
,
1513 struct sipe_account_data
*sip
,
1516 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1520 * Same as sipe_schedule_action() but timeout is in milliseconds.
1523 sipe_schedule_action_msec(const gchar
*name
,
1526 GDestroyNotify destroy
,
1527 struct sipe_account_data
*sip
,
1530 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1534 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1535 time_t calculate_from
);
1538 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
);
1541 sipe_get_status_by_availability(int avail
,
1542 const char* activity
);
1545 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
1546 const char *status_id
,
1547 const char *message
,
1548 time_t do_not_publish
[]);
1551 sipe_apply_calendar_status(struct sipe_account_data
*sip
,
1552 struct sipe_buddy
*sbuddy
,
1553 const char *status_id
)
1555 time_t cal_avail_since
;
1556 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
1558 gchar
*self_uri
= sip_uri_self(sip
);
1560 if (!sbuddy
) return;
1562 if (cal_status
< SIPE_CAL_NO_DATA
) {
1563 purple_debug_info("sipe", "update_calendar_status_cb: cal_status : %d for %s\n", cal_status
, sbuddy
->name
);
1564 purple_debug_info("sipe", "update_calendar_status_cb: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
1567 /* scheduled Cal update call */
1569 status_id
= sbuddy
->last_non_cal_status_id
;
1570 g_free(sbuddy
->activity
);
1571 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
1574 /* adjust to calendar status */
1575 if (cal_status
!= SIPE_CAL_NO_DATA
) {
1576 purple_debug_info("sipe", "update_calendar_status_cb: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
1578 if (cal_status
== SIPE_CAL_BUSY
1579 && cal_avail_since
> sbuddy
->user_avail_since
1580 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
1582 status_id
= SIPE_STATUS_ID_IN_MEETING
;
1583 g_free(sbuddy
->activity
);
1584 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
));
1586 avail
= sipe_get_availability_by_status(status_id
, NULL
);
1588 purple_debug_info("sipe", "update_calendar_status_cb: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
1589 if (cal_avail_since
> sbuddy
->activity_since
) {
1590 if (cal_status
== SIPE_CAL_OOF
1591 && avail
>= 15000) /* 12000 in 2007 */
1593 g_free(sbuddy
->activity
);
1594 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
1599 /* then set status_id actually */
1600 purple_debug_info("sipe", "sipe_got_user_status: to %s for %s\n", status_id
, sbuddy
->name
);
1601 purple_prpl_got_user_status(sip
->account
, sbuddy
->name
, status_id
, NULL
);
1603 /* set our account state to the one in roaming (including calendar info) */
1604 if (sip
->initial_state_published
&& !strcmp(sbuddy
->name
, self_uri
)) {
1605 if (!strcmp(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
1606 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
1609 purple_debug_info("sipe", "sipe_got_user_status: to %s for the account\n", sip
->status
);
1610 sipe_set_purple_account_status_and_note(sip
->account
, status_id
, sip
->note
, sip
->do_not_publish
);
1616 sipe_got_user_status(struct sipe_account_data
*sip
,
1618 const char *status_id
)
1620 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
1622 if (!sbuddy
) return;
1624 /* Check if on 2005 system contact's calendar,
1625 * then set/preserve it.
1627 if (!sip
->ocs2007
) {
1628 sipe_apply_calendar_status(sip
, sbuddy
, status_id
);
1630 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
1635 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
1636 struct sipe_buddy
*sbuddy
,
1637 struct sipe_account_data
*sip
)
1639 sipe_apply_calendar_status(sip
, sbuddy
, NULL
);
1643 * Updates contact's status
1644 * based on their calendar information.
1646 * Applicability: 2005 systems
1649 update_calendar_status(struct sipe_account_data
*sip
)
1651 purple_debug_info("sipe", "update_calendar_status() started.\n");
1652 g_hash_table_foreach(sip
->buddies
, (GHFunc
)update_calendar_status_cb
, (gpointer
)sip
);
1654 /* repeat scheduling */
1655 sipe_sched_calendar_status_update(sip
, time(NULL
) + 3*60 /* 3 min */);
1659 * Schedules process of contacts' status update
1660 * based on their calendar information.
1661 * Should be scheduled to the beginning of every
1662 * 15 min interval, like:
1663 * 13:00, 13:15, 13:30, 13:45, etc.
1665 * Applicability: 2005 systems
1668 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1669 time_t calculate_from
)
1671 int interval
= 15*60;
1672 /** start of the beginning of closest 15 min interval. */
1673 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1675 purple_debug_info("sipe", "sipe_sched_calendar_status_update: calculate_from time: %s",
1676 asctime(localtime(&calculate_from
)));
1677 purple_debug_info("sipe", "sipe_sched_calendar_status_update: next start time : %s",
1678 asctime(localtime(&next_start
)));
1680 sipe_schedule_action("<+2005-cal-status>",
1681 (int)(next_start
- time(NULL
)),
1682 (Action
)update_calendar_status
,
1689 * Schedules process of self status publish
1690 * based on own calendar information.
1691 * Should be scheduled to the beginning of every
1692 * 15 min interval, like:
1693 * 13:00, 13:15, 13:30, 13:45, etc.
1695 * Applicability: 2007+ systems
1698 sipe_sched_calendar_status_self_publish(struct sipe_account_data
*sip
,
1699 time_t calculate_from
)
1701 int interval
= 5*60;
1702 /** start of the beginning of closest 5 min interval. */
1703 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1705 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: calculate_from time: %s",
1706 asctime(localtime(&calculate_from
)));
1707 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: next start time : %s",
1708 asctime(localtime(&next_start
)));
1710 sipe_schedule_action("<+2007-cal-status>",
1711 (int)(next_start
- time(NULL
)),
1712 (Action
)publish_calendar_status_self
,
1718 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1720 /** Should be g_free()'d
1723 sipe_get_subscription_key(gchar
*event
,
1728 if (is_empty(event
)) return NULL
;
1730 if (event
&& !g_ascii_strcasecmp(event
, "presence")) {
1731 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1732 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, with
);
1734 /* @TODO drop participated buddies' just_added flag */
1736 /* Subscription is identified by <event> key */
1737 key
= g_strdup_printf("<%s>", event
);
1743 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
1744 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
1746 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
1747 gchar
*event
= sipmsg_find_header(msg
, "Event");
1750 /* The case with 2005 Public IM Connectivity (PIC) - no Event header */
1752 struct sipmsg
*request_msg
= trans
->msg
;
1753 event
= sipmsg_find_header(request_msg
, "Event");
1756 key
= sipe_get_subscription_key(event
, with
);
1758 /* 200 OK; 481 Call Leg Does Not Exist */
1759 if (key
&& (msg
->response
== 200 || msg
->response
== 481)) {
1760 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
1761 g_hash_table_remove(sip
->subscriptions
, key
);
1762 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
1766 /* create/store subscription dialog if not yet */
1767 if (msg
->response
== 200) {
1768 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1769 gchar
*cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
1772 struct sip_subscription
*subscription
= g_new0(struct sip_subscription
, 1);
1773 g_hash_table_insert(sip
->subscriptions
, g_strdup(key
), subscription
);
1775 subscription
->dialog
.callid
= g_strdup(callid
);
1776 subscription
->dialog
.cseq
= atoi(cseq
);
1777 subscription
->dialog
.with
= g_strdup(with
);
1778 subscription
->event
= g_strdup(event
);
1779 sipe_dialog_parse(&subscription
->dialog
, msg
, TRUE
);
1781 purple_debug_info("sipe", "process_subscribe_response: subscription dialog added for: %s\n", key
);
1790 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1792 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1797 static void sipe_subscribe_resource_uri(const char *name
,
1798 SIPE_UNUSED_PARAMETER gpointer value
,
1799 gchar
**resources_uri
)
1801 gchar
*tmp
= *resources_uri
;
1802 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1806 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1808 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1809 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1810 gchar
*tmp
= *resources_uri
;
1812 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
1814 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
1819 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1820 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1821 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1822 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1823 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1826 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1829 gchar
*contact
= get_contact(sip
);
1832 gchar
*require
= "";
1834 gchar
*autoextend
= "";
1835 gchar
*content_type
;
1836 struct sip_dialog
*dialog
;
1839 require
= ", categoryList";
1840 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1841 content_type
= "application/msrtc-adrl-categorylist+xml";
1842 content
= g_strdup_printf(
1843 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1844 "<action name=\"subscribe\" id=\"63792024\">\n"
1845 "<adhocList>\n%s</adhocList>\n"
1846 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1847 "<category name=\"calendarData\"/>\n"
1848 "<category name=\"contactCard\"/>\n"
1849 "<category name=\"note\"/>\n"
1850 "<category name=\"state\"/>\n"
1853 "</batchSub>", sip
->username
, resources_uri
);
1855 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1856 content_type
= "application/adrl+xml";
1857 content
= g_strdup_printf(
1858 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1859 "<create xmlns=\"\">\n%s</create>\n"
1860 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1862 g_free(resources_uri
);
1864 request
= g_strdup_printf(
1865 "Require: adhoclist%s\r\n"
1866 "Supported: eventlist\r\n"
1867 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1868 "Supported: ms-piggyback-first-notify\r\n"
1869 "%sSupported: ms-benotify\r\n"
1870 "Proxy-Require: ms-benotify\r\n"
1871 "Event: presence\r\n"
1872 "Content-Type: %s\r\n"
1873 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1876 /* subscribe to buddy presence */
1877 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1878 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1879 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1880 purple_debug_info("sipe", "sipe_subscribe_presence_batched_to: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
1882 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
1890 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
,
1891 SIPE_UNUSED_PARAMETER
void *unused
)
1893 gchar
*to
= sip_uri_self(sip
);
1894 gchar
*resources_uri
= g_strdup("");
1896 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1898 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1901 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1904 struct presence_batched_routed
{
1909 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1911 struct presence_batched_routed
*data
= payload
;
1912 GSList
*buddies
= data
->buddies
;
1914 g_free(buddies
->data
);
1915 buddies
= buddies
->next
;
1917 g_slist_free(data
->buddies
);
1922 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
1924 struct presence_batched_routed
*data
= payload
;
1925 GSList
*buddies
= data
->buddies
;
1926 gchar
*resources_uri
= g_strdup("");
1928 gchar
*tmp
= resources_uri
;
1929 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
1931 buddies
= buddies
->next
;
1933 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
1934 g_strdup(data
->host
));
1938 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1939 * The user sends a single SUBSCRIBE request to the subscribed contact.
1940 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1944 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
1948 gchar
*to
= sip_uri((char *)buddy_name
);
1949 gchar
*tmp
= get_contact(sip
);
1951 gchar
*content
= NULL
;
1952 gchar
*autoextend
= "";
1953 gchar
*content_type
= "";
1954 struct sip_dialog
*dialog
;
1955 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, to
);
1956 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1958 if (sbuddy
) sbuddy
->just_added
= FALSE
;
1961 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
1963 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1966 request
= g_strdup_printf(
1967 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1968 "Supported: ms-piggyback-first-notify\r\n"
1969 "%s%sSupported: ms-benotify\r\n"
1970 "Proxy-Require: ms-benotify\r\n"
1971 "Event: presence\r\n"
1972 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
1975 content
= g_strdup_printf(
1976 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1977 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1978 "<resource uri=\"%s\"%s\n"
1980 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1981 "<category name=\"calendarData\"/>\n"
1982 "<category name=\"contactCard\"/>\n"
1983 "<category name=\"note\"/>\n"
1984 "<category name=\"state\"/>\n"
1987 "</batchSub>", sip
->username
, to
, context
);
1992 /* subscribe to buddy presence */
1993 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1994 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1995 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1996 purple_debug_info("sipe", "sipe_subscribe_presence_single: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
1998 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
2006 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
2008 purple_debug_info("sipe", "sipe_set_status: status=%s\n", purple_status_get_id(status
));
2010 if (!purple_status_is_active(status
))
2014 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
2018 time_t now
= time(NULL
);
2019 const char *status_id
= purple_status_get_id(status
);
2020 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
2021 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
2022 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
2025 purple_debug_info("sipe", "sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d\n",
2026 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
2028 sip
->do_not_publish
[activity
] = 0;
2029 purple_debug_info("sipe", "sipe_set_status: set: sip->do_not_publish[%s]=%d [0]\n",
2030 status_id
, (int)sip
->do_not_publish
[activity
]);
2034 purple_debug_info("sipe", "sipe_set_status: publication was switched off, exiting.\n");
2038 g_free(sip
->status
);
2039 sip
->status
= g_strdup(status_id
);
2041 /* this will preserve OOF flag as well */
2042 if (!(note
&& sip
->note
&& !strcmp(note
, sip
->note
))) {
2043 sip
->is_oof_note
= FALSE
;
2045 sip
->note
= g_strdup(note
);
2046 sip
->note_since
= time(NULL
);
2049 /* schedule 2 sec to capture idle flag */
2050 action_name
= g_strdup_printf("<%s>", "+set-status");
2051 sipe_schedule_action(action_name
, 2, (Action
)send_presence_status
, NULL
, sip
, NULL
);
2052 g_free(action_name
);
2057 sipe_set_idle(PurpleConnection
* gc
,
2060 purple_debug_info("sipe", "sipe_set_idle: time=%d\n", time
);
2063 struct sipe_account_data
*sip
= gc
->proto_data
;
2066 sip
->was_idle
= sip
->is_idle
;
2067 sip
->is_idle
= (time
> 0);
2073 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
,
2074 SIPE_UNUSED_PARAMETER
const char *alias
)
2076 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2077 sipe_group_set_user(sip
, name
);
2081 sipe_group_buddy(PurpleConnection
*gc
,
2083 const char *old_group_name
,
2084 const char *new_group_name
)
2086 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2087 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
2088 struct sipe_group
* old_group
= NULL
;
2089 struct sipe_group
* new_group
;
2091 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
2092 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
2094 if(!buddy
) { // buddy not in roaming list
2098 if (old_group_name
) {
2099 old_group
= sipe_group_find_by_name(sip
, old_group_name
);
2101 new_group
= sipe_group_find_by_name(sip
, new_group_name
);
2104 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
2105 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
2109 sipe_group_create(sip
, new_group_name
, who
);
2111 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
2112 sipe_group_set_user(sip
, who
);
2116 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2118 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2120 /* libpurple can call us with undefined buddy or group */
2121 if (buddy
&& group
) {
2122 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2124 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2125 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
2126 purple_blist_rename_buddy(buddy
, buddy_name
);
2129 /* Prepend sip: if needed */
2130 if (strncmp("sip:", buddy
->name
, 4)) {
2131 gchar
*buf
= sip_uri_from_name(buddy
->name
);
2132 purple_blist_rename_buddy(buddy
, buf
);
2136 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
2137 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
2138 purple_debug_info("sipe", "sipe_add_buddy: adding %s\n", buddy
->name
);
2139 b
->name
= g_strdup(buddy
->name
);
2140 b
->just_added
= TRUE
;
2141 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
2142 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
2143 /* @TODO should go to callback */
2144 sipe_subscribe_presence_single(sip
, b
->name
);
2146 purple_debug_info("sipe", "sipe_add_buddy: buddy %s already in internal list\n", buddy
->name
);
2151 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
2155 * We are calling g_hash_table_foreach_steal(). That means that no
2156 * key/value deallocation functions are called. Therefore the glib
2157 * hash code does not touch the key (buddy->name) or value (buddy)
2158 * of the to-be-deleted hash node at all. It follows that we
2160 * - MUST free the memory for the key ourselves and
2161 * - ARE allowed to do it in this function
2163 * Conclusion: glib must be broken on the Windows platform if sipe
2164 * crashes with SIGTRAP when closing. You'll have to live
2165 * with the memory leak until this is fixed.
2167 g_free(buddy
->name
);
2169 g_free(buddy
->activity
);
2170 g_free(buddy
->meeting_subject
);
2171 g_free(buddy
->meeting_location
);
2172 g_free(buddy
->annotation
);
2174 g_free(buddy
->cal_start_time
);
2175 g_free(buddy
->cal_free_busy_base64
);
2176 g_free(buddy
->cal_free_busy
);
2177 g_free(buddy
->last_non_cal_activity
);
2179 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
2181 g_free(buddy
->device_name
);
2182 g_slist_free(buddy
->groups
);
2187 * Unassociates buddy from group first.
2188 * Then see if no groups left, removes buddy completely.
2189 * Otherwise updates buddy groups on server.
2191 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2193 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2194 struct sipe_buddy
*b
;
2195 struct sipe_group
*g
= NULL
;
2197 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2200 b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
2204 g
= sipe_group_find_by_name(sip
, group
->name
);
2208 b
->groups
= g_slist_remove(b
->groups
, g
);
2209 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
2212 if (g_slist_length(b
->groups
) < 1) {
2213 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
2214 sipe_cancel_scheduled_action(sip
, action_name
);
2215 g_free(action_name
);
2217 g_hash_table_remove(sip
->buddies
, buddy
->name
);
2220 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
2221 send_soap_request(sip
, body
);
2227 //updates groups on server
2228 sipe_group_set_user(sip
, b
->name
);
2234 sipe_rename_group(PurpleConnection
*gc
,
2235 const char *old_name
,
2237 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
2239 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2240 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, old_name
);
2242 sipe_group_rename(sip
, s_group
, group
->name
);
2244 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
2249 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
2251 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2252 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
2255 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
2256 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
2257 send_soap_request(sip
, body
);
2260 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
2261 g_free(s_group
->name
);
2264 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
2268 /** All statuses need message attribute to pass Note */
2269 static GList
*sipe_status_types(SIPE_UNUSED_PARAMETER PurpleAccount
*acc
)
2271 PurpleStatusType
*type
;
2272 GList
*types
= NULL
;
2274 /* Macros to reduce code repetition.
2275 Translators: noun */
2276 #define SIPE_ADD_STATUS(prim,id,name,user) type = purple_status_type_new_with_attrs( \
2278 TRUE, user, FALSE, \
2279 SIPE_STATUS_ATTR_ID_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), \
2281 types = g_list_append(types, type);
2284 SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE
,
2290 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2291 sipe_activity_map
[SIPE_ACTIVITY_BUSY
].status_id
,
2292 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSY
),
2295 /* BusyIdle (not user settable) */
2296 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2297 sipe_activity_map
[SIPE_ACTIVITY_BUSYIDLE
].status_id
,
2298 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
),
2301 /* Do Not Disturb */
2302 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2303 sipe_activity_map
[SIPE_ACTIVITY_DND
].status_id
,
2307 /* In a meeting (not user settable) */
2308 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2309 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].status_id
,
2310 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
),
2313 /* In a conference (not user settable) */
2314 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2315 sipe_activity_map
[SIPE_ACTIVITY_IN_CONF
].status_id
,
2316 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
),
2320 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2321 sipe_activity_map
[SIPE_ACTIVITY_BRB
].status_id
,
2322 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BRB
),
2326 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2331 /* On The Phone (not user settable) */
2332 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2333 sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].status_id
,
2334 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
),
2337 /* Out To Lunch (not user settable) */
2338 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2339 sipe_activity_map
[SIPE_ACTIVITY_LUNCH
].status_id
,
2340 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
),
2343 /* Idle/Inactive (not user settable) */
2344 SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE
,
2345 sipe_activity_map
[SIPE_ACTIVITY_INACTIVE
].status_id
,
2346 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
),
2349 /* Appear Offline */
2350 SIPE_ADD_STATUS(PURPLE_STATUS_INVISIBLE
,
2355 /* Offline (not user settable) */
2356 SIPE_ADD_STATUS(PURPLE_STATUS_OFFLINE
,
2365 * A callback for g_hash_table_foreach
2368 sipe_buddy_subscribe_cb(char *buddy_name
,
2369 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
2370 struct sipe_account_data
*sip
)
2372 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy_name
);
2373 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
2374 guint time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
2375 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
2377 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy_name
));
2378 g_free(action_name
);
2382 * Removes entries from purple buddy list
2383 * that does not correspond ones in the roaming contact list.
2385 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
2386 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
2387 GSList
*entry
= buddies
;
2388 struct sipe_buddy
*buddy
;
2392 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
2393 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
2396 g
= purple_buddy_get_group(b
);
2397 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
2399 gboolean in_sipe_groups
= FALSE
;
2400 GSList
*entry2
= buddy
->groups
;
2402 struct sipe_group
*group
= entry2
->data
;
2403 if (!strcmp(group
->name
, g
->name
)) {
2404 in_sipe_groups
= TRUE
;
2407 entry2
= entry2
->next
;
2409 if(!in_sipe_groups
) {
2410 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
2411 purple_blist_remove_buddy(b
);
2414 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
2415 purple_blist_remove_buddy(b
);
2417 entry
= entry
->next
;
2419 g_slist_free(buddies
);
2422 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2424 int len
= msg
->bodylen
;
2426 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
2429 const gchar
*contacts_delta
;
2430 xmlnode
*group_node
;
2431 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
2435 /* Convert the contact from XML to Purple Buddies */
2436 isc
= xmlnode_from_str(msg
->body
, len
);
2441 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
2442 if (contacts_delta
) {
2443 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2446 if (!strcmp(isc
->name
, "contactList")) {
2449 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
2450 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2451 const char *name
= xmlnode_get_attrib(group_node
, "name");
2453 if (!strncmp(name
, "~", 1)) {
2454 name
= _("Other Contacts");
2456 group
->name
= g_strdup(name
);
2457 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
2459 sipe_group_add(sip
, group
);
2462 // Make sure we have at least one group
2463 if (g_slist_length(sip
->groups
) == 0) {
2464 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2465 PurpleGroup
*purple_group
;
2466 group
->name
= g_strdup(_("Other Contacts"));
2468 purple_group
= purple_group_new(group
->name
);
2469 purple_blist_add_group(purple_group
, NULL
);
2470 sip
->groups
= g_slist_append(sip
->groups
, group
);
2473 /* Parse contacts */
2474 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
2475 const gchar
*uri
= xmlnode_get_attrib(item
, "uri");
2476 const gchar
*name
= xmlnode_get_attrib(item
, "name");
2478 struct sipe_buddy
*buddy
= NULL
;
2480 gchar
**item_groups
;
2483 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2484 tmp
= sip_uri_from_name(uri
);
2485 buddy_name
= g_ascii_strdown(tmp
, -1);
2488 /* assign to group Other Contacts if nothing else received */
2489 tmp
= g_strdup(xmlnode_get_attrib(item
, "groups"));
2490 if(!tmp
|| !strcmp("", tmp
) ) {
2491 struct sipe_group
*group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
2493 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
2495 item_groups
= g_strsplit(tmp
, " ", 0);
2498 while (item_groups
[i
]) {
2499 struct sipe_group
*group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2501 // If couldn't find the right group for this contact, just put them in the first group we have
2502 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2503 group
= sip
->groups
->data
;
2506 if (group
!= NULL
) {
2507 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2509 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2510 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2512 purple_debug_info("sipe", "Created new buddy %s with alias %s\n", buddy_name
, uri
);
2515 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
2516 if (name
!= NULL
&& strlen(name
) != 0) {
2517 purple_blist_alias_buddy(b
, name
);
2519 purple_debug_info("sipe", "Replaced buddy %s alias with %s\n", buddy_name
, name
);
2524 buddy
= g_new0(struct sipe_buddy
, 1);
2525 buddy
->name
= g_strdup(b
->name
);
2526 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2529 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2531 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2533 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2538 } // while, contact groups
2539 g_strfreev(item_groups
);
2544 sipe_cleanup_local_blist(sip
);
2546 /* Add self-contact if not there yet. 2005 systems. */
2547 /* This will resemble subscription to roaming_self in 2007 systems */
2548 if (!sip
->ocs2007
) {
2549 gchar
*self_uri
= sip_uri_self(sip
);
2550 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, self_uri
);
2553 buddy
= g_new0(struct sipe_buddy
, 1);
2554 buddy
->name
= g_strdup(self_uri
);
2555 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2562 /* subscribe to buddies */
2563 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2564 if (sip
->batched_support
) {
2565 sipe_subscribe_presence_batched(sip
, NULL
);
2567 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2569 sip
->subscribed_buddies
= TRUE
;
2571 /* for 2005 systems schedule contacts' status update
2572 * based on their calendar information
2574 if (!sip
->ocs2007
) {
2575 sipe_sched_calendar_status_update(sip
, time(NULL
));
2582 * Subscribe roaming contacts
2584 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
)
2586 gchar
*to
= sip_uri_self(sip
);
2587 gchar
*tmp
= get_contact(sip
);
2588 gchar
*hdr
= g_strdup_printf(
2589 "Event: vnd-microsoft-roaming-contacts\r\n"
2590 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2591 "Supported: com.microsoft.autoextend\r\n"
2592 "Supported: ms-benotify\r\n"
2593 "Proxy-Require: ms-benotify\r\n"
2594 "Supported: ms-piggyback-first-notify\r\n"
2595 "Contact: %s\r\n", tmp
);
2598 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2603 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
,
2604 SIPE_UNUSED_PARAMETER
void *unused
)
2607 struct sip_dialog
*dialog
;
2608 gchar
*to
= sip_uri_self(sip
);
2609 gchar
*tmp
= get_contact(sip
);
2610 gchar
*hdr
= g_strdup_printf(
2611 "Event: presence.wpending\r\n"
2612 "Accept: text/xml+msrtc.wpending\r\n"
2613 "Supported: com.microsoft.autoextend\r\n"
2614 "Supported: ms-benotify\r\n"
2615 "Proxy-Require: ms-benotify\r\n"
2616 "Supported: ms-piggyback-first-notify\r\n"
2617 "Contact: %s\r\n", tmp
);
2620 /* Subscription is identified by <event> key */
2621 key
= g_strdup_printf("<%s>", "presence.wpending");
2622 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2623 purple_debug_info("sipe", "sipe_subscribe_presence_wpending: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2625 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", dialog
, process_subscribe_response
);
2633 * Fires on deregistration event initiated by server.
2634 * [MS-SIPREGE] SIP extension.
2639 // Content-Type: text/registration-event
2640 // subscription-state: terminated;expires=0
2641 // ms-diagnostics-public: 4141;reason="User disabled"
2643 // deregistered;event=rejected
2645 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2647 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2648 gchar
*event
= NULL
;
2649 gchar
*reason
= NULL
;
2650 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2652 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2653 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2655 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2656 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2657 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2658 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2660 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2664 if (warning
!= NULL
) {
2665 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2666 } else { // for LCS2005
2668 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2669 error_id
= 4140; // [MS-SIPREGE]
2670 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2671 reason
= g_strdup(_("you are already signed in at another location"));
2672 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2674 reason
= g_strdup(_("user disabled")); // [MS-OCER]
2675 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2677 reason
= g_strdup(_("user moved")); // [MS-OCER]
2681 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
2684 sip
->gc
->wants_to_die
= TRUE
;
2685 purple_connection_error(sip
->gc
, warning
);
2690 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2692 xmlnode
*xn_provision_group_list
;
2695 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2697 /* provisionGroup */
2698 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2699 if (!strcmp("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2700 g_free(sip
->focus_factory_uri
);
2701 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2702 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2703 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2707 xmlnode_free(xn_provision_group_list
);
2710 /** for 2005 system */
2712 sipe_process_provisioning(struct sipe_account_data
*sip
,
2715 xmlnode
*xn_provision
;
2718 xn_provision
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2719 if ((node
= xmlnode_get_child(xn_provision
, "user"))) {
2720 purple_debug_info("sipe", "sipe_process_provisioning: uri=%s\n", xmlnode_get_attrib(node
, "uri"));
2721 if ((node
= xmlnode_get_child(node
, "line"))) {
2722 const gchar
*line_uri
= xmlnode_get_attrib(node
, "uri");
2723 const gchar
*server
= xmlnode_get_attrib(node
, "server");
2724 purple_debug_info("sipe", "sipe_process_provisioning: line_uri=%s server=%s\n", line_uri
, server
);
2725 sip_csta_open(sip
, line_uri
, server
);
2728 xmlnode_free(xn_provision
);
2731 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2733 const gchar
*contacts_delta
;
2736 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2742 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2745 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2752 free_container(struct sipe_container
*container
)
2756 if (!container
) return;
2758 entry
= container
->members
;
2760 g_free(entry
->data
);
2761 entry
= g_slist_remove(entry
, entry
->data
);
2767 * Finds locally stored MS-PRES container member
2769 static struct sipe_container_member
*
2770 sipe_find_container_member(struct sipe_container
*container
,
2774 struct sipe_container_member
*member
;
2777 if (container
== NULL
|| type
== NULL
) {
2781 entry
= container
->members
;
2783 member
= entry
->data
;
2784 if (!g_strcasecmp(member
->type
, type
)
2785 && ((!member
->value
&& !value
)
2786 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2790 entry
= entry
->next
;
2796 * Finds locally stored MS-PRES container by id
2798 static struct sipe_container
*
2799 sipe_find_container(struct sipe_account_data
*sip
,
2802 struct sipe_container
*container
;
2809 entry
= sip
->containers
;
2811 container
= entry
->data
;
2812 if (id
== container
->id
) {
2815 entry
= entry
->next
;
2829 sipe_find_access_level(struct sipe_account_data
*sip
,
2833 guint containers
[] = {32000, 400, 300, 200, 100};
2836 for (i
= 0; i
< 5; i
++) {
2837 struct sipe_container_member
*member
;
2838 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2839 if (!container
) continue;
2841 member
= sipe_find_container_member(container
, type
, value
);
2843 return containers
[i
];
2851 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2853 guint container_version
,
2854 const gchar
* action
,
2858 gchar
*self
= sip_uri_self(sip
);
2859 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2862 gchar
*body
= g_strdup_printf(
2863 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2864 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2865 "</setContainerMembers>",
2873 contact
= get_contact(sip
);
2874 hdr
= g_strdup_printf("Contact: %s\r\n"
2875 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2878 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2886 free_publication(struct sipe_publication
*publication
)
2888 g_free(publication
->category
);
2889 g_free(publication
->cal_event_hash
);
2890 g_free(publication
->note
);
2892 g_free(publication
->working_hours_xml_str
);
2893 g_free(publication
->fb_start_str
);
2894 g_free(publication
->free_busy_base64
);
2896 g_free(publication
);
2899 /* key is <category><instance><container> */
2901 sipe_is_our_publication(struct sipe_account_data
*sip
,
2906 /* filling keys for our publications if not yet cached */
2907 if (!sip
->our_publication_keys
) {
2908 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
2909 guint machine_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
2910 guint user_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
);
2911 guint calendar_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
2912 guint cal_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
);
2913 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
2914 guint note_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
);
2917 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2918 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
2920 /* state:machineState */
2921 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2922 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
2923 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2924 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
2926 /* state:userState */
2927 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2928 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
2929 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2930 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
2932 /* state:calendarState */
2933 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2934 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
2935 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2936 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
2938 /* state:calendarState OOF */
2939 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2940 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
2941 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2942 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
2945 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2946 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
2947 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2948 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
2949 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2950 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
2953 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2954 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
2955 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2956 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
2957 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2958 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
2960 /* calendarData:WorkingHours */
2961 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2962 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
2963 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2964 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
2965 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2966 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
2967 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2968 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
2969 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2970 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
2971 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2972 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
2974 /* calendarData:FreeBusy */
2975 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2976 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
2977 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2978 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
2979 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2980 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
2981 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2982 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
2983 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2984 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
2985 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2986 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
2988 //purple_debug_info("sipe", "sipe_is_our_publication: sip->our_publication_keys length=%d\n",
2989 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
2992 //purple_debug_info("sipe", "sipe_is_our_publication: key=%s\n", key);
2994 entry
= sip
->our_publication_keys
;
2996 //purple_debug_info("sipe", " sipe_is_our_publication: entry->data=%s\n", entry->data);
2997 if (!strcmp(entry
->data
, key
)) {
3000 entry
= entry
->next
;
3005 /** Property names to store in blist.xml */
3006 #define ALIAS_PROP "alias"
3007 #define EMAIL_PROP "email"
3008 #define PHONE_PROP "phone"
3009 #define PHONE_DISPLAY_PROP "phone-display"
3010 #define PHONE_MOBILE_PROP "phone-mobile"
3011 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
3012 #define PHONE_HOME_PROP "phone-home"
3013 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
3014 #define PHONE_OTHER_PROP "phone-other"
3015 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
3016 #define PHONE_CUSTOM1_PROP "phone-custom1"
3017 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
3018 #define SITE_PROP "site"
3019 #define COMPANY_PROP "company"
3020 #define DEPARTMENT_PROP "department"
3021 #define TITLE_PROP "title"
3022 #define OFFICE_PROP "office"
3023 /** implies work address */
3024 #define ADDRESS_STREET_PROP "address-street"
3025 #define ADDRESS_CITY_PROP "address-city"
3026 #define ADDRESS_STATE_PROP "address-state"
3027 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
3028 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
3030 * Update user information
3032 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3033 * @param property_name
3034 * @param property_value may be modified to strip white space
3037 sipe_update_user_info(struct sipe_account_data
*sip
,
3039 const char *property_name
,
3040 char *property_value
)
3042 GSList
*buddies
, *entry
;
3044 if (!property_name
|| strlen(property_name
) == 0) return;
3047 property_value
= g_strstrip(property_value
);
3049 entry
= buddies
= purple_find_buddies(sip
->account
, uri
); /* all buddies in different groups */
3051 const char *prop_str
;
3052 const char *server_alias
;
3053 PurpleBuddy
*p_buddy
= entry
->data
;
3055 /* for Display Name */
3056 if (!strcmp(property_name
, ALIAS_PROP
)) {
3057 if (property_value
&& sipe_is_bad_alias(uri
, purple_buddy_get_alias(p_buddy
))) {
3058 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, property_value
);
3059 purple_blist_alias_buddy(p_buddy
, property_value
);
3062 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3063 if (property_value
&& strlen(property_value
) > 0 &&
3064 ( (server_alias
&& strcmp(property_value
, server_alias
))
3065 || !server_alias
|| strlen(server_alias
) == 0 )
3067 purple_blist_server_alias_buddy(p_buddy
, property_value
);
3070 /* for other properties */
3072 if (property_value
&& strlen(property_value
) > 0) {
3073 prop_str
= purple_blist_node_get_string(&p_buddy
->node
, property_name
);
3074 if (!prop_str
|| g_ascii_strcasecmp(prop_str
, property_value
)) {
3075 purple_blist_node_set_string(&p_buddy
->node
, property_name
, property_value
);
3080 entry
= entry
->next
;
3082 g_slist_free(buddies
);
3087 * Suitable for both 2005 and 2007 systems.
3089 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3091 * @param phone may be modified to strip white space
3092 * @param phone_display_string may be modified to strip white space
3095 sipe_update_user_phone(struct sipe_account_data
*sip
,
3097 const gchar
*phone_type
,
3099 gchar
*phone_display_string
)
3101 const char *phone_node
= PHONE_PROP
; /* work phone by default */
3102 const char *phone_display_node
= PHONE_DISPLAY_PROP
; /* work phone by default */
3104 if(!phone
|| strlen(phone
) == 0) return;
3106 if (phone_type
&& (!strcmp(phone_type
, "mobile") || !strcmp(phone_type
, "cell"))) {
3107 phone_node
= PHONE_MOBILE_PROP
;
3108 phone_display_node
= PHONE_MOBILE_DISPLAY_PROP
;
3109 } else if (phone_type
&& !strcmp(phone_type
, "home")) {
3110 phone_node
= PHONE_HOME_PROP
;
3111 phone_display_node
= PHONE_HOME_DISPLAY_PROP
;
3112 } else if (phone_type
&& !strcmp(phone_type
, "other")) {
3113 phone_node
= PHONE_OTHER_PROP
;
3114 phone_display_node
= PHONE_OTHER_DISPLAY_PROP
;
3115 } else if (phone_type
&& !strcmp(phone_type
, "custom1")) {
3116 phone_node
= PHONE_CUSTOM1_PROP
;
3117 phone_display_node
= PHONE_CUSTOM1_DISPLAY_PROP
;
3120 sipe_update_user_info(sip
, uri
, phone_node
, phone
);
3121 if (phone_display_string
) {
3122 sipe_update_user_info(sip
, uri
, phone_display_node
, phone_display_string
);
3127 sipe_update_calendar(struct sipe_account_data
*sip
)
3129 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
3131 purple_debug_info("sipe", "sipe_update_calendar: started.\n");
3133 if (!strcmp(calendar
, "EXCH")) {
3134 sipe_ews_update_calendar(sip
);
3137 /* schedule repeat */
3138 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_INTERVAL
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3140 purple_debug_info("sipe", "sipe_update_calendar: finished.\n");
3144 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
3145 * by using standard Purple's means of signals and saved statuses.
3147 * Thus all UI elements get updated: Status Button with Note, docklet.
3148 * This is ablolutely important as both our status and note can come
3149 * inbound (roaming) or be updated programmatically (e.g. based on our
3153 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
3154 const char *status_id
,
3155 const char *message
,
3156 time_t do_not_publish
[])
3158 PurpleStatus
*status
= purple_account_get_active_status(account
);
3159 gboolean changed
= TRUE
;
3161 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
3162 purple_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
3168 PurpleSavedStatus
*saved_status
;
3169 const PurpleStatusType
*acct_status_type
=
3170 purple_status_type_find_with_id(account
->status_types
, status_id
);
3171 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
3172 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
3174 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
3176 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
3179 /* If this type+message is unique then create a new transient saved status
3180 * Ref: gtkstatusbox.c
3182 if (!saved_status
) {
3184 GList
*active_accts
= purple_accounts_get_all_active();
3186 saved_status
= purple_savedstatus_new(NULL
, primitive
);
3187 purple_savedstatus_set_message(saved_status
, message
);
3189 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
3190 purple_savedstatus_set_substatus(saved_status
,
3191 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
3193 g_list_free(active_accts
);
3196 do_not_publish
[activity
] = time(NULL
);
3197 purple_debug_info("sipe", "sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]\n",
3198 status_id
, (int)do_not_publish
[activity
]);
3200 /* Set the status for each account */
3201 purple_savedstatus_activate(saved_status
);
3206 send_publish_category_initial(struct sipe_account_data
*sip
);
3209 * When we receive some self (BE) NOTIFY with a new subscriber
3210 * we sends a setSubscribers request to him [SIP-PRES] 4.8
3213 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3220 char *display_name
= NULL
;
3222 GSList
*category_names
= NULL
;
3223 int aggreg_avail
= 0;
3224 static sipe_activity aggreg_activity
= SIPE_ACTIVITY_UNSET
;
3225 gboolean do_update_status
= FALSE
;
3227 purple_debug_info("sipe", "sipe_process_roaming_self\n");
3229 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3232 contact
= get_contact(sip
);
3233 to
= sip_uri_self(sip
);
3237 /* set list of categories participating in this XML */
3238 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3239 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3240 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
3242 purple_debug_info("sipe", "sipe_process_roaming_self: category_names length=%d\n",
3243 category_names
? (int) g_slist_length(category_names
) : -1);
3244 /* drop category information */
3245 if (category_names
) {
3246 GSList
*entry
= category_names
;
3248 GHashTable
*cat_publications
;
3249 const gchar
*category
= entry
->data
;
3250 entry
= entry
->next
;
3251 purple_debug_info("sipe", "sipe_process_roaming_self: dropping category: %s\n", category
);
3252 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
3253 if (cat_publications
) {
3254 g_hash_table_remove(sip
->our_publications
, category
);
3255 purple_debug_info("sipe", " sipe_process_roaming_self: dropped category: %s\n", category
);
3259 g_slist_free(category_names
);
3260 /* filling our categories reflected in roaming data */
3261 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3262 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3263 const gchar
*container
= xmlnode_get_attrib(node
, "container");
3264 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
3265 const gchar
*version
= xmlnode_get_attrib(node
, "version");
3266 guint version_int
= version
? atoi(version
) : 0;
3269 if (!container
|| !instance
) continue;
3271 /* key is <category><instance><container> */
3272 key
= g_strdup_printf("<%s><%s><%s>", name
, instance
, container
);
3273 purple_debug_info("sipe", "sipe_process_roaming_self: key=%s version=%d\n", key
, version_int
);
3275 /* capture all userState publication for later clean up if required */
3276 if (!strcmp(name
, "state") && (atoi(container
) == 2 || atoi(container
) == 3)) {
3277 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3279 if (xn_state
&& !strcmp(xmlnode_get_attrib(xn_state
, "type"), "userState")) {
3280 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3281 publication
->category
= g_strdup(name
);
3282 publication
->instance
= atoi(instance
);
3283 publication
->container
= atoi(container
);
3284 publication
->version
= version_int
;
3286 if (!sip
->user_state_publications
) {
3287 sip
->user_state_publications
= g_hash_table_new_full(
3288 g_str_hash
, g_str_equal
,
3289 g_free
, (GDestroyNotify
)free_publication
);
3291 g_hash_table_insert(sip
->user_state_publications
, g_strdup(key
), publication
);
3292 purple_debug_info("sipe", "sipe_process_roaming_self: added to user_state_publications key=%s version=%d\n",
3297 if (sipe_is_our_publication(sip
, key
)) {
3298 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
3300 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3301 publication
->category
= g_strdup(name
);
3302 publication
->instance
= atoi(instance
);
3303 publication
->container
= atoi(container
);
3304 publication
->version
= version_int
;
3305 /* filling publication->availability */
3306 if (!strcmp(name
, "state")) {
3307 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3308 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3311 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3313 publication
->availability
= atoi(avail_str
);
3317 /* for calendarState */
3318 if (xn_state
&& !strcmp(xmlnode_get_attrib(xn_state
, "type"), "calendarState")) {
3319 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3320 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
3322 event
->start_time
= purple_str_to_time(xmlnode_get_attrib(xn_state
, "startTime"),
3323 FALSE
, NULL
, NULL
, NULL
);
3325 if (!strcmp(xmlnode_get_attrib(xn_activity
, "token"),
3326 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
))
3328 event
->is_meeting
= TRUE
;
3331 event
->subject
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingSubject"));
3332 event
->location
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingLocation"));
3334 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
3335 purple_debug_info("sipe", "sipe_process_roaming_self: hash=%s\n",
3336 publication
->cal_event_hash
);
3337 sipe_cal_event_free(event
);
3340 /* filling publication->note */
3341 if (!strcmp(name
, "note")) {
3342 xmlnode
*xn_body
= xmlnode_get_descendant(node
, "note", "body", NULL
);
3346 publication
->note
= xmlnode_get_data(xn_body
);
3347 sip
->note
= g_strdup(publication
->note
);
3349 do_update_status
= TRUE
;
3352 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
3353 if (!strcmp(name
, "calendarData") && (publication
->container
== 300)) {
3354 xmlnode
*xn_free_busy
= xmlnode_get_descendant(node
, "calendarData", "freeBusy", NULL
);
3355 xmlnode
*xn_working_hours
= xmlnode_get_descendant(node
, "calendarData", "WorkingHours", NULL
);
3357 publication
->fb_start_str
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
3358 publication
->free_busy_base64
= xmlnode_get_data(xn_free_busy
);
3360 if (xn_working_hours
) {
3361 publication
->working_hours_xml_str
= xmlnode_to_str(xn_working_hours
, NULL
);
3365 if (!cat_publications
) {
3366 cat_publications
= g_hash_table_new_full(
3367 g_str_hash
, g_str_equal
,
3368 g_free
, (GDestroyNotify
)free_publication
);
3369 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
3370 purple_debug_info("sipe", "sipe_process_roaming_self: added GHashTable cat=%s\n", name
);
3372 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
3373 purple_debug_info("sipe", "sipe_process_roaming_self: added key=%s version=%d\n", key
, version_int
);
3377 /* aggregateState (not an our publication) from 2-nd container */
3378 if (!strcmp(name
, "state") && atoi(container
) == 2) {
3379 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3381 if (xn_state
&& !strcmp(xmlnode_get_attrib(xn_state
, "type"), "aggregateState")) {
3382 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3383 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3386 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3388 aggreg_avail
= atoi(avail_str
);
3394 const char *activity_token
= xmlnode_get_attrib(xn_activity
, "token");
3396 aggreg_activity
= sipe_get_activity_by_token(activity_token
);
3399 do_update_status
= TRUE
;
3403 /* userProperties published by server from AD */
3404 if (!sip
->csta
&& !strcmp(name
, "userProperties")) {
3406 /* line, for Remote Call Control (RCC) */
3407 for (line
= xmlnode_get_descendant(node
, "userProperties", "lines", "line", NULL
); line
; line
= xmlnode_get_next_twin(line
)) {
3408 const gchar
*line_server
= xmlnode_get_attrib(line
, "lineServer");
3409 const gchar
*line_type
= xmlnode_get_attrib(line
, "lineType");
3412 if (!line_server
|| (strcmp(line_type
, "Rcc") && strcmp(line_type
, "Dual"))) continue;
3414 line_uri
= xmlnode_get_data(line
);
3416 purple_debug_info("sipe", "sipe_process_roaming_self: line_uri=%s server=%s\n", line_uri
, line_server
);
3417 sip_csta_open(sip
, line_uri
, line_server
);
3425 purple_debug_info("sipe", "sipe_process_roaming_self: sip->our_publications size=%d\n",
3426 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
3429 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3430 guint id
= atoi(xmlnode_get_attrib(node
, "id"));
3431 struct sipe_container
*container
= sipe_find_container(sip
, id
);
3434 sip
->containers
= g_slist_remove(sip
->containers
, container
);
3435 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
3436 free_container(container
);
3438 container
= g_new0(struct sipe_container
, 1);
3440 container
->version
= atoi(xmlnode_get_attrib(node
, "version"));
3441 sip
->containers
= g_slist_append(sip
->containers
, container
);
3442 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
3444 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
3445 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
3446 member
->type
= xmlnode_get_attrib(node2
, "type");
3447 member
->value
= xmlnode_get_attrib(node2
, "value");
3448 container
->members
= g_slist_append(container
->members
, member
);
3449 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
3450 member
->type
, member
->value
? member
->value
: "");
3454 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
3455 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
3456 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
3457 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
3458 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
3459 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
3460 /* initial set-up to let counterparties see your status */
3461 if (sameEnterpriseAL
< 0) {
3462 struct sipe_container
*container
= sipe_find_container(sip
, 200);
3463 guint version
= container
? container
->version
: 0;
3464 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
3466 if (federatedAL
< 0) {
3467 struct sipe_container
*container
= sipe_find_container(sip
, 100);
3468 guint version
= container
? container
->version
: 0;
3469 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
3471 sip
->access_level_set
= TRUE
;
3475 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3477 const char *acknowledged
;
3481 user
= xmlnode_get_attrib(node
, "user"); /* without 'sip:' prefix */
3482 if (!user
) continue;
3483 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
3484 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
3485 uri
= sip_uri_from_name(user
);
3487 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
3489 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
3490 if(!g_ascii_strcasecmp(acknowledged
,"false")){
3491 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
3492 if (!purple_find_buddy(sip
->account
, uri
)) {
3493 purple_account_request_add(sip
->account
, uri
, _("you"), display_name
, NULL
);
3496 hdr
= g_strdup_printf(
3498 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
3500 body
= g_strdup_printf(
3501 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
3502 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
3503 "</setSubscribers>", user
);
3505 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
3509 g_free(display_name
);
3516 /* Publish initial state if not yet.
3517 * Assuming this happens on initial responce to subscription to roaming-self
3518 * so we've already updated our roaming data in full.
3521 if (!sip
->initial_state_published
) {
3522 send_publish_category_initial(sip
);
3523 sip
->initial_state_published
= TRUE
;
3525 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3526 do_update_status
= FALSE
;
3527 } else if (aggreg_avail
) {
3529 g_free(sip
->status
);
3530 if (aggreg_avail
&& aggreg_avail
< 18000) { /* not offline */
3531 if (aggreg_activity
== SIPE_ACTIVITY_IN_MEETING
||
3532 aggreg_activity
== SIPE_ACTIVITY_IN_CONF
||
3533 aggreg_activity
== SIPE_ACTIVITY_ON_PHONE
)
3535 sip
->status
= g_strdup(sipe_activity_map
[aggreg_activity
].status_id
);
3539 sip
->status
= g_strdup(sipe_get_status_by_availability(aggreg_avail
, NULL
));
3542 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
3546 if (do_update_status
) {
3547 purple_debug_info("sipe", "sipe_process_roaming_self: to %s for the account\n", sip
->status
);
3548 sipe_set_purple_account_status_and_note(sip
->account
, sip
->status
, sip
->note
, sip
->do_not_publish
);
3554 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
)
3556 gchar
*to
= sip_uri_self(sip
);
3557 gchar
*tmp
= get_contact(sip
);
3558 gchar
*hdr
= g_strdup_printf(
3559 "Event: vnd-microsoft-roaming-ACL\r\n"
3560 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
3561 "Supported: com.microsoft.autoextend\r\n"
3562 "Supported: ms-benotify\r\n"
3563 "Proxy-Require: ms-benotify\r\n"
3564 "Supported: ms-piggyback-first-notify\r\n"
3565 "Contact: %s\r\n", tmp
);
3568 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
3574 * To request for presence information about the user, access level settings that have already been configured by the user
3575 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
3576 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
3579 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
)
3581 gchar
*to
= sip_uri_self(sip
);
3582 gchar
*tmp
= get_contact(sip
);
3583 gchar
*hdr
= g_strdup_printf(
3584 "Event: vnd-microsoft-roaming-self\r\n"
3585 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
3586 "Supported: ms-benotify\r\n"
3587 "Proxy-Require: ms-benotify\r\n"
3588 "Supported: ms-piggyback-first-notify\r\n"
3590 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
3592 gchar
*body
=g_strdup(
3593 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
3594 "<roaming type=\"categories\"/>"
3595 "<roaming type=\"containers\"/>"
3596 "<roaming type=\"subscribers\"/></roamingList>");
3599 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3608 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
)
3610 gchar
*to
= sip_uri_self(sip
);
3611 gchar
*tmp
= get_contact(sip
);
3612 gchar
*hdr
= g_strdup_printf(
3613 "Event: vnd-microsoft-provisioning\r\n"
3614 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
3615 "Supported: com.microsoft.autoextend\r\n"
3616 "Supported: ms-benotify\r\n"
3617 "Proxy-Require: ms-benotify\r\n"
3618 "Supported: ms-piggyback-first-notify\r\n"
3620 "Contact: %s\r\n", tmp
);
3623 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
3628 /** Subscription for provisioning information to help with initial
3629 * configuration. This subscription is a one-time query (denoted by the Expires header,
3630 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
3631 * configuration, meeting policies, and policy settings that Communicator must enforce.
3632 * TODO: for what we need this information.
3635 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
)
3637 gchar
*to
= sip_uri_self(sip
);
3638 gchar
*tmp
= get_contact(sip
);
3639 gchar
*hdr
= g_strdup_printf(
3640 "Event: vnd-microsoft-provisioning-v2\r\n"
3641 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
3642 "Supported: com.microsoft.autoextend\r\n"
3643 "Supported: ms-benotify\r\n"
3644 "Proxy-Require: ms-benotify\r\n"
3645 "Supported: ms-piggyback-first-notify\r\n"
3648 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
3649 gchar
*body
= g_strdup(
3650 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
3651 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
3652 "<provisioningGroup name=\"ucPolicy\"/>"
3653 "</provisioningGroupList>");
3656 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3663 sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key
,
3664 gpointer value
, gpointer user_data
)
3666 struct sip_subscription
*subscription
= value
;
3667 struct sip_dialog
*dialog
= &subscription
->dialog
;
3668 struct sipe_account_data
*sip
= user_data
;
3669 gchar
*tmp
= get_contact(sip
);
3670 gchar
*hdr
= g_strdup_printf(
3673 "Contact: %s\r\n", subscription
->event
, tmp
);
3676 /* Rate limit to max. 25 requests per seconds */
3677 g_usleep(1000000 / 25);
3679 send_sip_request(sip
->gc
, "SUBSCRIBE", dialog
->with
, dialog
->with
, hdr
, NULL
, dialog
, NULL
);
3683 /* IM Session (INVITE and MESSAGE methods) */
3685 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
3687 get_end_points (struct sipe_account_data
*sip
,
3688 struct sip_session
*session
)
3692 if (session
== NULL
) {
3696 res
= g_strdup_printf("<sip:%s>", sip
->username
);
3698 SIPE_DIALOG_FOREACH
{
3700 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
3703 if (dialog
->theirepid
) {
3705 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
3708 } SIPE_DIALOG_FOREACH_END
;
3714 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_account_data
*sip
,
3716 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3718 gboolean ret
= TRUE
;
3720 if (msg
->response
!= 200) {
3721 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
3725 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
3731 * Asks UA/proxy about its capabilities.
3733 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
3735 gchar
*to
= sip_uri(who
);
3736 gchar
*contact
= get_contact(sip
);
3737 gchar
*request
= g_strdup_printf(
3738 "Accept: application/sdp\r\n"
3739 "Contact: %s\r\n", contact
);
3742 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
3749 sipe_notify_user(struct sipe_account_data
*sip
,
3750 struct sip_session
*session
,
3751 PurpleMessageFlags flags
,
3752 const gchar
*message
)
3754 PurpleConversation
*conv
;
3756 if (!session
->conv
) {
3757 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
3759 conv
= session
->conv
;
3761 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
3765 sipe_present_info(struct sipe_account_data
*sip
,
3766 struct sip_session
*session
,
3767 const gchar
*message
)
3769 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
3773 sipe_present_err(struct sipe_account_data
*sip
,
3774 struct sip_session
*session
,
3775 const gchar
*message
)
3777 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_ERROR
, message
);
3781 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
3782 struct sip_session
*session
,
3785 const gchar
*message
)
3787 char *msg
, *msg_tmp
, *msg_tmp2
;
3790 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
3791 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
3793 /* Service unavailable; Server Internal Error; Server Time-out */
3794 if (sip_error
== 503 || sip_error
== 500 || sip_error
== 504) {
3795 label
= _("This message was not delivered to %s because the service is not available");
3796 } else if (sip_error
== 486) { /* Busy Here */
3797 label
= _("This message was not delivered to %s because one or more recipients do not want to be disturbed");
3799 label
= _("This message was not delivered to %s because one or more recipients are offline");
3802 msg_tmp
= g_strdup_printf( "%s:\n%s" ,
3803 msg_tmp2
= g_strdup_printf(label
, who
? who
: ""), msg
? msg
: "");
3804 sipe_present_err(sip
, session
, msg_tmp
);
3811 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
);
3814 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
3815 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3817 gboolean ret
= TRUE
;
3818 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3819 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
3820 struct sip_dialog
*dialog
;
3826 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
3831 dialog
= sipe_dialog_find(session
, with
);
3833 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
3838 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3839 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
3841 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
3843 if (msg
->response
>= 400) {
3844 PurpleBuddy
*pbuddy
;
3845 gchar
*alias
= with
;
3847 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
3849 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
3850 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
3853 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, alias
, message
);
3856 gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
3858 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
));
3859 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
3860 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
3863 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3864 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
3865 key
, g_hash_table_size(session
->unconfirmed_messages
));
3871 if (ret
) sipe_im_process_queue(sip
, session
);
3876 sipe_is_election_finished(struct sip_session
*session
);
3879 sipe_election_result(struct sipe_account_data
*sip
,
3883 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
3884 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3886 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
3887 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3888 struct sip_dialog
*dialog
;
3889 struct sip_session
*session
;
3891 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3893 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
3897 if (msg
->response
== 200 && !strncmp(contenttype
, "application/x-ms-mim", 20)) {
3898 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3899 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
3900 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
3902 if (xn_request_rm_response
) {
3903 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
3904 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
3906 dialog
= sipe_dialog_find(session
, with
);
3908 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
3912 if (allow
&& !g_strcasecmp(allow
, "true")) {
3913 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
3914 dialog
->election_vote
= 1;
3915 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
3916 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
3917 dialog
->election_vote
= -1;
3920 if (sipe_is_election_finished(session
)) {
3921 sipe_election_result(sip
, session
);
3924 } else if (xn_set_rm_response
) {
3927 xmlnode_free(xn_action
);
3934 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
)
3943 sipe_parse_html(msg
, &msgformat
, &msgtext
);
3944 purple_debug_info("sipe", "sipe_send_message: msgformat=%s\n", msgformat
);
3946 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3949 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
3952 msgr
= g_strdup("");
3955 tmp
= get_contact(sip
);
3956 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
3957 //hdr = g_strdup("Content-Type: text/rtf\r\n");
3958 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
3959 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n", tmp
, msgr
);
3963 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
3970 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
3972 GSList
*entry2
= session
->outgoing_message_queue
;
3974 char *queued_msg
= entry2
->data
;
3976 /* for multiparty chat or conference */
3977 if (session
->is_multiparty
|| session
->focus_uri
) {
3978 gchar
*who
= sip_uri_self(sip
);
3979 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
3980 PURPLE_MESSAGE_SEND
, queued_msg
, time(NULL
));
3984 SIPE_DIALOG_FOREACH
{
3987 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
3989 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
3990 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
3991 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
3992 key
, g_hash_table_size(session
->unconfirmed_messages
));
3995 sipe_send_message(sip
, dialog
, queued_msg
);
3996 } SIPE_DIALOG_FOREACH_END
;
3998 entry2
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
4004 sipe_refer_notify(struct sipe_account_data
*sip
,
4005 struct sip_session
*session
,
4012 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4014 hdr
= g_strdup_printf(
4016 "Subscription-State: %s\r\n"
4017 "Content-Type: message/sipfrag\r\n",
4018 status
>= 200 ? "terminated" : "active");
4020 body
= g_strdup_printf(
4021 "SIP/2.0 %d %s\r\n",
4024 send_sip_request(sip
->gc
, "NOTIFY", who
, who
, hdr
, body
, dialog
, NULL
);
4031 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
4033 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
4034 struct sip_session
*session
;
4035 struct sip_dialog
*dialog
;
4039 struct sipmsg
*request_msg
= trans
->msg
;
4041 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4044 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4046 session
= sipe_session_find_im(sip
, with
);
4049 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
4054 dialog
= sipe_dialog_find(session
, with
);
4056 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
4061 sipe_dialog_parse(dialog
, msg
, TRUE
);
4063 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4064 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
4066 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4068 if (msg
->response
!= 200) {
4069 PurpleBuddy
*pbuddy
;
4070 gchar
*alias
= with
;
4072 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
4074 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4075 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
4079 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, alias
, message
);
4081 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
4082 sipe_present_err(sip
, session
, tmp_msg
);
4086 sipe_dialog_remove(session
, with
);
4094 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4095 dialog
->outgoing_invite
= NULL
;
4096 dialog
->is_established
= TRUE
;
4098 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
4100 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
4101 g_free(referred_by
);
4104 /* add user to chat if it is a multiparty session */
4105 if (session
->is_multiparty
) {
4106 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4108 PURPLE_CBFLAGS_NONE
, TRUE
);
4111 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
4112 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
4113 if (session
->outgoing_message_queue
) {
4114 char *queued_msg
= session
->outgoing_message_queue
->data
;
4115 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
4120 sipe_im_process_queue(sip
, session
);
4122 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4123 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
4124 key
, g_hash_table_size(session
->unconfirmed_messages
));
4133 sipe_invite(struct sipe_account_data
*sip
,
4134 struct sip_session
*session
,
4136 const gchar
*msg_body
,
4137 const gchar
*referred_by
,
4138 const gboolean is_triggered
)
4145 char *ms_text_format
= NULL
;
4146 gchar
*roster_manager
;
4148 gchar
*referred_by_str
;
4149 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4151 if (dialog
&& dialog
->is_established
) {
4152 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
4157 dialog
= sipe_dialog_add(session
);
4158 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
4159 dialog
->with
= g_strdup(who
);
4162 if (!(dialog
->ourtag
)) {
4163 dialog
->ourtag
= gentag();
4176 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
4177 purple_debug_info("sipe", "sipe_invite: msgformat=%s\n", msgformat
);
4179 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4183 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
4187 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
4188 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
4193 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
4194 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
4195 purple_debug_info("sipe", "sipe_invite: added message %s to unconfirmed_messages(count=%d)\n",
4196 key
, g_hash_table_size(session
->unconfirmed_messages
));
4200 contact
= get_contact(sip
);
4201 end_points
= get_end_points(sip
, session
);
4202 self
= sip_uri_self(sip
);
4203 roster_manager
= g_strdup_printf(
4204 "Roster-Manager: %s\r\n"
4205 "EndPoints: %s\r\n",
4208 referred_by_str
= referred_by
?
4210 "Referred-By: %s\r\n",
4213 hdr
= g_strdup_printf(
4214 "Supported: ms-sender\r\n"
4220 "Content-Type: application/sdp\r\n",
4221 (session
->roster_manager
&& !strcmp(session
->roster_manager
, self
)) ? roster_manager
: "",
4223 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
4224 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
4226 ms_text_format
? ms_text_format
: "");
4227 g_free(ms_text_format
);
4230 body
= g_strdup_printf(
4232 "o=- 0 0 IN IP4 %s\r\n"
4236 "m=%s %d sip null\r\n"
4237 "a=accept-types:text/plain text/html image/gif "
4238 "multipart/related multipart/alternative application/im-iscomposing+xml application/ms-imdn+xml\r\n",
4239 purple_network_get_my_ip(-1),
4240 purple_network_get_my_ip(-1),
4241 sip
->ocs2007
? "message" : "x-ms-message",
4244 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
4245 to
, to
, hdr
, body
, dialog
, process_invite_response
);
4248 g_free(roster_manager
);
4250 g_free(referred_by_str
);
4257 sipe_refer(struct sipe_account_data
*sip
,
4258 struct sip_session
*session
,
4263 gchar
*epid
= get_epid(sip
);
4264 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
4265 session
->roster_manager
);
4266 const char *ourtag
= dialog
&& dialog
->ourtag
? dialog
->ourtag
: NULL
;
4268 contact
= get_contact(sip
);
4269 hdr
= g_strdup_printf(
4271 "Refer-to: <%s>\r\n"
4272 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
4273 "Require: com.microsoft.rtc-multiparty\r\n",
4277 ourtag
? ";tag=" : "",
4278 ourtag
? ourtag
: "",
4282 send_sip_request(sip
->gc
, "REFER",
4283 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
4290 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
4291 struct sip_dialog
*dialog
,
4294 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4296 gchar
*body
= g_strdup_printf(
4297 "<?xml version=\"1.0\"?>\r\n"
4298 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4299 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
4300 sip
->username
, bid
);
4302 send_sip_request(sip
->gc
, "INFO",
4303 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4309 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
4310 struct sip_dialog
*dialog
)
4312 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4314 gchar
*body
= g_strdup_printf(
4315 "<?xml version=\"1.0\"?>\r\n"
4316 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4317 "<SetRM uri=\"sip:%s\"/></action>\r\n",
4320 send_sip_request(sip
->gc
, "INFO",
4321 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4327 sipe_session_close(struct sipe_account_data
*sip
,
4328 struct sip_session
* session
)
4330 if (session
&& session
->focus_uri
) {
4331 sipe_conf_immcu_closed(sip
, session
);
4332 conf_session_close(sip
, session
);
4336 SIPE_DIALOG_FOREACH
{
4337 /* @TODO slow down BYE message sending rate */
4338 /* @see single subscription code */
4339 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4340 } SIPE_DIALOG_FOREACH_END
;
4342 sipe_session_remove(sip
, session
);
4347 sipe_session_close_all(struct sipe_account_data
*sip
)
4350 while ((entry
= sip
->sessions
) != NULL
) {
4351 sipe_session_close(sip
, entry
->data
);
4356 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
4358 struct sipe_account_data
*sip
= gc
->proto_data
;
4360 purple_debug_info("sipe", "conversation with %s closed\n", who
);
4361 sipe_session_close(sip
, sipe_session_find_im(sip
, who
));
4365 sipe_chat_leave (PurpleConnection
*gc
, int id
)
4367 struct sipe_account_data
*sip
= gc
->proto_data
;
4368 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
4370 sipe_session_close(sip
, session
);
4373 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
4374 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4376 struct sipe_account_data
*sip
= gc
->proto_data
;
4377 struct sip_session
*session
;
4378 struct sip_dialog
*dialog
;
4379 gchar
*uri
= sip_uri(who
);
4381 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
4383 session
= sipe_session_find_or_add_im(sip
, uri
);
4384 dialog
= sipe_dialog_find(session
, uri
);
4386 // Queue the message
4387 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
4389 if (dialog
&& !dialog
->outgoing_invite
) {
4390 sipe_im_process_queue(sip
, session
);
4391 } else if (!dialog
|| !dialog
->outgoing_invite
) {
4392 // Need to send the INVITE to get the outgoing dialog setup
4393 sipe_invite(sip
, session
, uri
, what
, NULL
, FALSE
);
4400 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
4401 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4403 struct sipe_account_data
*sip
= gc
->proto_data
;
4404 struct sip_session
*session
;
4406 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
4408 session
= sipe_session_find_chat_by_id(sip
, id
);
4410 // Queue the message
4411 if (session
&& session
->dialogs
) {
4412 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
4414 sipe_im_process_queue(sip
, session
);
4416 gchar
*chat_name
= purple_find_chat(sip
->gc
, id
)->name
;
4417 const gchar
*proto_chat_id
= sipe_chat_find_name(chat_name
);
4419 purple_debug_info("sipe", "sipe_chat_send: chat_name='%s'\n", chat_name
? chat_name
: "NULL");
4420 purple_debug_info("sipe", "sipe_chat_send: proto_chat_id='%s'\n", proto_chat_id
? proto_chat_id
: "NULL");
4423 struct sip_session
*session
= sipe_session_add_chat(sip
);
4425 session
->is_multiparty
= FALSE
;
4426 session
->focus_uri
= g_strdup(proto_chat_id
);
4427 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
4429 sipe_invite_conf_focus(sip
, session
);
4436 /* End IM Session (INVITE and MESSAGE methods) */
4438 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4440 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4441 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4443 struct sip_session
*session
;
4445 purple_debug_info("sipe", "process_incoming_info: \n%s\n", msg
->body
? msg
->body
: "");
4447 /* Call Control protocol */
4448 if (g_str_has_prefix(contenttype
, "application/csta+xml"))
4450 process_incoming_info_csta(sip
, msg
);
4454 from
= parse_from(sipmsg_find_header(msg
, "From"));
4455 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4457 session
= sipe_session_find_im(sip
, from
);
4464 if (g_str_has_prefix(contenttype
, "application/x-ms-mim"))
4466 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4467 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
4468 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
4470 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
4472 if (xn_request_rm
) {
4473 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
4474 int bid
= atoi(xmlnode_get_attrib(xn_request_rm
, "bid"));
4475 gchar
*body
= g_strdup_printf(
4476 "<?xml version=\"1.0\"?>\r\n"
4477 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4478 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
4480 session
->bid
< bid
? "true" : "false");
4481 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4483 } else if (xn_set_rm
) {
4485 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
4486 g_free(session
->roster_manager
);
4487 session
->roster_manager
= g_strdup(rm
);
4489 body
= g_strdup_printf(
4490 "<?xml version=\"1.0\"?>\r\n"
4491 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4492 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
4494 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4497 xmlnode_free(xn_action
);
4502 /* looks like purple lacks typing notification for chat */
4503 if (!session
->is_multiparty
&& !session
->focus_uri
) {
4504 xmlnode
*xn_keyboard_activity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4505 const char *status
= xmlnode_get_attrib(xmlnode_get_child(xn_keyboard_activity
, "status"),
4507 if (status
&& !strcmp(status
, "type")) {
4508 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
4509 } else if (status
&& !strcmp(status
, "idle")) {
4510 serv_got_typing_stopped(sip
->gc
, from
);
4512 xmlnode_free(xn_keyboard_activity
);
4515 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4520 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4522 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4523 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4524 struct sip_session
*session
;
4525 struct sip_dialog
*dialog
;
4527 /* collect dialog identification
4528 * we need callid, ourtag and theirtag to unambiguously identify dialog
4530 /* take data before 'msg' will be modified by send_sip_response */
4531 dialog
= g_new0(struct sip_dialog
, 1);
4532 dialog
->callid
= g_strdup(callid
);
4533 dialog
->cseq
= parse_cseq(sipmsg_find_header(msg
, "CSeq"));
4534 dialog
->with
= g_strdup(from
);
4535 sipe_dialog_parse(dialog
, msg
, FALSE
);
4537 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4539 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4541 session
= sipe_session_find_im(sip
, from
);
4548 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
4549 g_free(session
->roster_manager
);
4550 session
->roster_manager
= NULL
;
4553 /* This what BYE is essentially for - terminating dialog */
4554 sipe_dialog_remove_3(session
, dialog
);
4555 sipe_dialog_free(dialog
);
4556 if (session
->focus_uri
&& !g_strcasecmp(from
, session
->im_mcu_uri
)) {
4557 sipe_conf_immcu_closed(sip
, session
);
4558 } else if (session
->is_multiparty
) {
4559 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
4565 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4567 gchar
*self
= sip_uri_self(sip
);
4568 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4569 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4570 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
4571 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
4572 struct sip_session
*session
;
4573 struct sip_dialog
*dialog
;
4575 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4576 dialog
= sipe_dialog_find(session
, from
);
4578 if (!session
|| !dialog
|| !session
->roster_manager
|| strcmp(session
->roster_manager
, self
)) {
4579 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
4581 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
4583 sipe_invite(sip
, session
, refer_to
, NULL
, referred_by
, FALSE
);
4589 g_free(referred_by
);
4593 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
4595 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
4596 struct sip_session
*session
;
4597 struct sip_dialog
*dialog
;
4599 if (state
== PURPLE_NOT_TYPING
)
4602 session
= sipe_session_find_im(sip
, who
);
4603 dialog
= sipe_dialog_find(session
, who
);
4605 if (session
&& dialog
&& dialog
->is_established
) {
4606 send_sip_request(gc
, "INFO", who
, who
,
4607 "Content-Type: application/xml\r\n",
4608 SIPE_SEND_TYPING
, dialog
, NULL
);
4610 return SIPE_TYPING_SEND_TIMEOUT
;
4613 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
4615 GSList
*tmp
= sip
->transactions
;
4616 time_t currtime
= time(NULL
);
4618 struct transaction
*trans
= tmp
->data
;
4620 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
4621 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
4624 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
4626 sendout_sipmsg(sip
, trans
->msg
);
4633 static void do_reauthenticate_cb(struct sipe_account_data
*sip
,
4634 SIPE_UNUSED_PARAMETER
void *unused
)
4636 /* register again when security token expires */
4637 /* we have to start a new authentication as the security token
4638 * is almost expired by sending a not signed REGISTER message */
4639 purple_debug_info("sipe", "do a full reauthentication\n");
4640 sipe_auth_free(&sip
->registrar
);
4641 sipe_auth_free(&sip
->proxy
);
4642 sip
->registerstatus
= 0;
4644 sip
->reauthenticate_set
= FALSE
;
4647 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4651 gboolean found
= FALSE
;
4653 from
= parse_from(sipmsg_find_header(msg
, "From"));
4657 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
4659 contenttype
= sipmsg_find_header(msg
, "Content-Type");
4660 if (!strncmp(contenttype
, "text/plain", 10)
4661 || !strncmp(contenttype
, "text/html", 9)
4662 || !strncmp(contenttype
, "multipart/related", 17)
4663 || !strncmp(contenttype
, "multipart/alternative", 21))
4665 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4666 gchar
*html
= get_html_message(contenttype
, msg
->body
);
4668 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
4670 session
= sipe_session_find_im(sip
, from
);
4673 if (session
&& session
->focus_uri
) { /* a conference */
4674 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
4675 gchar
*sender
= parse_from(tmp
);
4677 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
4678 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4680 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
4681 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
4682 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4684 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
4687 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4690 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
4691 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4696 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
4700 state
= xmlnode_get_child(isc
, "state");
4703 purple_debug_info("sipe", "process_incoming_message: no state found\n");
4708 statedata
= xmlnode_get_data(state
);
4710 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
4711 else serv_got_typing_stopped(sip
->gc
, from
);
4716 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4720 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4721 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
4723 session
= sipe_session_find_im(sip
, from
);
4726 gchar
*msg
= g_strdup_printf(_("Received a message with unrecognized contents from %s"),
4728 sipe_present_err(sip
, session
, msg
);
4732 purple_debug_info("sipe", "got unknown mime-type '%s'\n", contenttype
);
4733 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
4738 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4744 gboolean is_multiparty
= FALSE
;
4745 gboolean is_triggered
= FALSE
;
4746 gboolean was_multiparty
= TRUE
;
4747 gboolean just_joined
= FALSE
;
4749 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4750 gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
4751 gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
4752 gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
4753 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4754 GSList
*end_points
= NULL
;
4756 struct sip_session
*session
;
4758 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? tmp
= fix_newlines(msg
->body
) : "");
4761 /* Invitation to join conference */
4762 if (!strncmp(content_type
, "application/ms-conf-invite+xml", 30)) {
4763 process_incoming_invite_conf(sip
, msg
);
4767 /* Only accept text invitations */
4768 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
4769 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
4773 // TODO There *must* be a better way to clean up the To header to add a tag...
4774 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
4775 oldHeader
= sipmsg_find_header(msg
, "To");
4777 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
4778 sipmsg_remove_header_now(msg
, "To");
4779 sipmsg_add_header_now(msg
, "To", newHeader
);
4782 if (end_points_hdr
) {
4783 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
4785 if (g_slist_length(end_points
) > 2) {
4786 is_multiparty
= TRUE
;
4789 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
4790 is_triggered
= TRUE
;
4791 is_multiparty
= TRUE
;
4794 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4795 /* Convert to multiparty */
4796 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
4797 g_free(session
->with
);
4798 session
->with
= NULL
;
4799 was_multiparty
= FALSE
;
4800 session
->is_multiparty
= TRUE
;
4801 session
->chat_id
= rand();
4804 if (!session
&& is_multiparty
) {
4805 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
4808 from
= parse_from(sipmsg_find_header(msg
, "From"));
4810 session
= sipe_session_find_or_add_im(sip
, from
);
4814 g_free(session
->callid
);
4815 session
->callid
= g_strdup(callid
);
4817 session
->is_multiparty
= is_multiparty
;
4818 if (roster_manager
) {
4819 session
->roster_manager
= g_strdup(roster_manager
);
4823 if (is_multiparty
&& end_points
) {
4824 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
4825 GSList
*entry
= end_points
;
4827 struct sip_dialog
*dialog
;
4828 struct sipendpoint
*end_point
= entry
->data
;
4829 entry
= entry
->next
;
4831 if (!g_strcasecmp(from
, end_point
->contact
) ||
4832 !g_strcasecmp(to
, end_point
->contact
))
4835 dialog
= sipe_dialog_find(session
, end_point
->contact
);
4837 g_free(dialog
->theirepid
);
4838 dialog
->theirepid
= end_point
->epid
;
4839 end_point
->epid
= NULL
;
4841 dialog
= sipe_dialog_add(session
);
4843 dialog
->callid
= g_strdup(session
->callid
);
4844 dialog
->with
= end_point
->contact
;
4845 end_point
->contact
= NULL
;
4846 dialog
->theirepid
= end_point
->epid
;
4847 end_point
->epid
= NULL
;
4851 /* send triggered INVITE */
4852 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, TRUE
);
4859 GSList
*entry
= end_points
;
4861 struct sipendpoint
*end_point
= entry
->data
;
4862 entry
= entry
->next
;
4863 g_free(end_point
->contact
);
4864 g_free(end_point
->epid
);
4867 g_slist_free(end_points
);
4871 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
4873 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
4875 dialog
= sipe_dialog_add(session
);
4877 dialog
->callid
= g_strdup(session
->callid
);
4878 dialog
->with
= g_strdup(from
);
4879 sipe_dialog_parse(dialog
, msg
, FALSE
);
4881 if (!dialog
->ourtag
) {
4882 dialog
->ourtag
= newTag
;
4889 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
4893 if (is_multiparty
&& !session
->conv
) {
4894 gchar
*chat_title
= sipe_chat_get_name(callid
);
4895 gchar
*self
= sip_uri_self(sip
);
4896 /* create prpl chat */
4897 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_title
);
4898 session
->chat_title
= g_strdup(chat_title
);
4899 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
4901 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4903 PURPLE_CBFLAGS_NONE
, FALSE
);
4908 if (is_multiparty
&& !was_multiparty
) {
4909 /* add current IM counterparty to chat */
4910 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4911 sipe_dialog_first(session
)->with
, NULL
,
4912 PURPLE_CBFLAGS_NONE
, FALSE
);
4915 /* add inviting party to chat */
4916 if (just_joined
&& session
->conv
) {
4917 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4919 PURPLE_CBFLAGS_NONE
, TRUE
);
4922 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
4924 /* This used only in 2005 official client, not 2007 or Reuters.
4925 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
4926 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
4928 if (is_multiparty
) {
4929 /* please do not optimize logic inside as this code may be re-enabled for other cases */
4930 gchar
*ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
4931 if (ms_text_format
) {
4932 if (g_str_has_prefix(ms_text_format
, "text/plain") || g_str_has_prefix(ms_text_format
, "text/html")) {
4934 gchar
*html
= get_html_message(ms_text_format
, NULL
);
4936 if (is_multiparty
) {
4937 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
4938 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4940 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
4943 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
4952 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
4953 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
4954 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
4956 body
= g_strdup_printf(
4958 "o=- 0 0 IN IP4 %s\r\n"
4962 "m=%s %d sip sip:%s\r\n"
4963 "a=accept-types:text/plain text/html image/gif multipart/related multipart/alternative application/im-iscomposing+xml application/ms-imdn+xml\r\n",
4964 purple_network_get_my_ip(-1),
4965 purple_network_get_my_ip(-1),
4966 sip
->ocs2007
? "message" : "x-ms-message",
4969 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4973 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4977 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
4978 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
4979 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
4981 body
= g_strdup_printf(
4983 "o=- 0 0 IN IP4 0.0.0.0\r\n"
4985 "c=IN IP4 0.0.0.0\r\n"
4987 "m=%s %d sip sip:%s\r\n"
4988 "a=accept-types:text/plain text/html image/gif multipart/related multipart/alternative application/im-iscomposing+xml application/ms-imdn+xml\r\n",
4989 sip
->ocs2007
? "message" : "x-ms-message",
4992 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4996 static void sipe_connection_cleanup(struct sipe_account_data
*);
4997 static void create_connection(struct sipe_account_data
*, gchar
*, int);
4999 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
5000 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5003 const gchar
*expires_header
;
5005 GSList
*hdr
= msg
->headers
;
5006 struct siphdrelement
*elem
;
5008 expires_header
= sipmsg_find_header(msg
, "Expires");
5009 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
5010 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
5012 switch (msg
->response
) {
5015 sip
->registerstatus
= 0;
5017 gchar
*contact_hdr
= NULL
;
5022 gchar
*server_hdr
= sipmsg_find_header(msg
, "Server");
5024 if (!sip
->reregister_set
) {
5025 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
5026 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
5027 g_free(action_name
);
5028 sip
->reregister_set
= TRUE
;
5031 sip
->registerstatus
= 3;
5033 if (server_hdr
&& !sip
->server_version
) {
5034 sip
->server_version
= g_strdup(server_hdr
);
5040 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5042 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
5045 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
5049 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
5050 fill_auth(tmp
, &sip
->registrar
);
5053 if (!sip
->reauthenticate_set
) {
5054 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
5055 guint reauth_timeout
;
5056 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
5057 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
5058 reauth_timeout
= sip
->registrar
.expires
- 300;
5060 /* NTLM: we have to reauthenticate as our security token expires
5061 after eight hours (be five minutes early) */
5062 reauth_timeout
= (8 * 3600) - 300;
5064 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
5065 g_free(action_name
);
5066 sip
->reauthenticate_set
= TRUE
;
5069 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
5071 epid
= get_epid(sip
);
5072 uuid
= generateUUIDfromEPID(epid
);
5075 // There can be multiple Contact headers (one per location where the user is logged in) so
5076 // make sure to only get the one for this uuid
5077 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
5078 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
5079 if (valid_contact
) {
5080 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
5081 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
5082 g_free(valid_contact
);
5085 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
5090 g_free(sip
->contact
);
5092 sip
->contact
= g_strdup_printf("<%s>", gruu
);
5095 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
5096 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
);
5098 sip
->ocs2007
= FALSE
;
5099 sip
->batched_support
= FALSE
;
5104 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
5105 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
5106 /* We interpret this as OCS2007+ indicator */
5107 sip
->ocs2007
= TRUE
;
5108 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s (indicates OCS2007+)\n", elem
->value
);
5110 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
5111 sip
->batched_support
= TRUE
;
5112 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s\n", elem
->value
);
5115 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
5116 gchar
**caps
= g_strsplit(elem
->value
,",",0);
5119 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
5120 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
5125 hdr
= g_slist_next(hdr
);
5128 /* rejoin open chats to be able to use them by continue to send messages */
5129 purple_conversation_foreach(sipe_rejoin_chat
);
5132 if (!sip
->subscribed
) { //do it just once, not every re-register
5134 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
5135 (GCompareFunc
)g_ascii_strcasecmp
)) {
5136 sipe_subscribe_roaming_contacts(sip
);
5139 /* For 2007+ it does not make sence to subscribe to:
5140 * vnd-microsoft-roaming-ACL
5141 * vnd-microsoft-provisioning (not v2)
5143 * These are for backward compatibility.
5147 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
5148 (GCompareFunc
)g_ascii_strcasecmp
)) {
5149 sipe_subscribe_roaming_self(sip
);
5151 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
5152 (GCompareFunc
)g_ascii_strcasecmp
)) {
5153 sipe_subscribe_roaming_provisioning_v2(sip
);
5156 /* For 2005- servers */
5159 //sipe_options_request(sip, sip->sipdomain);
5161 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
5162 (GCompareFunc
)g_ascii_strcasecmp
)) {
5163 sipe_subscribe_roaming_acl(sip
);
5165 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
5166 (GCompareFunc
)g_ascii_strcasecmp
)) {
5167 sipe_subscribe_roaming_provisioning(sip
);
5169 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
5170 (GCompareFunc
)g_ascii_strcasecmp
)) {
5171 sipe_subscribe_presence_wpending(sip
, msg
);
5174 /* For 2007+ we publish our initial statuses and calendar data only after
5175 * received our existing publications in sipe_process_roaming_self()
5176 * Only in this case we know versions of current publications made
5179 /* For 2005- we publish our initial statuses only after
5180 * received our existing UserInfo data in response to
5181 * self subscription.
5182 * Only in this case we won't override existing UserInfo data
5183 * set earlier or by other client on our behalf.
5187 sip
->subscribed
= TRUE
;
5190 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
5191 "timeout=", ";", NULL
);
5192 if (timeout
!= NULL
) {
5193 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
5194 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
5195 sip
->keepalive_timeout
);
5199 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\n", sip
->cseq
);
5204 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
5206 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
5207 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
5211 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
5214 tmp
= g_strsplit(parts
[0], ":", 0);
5215 hostname
= g_strdup(tmp
[0]);
5216 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
5220 tmp
= g_strsplit(parts
[i
], "=", 0);
5222 if (g_strcasecmp("transport", tmp
[0]) == 0) {
5223 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
5224 transport
= SIPE_TRANSPORT_TCP
;
5225 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
5226 transport
= SIPE_TRANSPORT_UDP
;
5235 /* Close old connection */
5236 sipe_connection_cleanup(sip
);
5238 /* Create new connection */
5239 sip
->transport
= transport
;
5240 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
5241 hostname
, port
, TRANSPORT_DESCRIPTOR
);
5242 create_connection(sip
, hostname
, port
);
5248 if (sip
->registerstatus
!= 2) {
5249 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
5250 if (sip
->registrar
.retries
> 3) {
5251 sip
->gc
->wants_to_die
= TRUE
;
5252 purple_connection_error(sip
->gc
, _("Wrong password"));
5256 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5258 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
5261 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
5264 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
5265 fill_auth(tmp
, &sip
->registrar
);
5266 sip
->registerstatus
= 2;
5267 if (sip
->account
->disconnecting
) {
5268 do_register_exp(sip
, 0);
5276 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
5277 gchar
**reason
= NULL
;
5278 if (warning
!= NULL
) {
5280 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
5282 reason
= g_strsplit(warning
, "\"", 0);
5284 warning
= g_strdup_printf(_("You have been rejected by the server: %s"),
5285 (reason
&& reason
[1]) ? reason
[1] : _("no reason given"));
5288 sip
->gc
->wants_to_die
= TRUE
;
5289 purple_connection_error(sip
->gc
, warning
);
5296 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
5297 gchar
*reason
= NULL
;
5298 if (warning
!= NULL
) {
5299 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
5301 warning
= g_strdup_printf(_("Not found: %s. Please contact your Administrator"),
5302 warning
? (reason
? reason
: _("no reason given")) :
5303 _("SIP is either not enabled for the destination URI or it does not exist"));
5306 sip
->gc
->wants_to_die
= TRUE
;
5307 purple_connection_error(sip
->gc
, warning
);
5313 case 504: /* Server time-out */
5315 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
5316 gchar
*reason
= NULL
;
5317 if (warning
!= NULL
) {
5318 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
5320 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: "<a href=\"http://www.reuters.com\">http://www.reuters.com</a>"/*_("no reason given")*/);
5323 sip
->gc
->wants_to_die
= TRUE
;
5324 purple_connection_error(sip
->gc
, warning
);
5334 * Returns 2005-style activity and Availability.
5336 * @param status Sipe statis id.
5339 sipe_get_act_avail_by_status_2005(const char *status
, int *activity
, int *availability
)
5341 int avail
= 300; /* online */
5342 int act
= 400; /* Available */
5344 if (!strcmp(status
, SIPE_STATUS_ID_AWAY
)) {
5346 } else if (!strcmp(status
, SIPE_STATUS_ID_LUNCH
)) {
5348 } else if (!strcmp(status
, SIPE_STATUS_ID_BRB
)) {
5350 } else if (!strcmp(status
, SIPE_STATUS_ID_AVAILABLE
)) {
5352 } else if (!strcmp(status
, SIPE_STATUS_ID_ON_PHONE
)) {
5354 } else if (!strcmp(status
, SIPE_STATUS_ID_BUSY
) ||
5355 !strcmp(status
, SIPE_STATUS_ID_IN_MEETING
) ||
5356 !strcmp(status
, SIPE_STATUS_ID_IN_CONF
) ||
5357 !strcmp(status
, SIPE_STATUS_ID_DND
)) {
5359 } else if (!strcmp(status
, SIPE_STATUS_ID_INVISIBLE
) ||
5360 !strcmp(status
, SIPE_STATUS_ID_OFFLINE
)) {
5361 avail
= 0; /* offline */
5364 act
= 400; /* Available */
5367 if (activity
) *activity
= act
;
5368 if (availability
) *availability
= avail
;
5374 * @param activity 2005 aggregated activity. Ex.: 600
5375 * @param availablity 2005 aggregated availablity. Ex.: 300
5378 sipe_get_status_by_act_avail_2005(const int activity
,
5379 const int availablity
)
5381 const char *status_id
= NULL
;
5384 status_id
= SIPE_STATUS_ID_AWAY
;
5385 else if (activity
< 200)
5386 status_id
= SIPE_STATUS_ID_LUNCH
;
5387 else if (activity
< 300)
5388 status_id
= SIPE_STATUS_ID_IDLE
;
5389 else if (activity
< 400)
5390 status_id
= SIPE_STATUS_ID_BRB
;
5391 else if (activity
< 500)
5392 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5393 else if (activity
< 600)
5394 status_id
= SIPE_STATUS_ID_ON_PHONE
;
5395 else if (activity
< 700)
5396 status_id
= SIPE_STATUS_ID_BUSY
;
5397 else if (activity
< 800)
5398 status_id
= SIPE_STATUS_ID_AWAY
;
5400 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5402 if (availablity
< 100)
5403 status_id
= SIPE_STATUS_ID_OFFLINE
;
5409 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
5412 sipe_get_status_by_availability(int avail
,
5413 const char* activity
)
5418 status
= SIPE_STATUS_ID_OFFLINE
;
5419 else if (avail
< 4500)
5420 status
= SIPE_STATUS_ID_AVAILABLE
;
5421 else if (avail
< 6000)
5422 status
= SIPE_STATUS_ID_IDLE
;
5423 else if (avail
< 7500)
5424 if (activity
&& !strcmp(activity
, SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
))) {
5425 status
= SIPE_STATUS_ID_IN_MEETING
;
5426 } else if (activity
&& !strcmp(activity
, SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
))) {
5427 status
= SIPE_STATUS_ID_IN_CONF
;
5428 } else if (activity
&& !strcmp(activity
, SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
))) {
5429 status
= SIPE_STATUS_ID_ON_PHONE
;
5431 status
= SIPE_STATUS_ID_BUSY
;
5433 else if (avail
< 9000)
5434 status
= SIPE_STATUS_ID_BUSYIDLE
;
5435 else if (avail
< 12000)
5436 status
= SIPE_STATUS_ID_DND
;
5437 else if (avail
< 15000)
5438 status
= SIPE_STATUS_ID_BRB
;
5439 else if (avail
< 18000)
5440 status
= SIPE_STATUS_ID_AWAY
;
5442 status
= SIPE_STATUS_ID_OFFLINE
;
5448 * Returns 2007-style availability value
5450 * @param sipe_status_id (in)
5451 * @param activity_token (out) Must be g_free()'d after use if consumed.
5454 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
5457 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
5459 if (!strcmp(sipe_status_id
, SIPE_STATUS_ID_AWAY
) ||
5460 !strcmp(sipe_status_id
, SIPE_STATUS_ID_LUNCH
)) {
5461 availability
= 15500;
5462 activity
= SIPE_ACTIVITY_AWAY
;
5463 } else if (!strcmp(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
5464 availability
= 12500;
5465 activity
= SIPE_ACTIVITY_BRB
;
5466 } else if (!strcmp(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
5467 availability
= 9500;
5468 activity
= SIPE_ACTIVITY_DND
;
5469 } else if (!strcmp(sipe_status_id
, SIPE_STATUS_ID_BUSY
) ||
5470 !strcmp(sipe_status_id
, SIPE_STATUS_ID_IN_MEETING
) ||
5471 !strcmp(sipe_status_id
, SIPE_STATUS_ID_IN_CONF
) ||
5472 !strcmp(sipe_status_id
, SIPE_STATUS_ID_ON_PHONE
)) {
5473 availability
= 6500;
5474 activity
= SIPE_ACTIVITY_BUSY
;
5475 } else if (!strcmp(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
5476 availability
= 3500;
5477 activity
= SIPE_ACTIVITY_ONLINE
;
5478 } else if (!strcmp(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
5481 // Offline or invisible
5482 availability
= 18500;
5483 activity
= SIPE_ACTIVITY_OFFLINE
;
5486 if (activity_token
) {
5487 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
5489 return availability
;
5492 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5495 xmlnode
*xn_categories
;
5496 xmlnode
*xn_category
;
5498 const char *status
= NULL
;
5499 gboolean do_update_status
= FALSE
;
5501 xn_categories
= xmlnode_from_str(data
, len
);
5502 uri
= xmlnode_get_attrib(xn_categories
, "uri"); /* with 'sip:' prefix */
5504 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
5506 xn_category
= xmlnode_get_next_twin(xn_category
) )
5508 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
5511 if (!strcmp(attrVar
, "contactCard"))
5514 /* identity - Display Name and email */
5515 node
= xmlnode_get_descendant(xn_category
, "contactCard", "identity", NULL
);
5517 char* display_name
= xmlnode_get_data(
5518 xmlnode_get_descendant(node
, "name", "displayName", NULL
));
5519 char* email
= xmlnode_get_data(
5520 xmlnode_get_child(node
, "email"));
5522 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5523 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
5525 g_free(display_name
);
5529 node
= xmlnode_get_descendant(xn_category
, "contactCard", "company", NULL
);
5531 char* company
= xmlnode_get_data(node
);
5532 sipe_update_user_info(sip
, uri
, COMPANY_PROP
, company
);
5536 node
= xmlnode_get_descendant(xn_category
, "contactCard", "department", NULL
);
5538 char* department
= xmlnode_get_data(node
);
5539 sipe_update_user_info(sip
, uri
, DEPARTMENT_PROP
, department
);
5543 node
= xmlnode_get_descendant(xn_category
, "contactCard", "title", NULL
);
5545 char* title
= xmlnode_get_data(node
);
5546 sipe_update_user_info(sip
, uri
, TITLE_PROP
, title
);
5550 node
= xmlnode_get_descendant(xn_category
, "contactCard", "office", NULL
);
5552 char* office
= xmlnode_get_data(node
);
5553 sipe_update_user_info(sip
, uri
, OFFICE_PROP
, office
);
5557 node
= xmlnode_get_descendant(xn_category
, "contactCard", "url", NULL
);
5559 char* site
= xmlnode_get_data(node
);
5560 sipe_update_user_info(sip
, uri
, SITE_PROP
, site
);
5564 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "phone", NULL
);
5566 node
= xmlnode_get_next_twin(node
))
5568 const char *phone_type
= xmlnode_get_attrib(node
, "type");
5569 char* phone
= xmlnode_get_data(xmlnode_get_child(node
, "uri"));
5570 char* phone_display_string
= xmlnode_get_data(xmlnode_get_child(node
, "displayString"));
5572 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, phone_display_string
);
5575 g_free(phone_display_string
);
5578 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "address", NULL
);
5580 node
= xmlnode_get_next_twin(node
))
5582 if (!strcmp(xmlnode_get_attrib(node
, "type"), "work")) {
5583 char* street
= xmlnode_get_data(xmlnode_get_child(node
, "street"));
5584 char* city
= xmlnode_get_data(xmlnode_get_child(node
, "city"));
5585 char* state
= xmlnode_get_data(xmlnode_get_child(node
, "state"));
5586 char* zipcode
= xmlnode_get_data(xmlnode_get_child(node
, "zipcode"));
5587 char* country_code
= xmlnode_get_data(xmlnode_get_child(node
, "countryCode"));
5589 sipe_update_user_info(sip
, uri
, ADDRESS_STREET_PROP
, street
);
5590 sipe_update_user_info(sip
, uri
, ADDRESS_CITY_PROP
, city
);
5591 sipe_update_user_info(sip
, uri
, ADDRESS_STATE_PROP
, state
);
5592 sipe_update_user_info(sip
, uri
, ADDRESS_ZIPCODE_PROP
, zipcode
);
5593 sipe_update_user_info(sip
, uri
, ADDRESS_COUNTRYCODE_PROP
, country_code
);
5599 g_free(country_code
);
5606 else if (!strcmp(attrVar
, "note"))
5609 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
5612 /* clean up in case to 'note' element is supplied
5613 * which indicate note removal in client
5615 g_free(sbuddy
->annotation
);
5616 sbuddy
->annotation
= NULL
;
5617 sbuddy
->is_oof_note
= FALSE
;
5619 xn_node
= xmlnode_get_descendant(xn_category
, "note", "body", NULL
);
5621 sbuddy
->annotation
= xmlnode_get_data(xn_node
);
5622 sbuddy
->is_oof_note
= !strcmp(xmlnode_get_attrib(xn_node
, "type"), "OOF");
5624 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s),note(%s)\n",
5625 uri
, sbuddy
->annotation
? sbuddy
->annotation
: "");
5628 /* to trigger UI refresh in case no status info is supplied in this update */
5629 do_update_status
= TRUE
;
5634 else if(!strcmp(attrVar
, "state"))
5638 xmlnode
*xn_availability
;
5639 xmlnode
*xn_activity
;
5640 xmlnode
*xn_meeting_subject
;
5641 xmlnode
*xn_meeting_location
;
5642 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
5644 xn_node
= xmlnode_get_child(xn_category
, "state");
5645 if (!xn_node
) continue;
5646 xn_availability
= xmlnode_get_child(xn_node
, "availability");
5647 if (!xn_availability
) continue;
5648 xn_activity
= xmlnode_get_child(xn_node
, "activity");
5649 xn_meeting_subject
= xmlnode_get_child(xn_node
, "meetingSubject");
5650 xn_meeting_location
= xmlnode_get_child(xn_node
, "meetingLocation");
5652 data
= xmlnode_get_data(xn_availability
);
5653 availability
= atoi(data
);
5656 /* activity, meeting_subject, meeting_location */
5659 g_free(sbuddy
->activity
);
5660 sbuddy
->activity
= NULL
;
5662 const char *token
= xmlnode_get_attrib(xn_activity
, "token");
5663 xmlnode
*xn_custom
= xmlnode_get_child(xn_activity
, "custom");
5666 if (!is_empty(token
)) {
5667 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
5669 /* from custom element */
5671 char *custom
= xmlnode_get_data(xn_custom
);
5673 if (!is_empty(custom
)) {
5674 sbuddy
->activity
= custom
;
5680 /* meeting_subject */
5681 g_free(sbuddy
->meeting_subject
);
5682 sbuddy
->meeting_subject
= NULL
;
5683 if (xn_meeting_subject
) {
5684 char *meeting_subject
= xmlnode_get_data(xn_meeting_subject
);
5686 if (!is_empty(meeting_subject
)) {
5687 sbuddy
->meeting_subject
= meeting_subject
;
5688 meeting_subject
= NULL
;
5690 g_free(meeting_subject
);
5692 /* meeting_location */
5693 g_free(sbuddy
->meeting_location
);
5694 sbuddy
->meeting_location
= NULL
;
5695 if (xn_meeting_location
) {
5696 char *meeting_location
= xmlnode_get_data(xn_meeting_location
);
5698 if (!is_empty(meeting_location
)) {
5699 sbuddy
->meeting_location
= meeting_location
;
5700 meeting_location
= NULL
;
5702 g_free(meeting_location
);
5705 status
= sipe_get_status_by_availability(availability
, sbuddy
->activity
);
5708 do_update_status
= TRUE
;
5711 else if(!strcmp(attrVar
, "calendarData"))
5713 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
5714 xmlnode
*xn_free_busy
= xmlnode_get_descendant(xn_category
, "calendarData", "freeBusy", NULL
);
5715 xmlnode
*xn_working_hours
= xmlnode_get_descendant(xn_category
, "calendarData", "WorkingHours", NULL
);
5717 if (sbuddy
&& xn_free_busy
) {
5718 g_free(sbuddy
->cal_start_time
);
5719 sbuddy
->cal_start_time
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
5721 sbuddy
->cal_granularity
= !g_ascii_strcasecmp(xmlnode_get_attrib(xn_free_busy
, "granularity"), "PT15M") ?
5724 g_free(sbuddy
->cal_free_busy_base64
);
5725 sbuddy
->cal_free_busy_base64
= xmlnode_get_data(xn_free_busy
);
5727 g_free(sbuddy
->cal_free_busy
);
5728 sbuddy
->cal_free_busy
= NULL
;
5730 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
);
5733 if (sbuddy
&& xn_working_hours
) {
5734 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
5739 if (do_update_status
) {
5740 if (!status
) { /* no status category in this update, using contact's current status */
5741 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
5742 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
5743 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
5744 status
= purple_status_get_id(pstatus
);
5747 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", status
);
5748 sipe_got_user_status(sip
, uri
, status
);
5751 xmlnode_free(xn_categories
);
5754 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
5756 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
5757 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
5758 payload
->host
= g_strdup(host
);
5759 payload
->buddies
= server
;
5760 sipe_subscribe_presence_batched_routed(sip
, payload
);
5761 sipe_subscribe_presence_batched_routed_free(payload
);
5764 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5767 xmlnode
*xn_resource
;
5768 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
5773 xn_list
= xmlnode_from_str(data
, len
);
5775 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
5777 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
5779 const char *uri
, *state
;
5780 xmlnode
*xn_instance
;
5782 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
5783 if (!xn_instance
) continue;
5785 uri
= xmlnode_get_attrib(xn_resource
, "uri");
5786 state
= xmlnode_get_attrib(xn_instance
, "state");
5787 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
5789 if (strstr(state
, "resubscribe")) {
5790 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
5792 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
5793 gchar
*user
= g_strdup(uri
);
5794 host
= g_strdup(poolFqdn
);
5795 server
= g_hash_table_lookup(servers
, host
);
5796 server
= g_slist_append(server
, user
);
5797 g_hash_table_insert(servers
, host
, server
);
5799 sipe_subscribe_presence_single(sip
, (void *) uri
);
5804 /* Send out any deferred poolFqdn subscriptions */
5805 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
5806 g_hash_table_destroy(servers
);
5808 xmlnode_free(xn_list
);
5811 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5815 gchar
*activity
= NULL
;
5817 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
5818 gboolean isonline
= FALSE
;
5819 xmlnode
*display_name_node
;
5821 pidf
= xmlnode_from_str(data
, len
);
5823 purple_debug_info("sipe", "process_incoming_notify_pidf: no parseable pidf:%s\n",data
);
5827 uri
= sip_uri(xmlnode_get_attrib(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
5829 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
5831 if ((status
= xmlnode_get_child(tuple
, "status"))) {
5832 basicstatus
= xmlnode_get_child(status
, "basic");
5837 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic found\n");
5842 getbasic
= xmlnode_get_data(basicstatus
);
5844 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic data found\n");
5849 purple_debug_info("sipe", "process_incoming_notify_pidf: basic-status(%s)\n", getbasic
);
5850 if (strstr(getbasic
, "open")) {
5855 display_name_node
= xmlnode_get_child(pidf
, "display-name");
5856 if (display_name_node
) {
5857 char * display_name
= xmlnode_get_data(display_name_node
);
5859 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5860 g_free(display_name
);
5863 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
5864 if ((status
= xmlnode_get_child(tuple
, "status"))) {
5865 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
5866 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
5867 activity
= xmlnode_get_data(basicstatus
);
5868 purple_debug_info("sipe", "process_incoming_notify_pidf: activity(%s)\n", activity
);
5875 const gchar
* status_id
= NULL
;
5877 if (!strcmp(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
5878 status_id
= SIPE_STATUS_ID_BUSY
;
5879 } else if (!strcmp(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
5880 status_id
= SIPE_STATUS_ID_AWAY
;
5885 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5888 purple_debug_info("sipe", "process_incoming_notify_pidf: status_id(%s)\n", status_id
);
5889 sipe_got_user_status(sip
, uri
, status_id
);
5891 sipe_got_user_status(sip
, uri
, SIPE_STATUS_ID_OFFLINE
);
5901 sipe_user_info_has_updated(struct sipe_account_data
*sip
,
5902 xmlnode
*xn_userinfo
)
5904 if (sip
->user_info
) {
5905 xmlnode_free(sip
->user_info
);
5907 sip
->user_info
= xmlnode_copy(xn_userinfo
);
5909 /* Publish initial state if not yet.
5910 * Assuming this happens on initial responce to self subscription
5911 * so we've already updated our UserInfo.
5913 if (!sip
->initial_state_published
) {
5914 send_presence_soap(sip
, FALSE
);
5916 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
5920 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5922 const char *activity
= NULL
;
5924 const char *status_id
= NULL
;
5927 char *self_uri
= sip_uri_self(sip
);
5930 const char *device_name
= NULL
;
5931 const char *cal_start_time
= NULL
;
5932 const char *cal_granularity
= NULL
;
5933 char *cal_free_busy_base64
= NULL
;
5934 struct sipe_buddy
*sbuddy
;
5936 xmlnode
*xn_presentity
;
5937 xmlnode
*xn_availability
;
5938 xmlnode
*xn_activity
;
5939 xmlnode
*xn_display_name
;
5941 xmlnode
*xn_phone_number
;
5942 xmlnode
*xn_userinfo
;
5946 xmlnode
*xn_contact
;
5948 char *free_activity
;
5950 const char *user_avail_nil
;
5952 time_t user_avail_since
= 0;
5953 time_t activity_since
= 0;
5955 /* fix for Reuters environment on Linux */
5956 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
5958 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
5959 xn_presentity
= xmlnode_from_str(tmp_data
, strlen(tmp_data
));
5962 xn_presentity
= xmlnode_from_str(data
, len
);
5965 xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
5966 xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
5967 xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
5968 xn_email
= xmlnode_get_child(xn_presentity
, "email");
5969 xn_phone_number
= xmlnode_get_child(xn_presentity
, "phoneNumber");
5970 xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
5971 xn_oof
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "oof") : NULL
;
5972 xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
): NULL
;
5973 user_avail
= xn_state
? atoi(xmlnode_get_attrib(xn_state
, "avail")) : 0;
5974 user_avail_since
= xn_state
? purple_str_to_time(xmlnode_get_attrib(xn_state
, "since"), FALSE
, NULL
, NULL
, NULL
) : 0;
5975 user_avail_nil
= xn_state
? xmlnode_get_attrib(xn_state
, "nil") : NULL
;
5976 xn_contact
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "contact") : NULL
;
5977 xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
5978 note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
5980 if (user_avail_nil
&& !strcmp(user_avail_nil
, "true")) { /* null-ed */
5982 user_avail_since
= 0;
5985 free_activity
= NULL
;
5987 name
= xmlnode_get_attrib(xn_presentity
, "uri"); /* without 'sip:' prefix */
5988 uri
= sip_uri_from_name(name
);
5989 avl
= atoi(xmlnode_get_attrib(xn_availability
, "aggregate"));
5990 epid
= xmlnode_get_attrib(xn_availability
, "epid");
5991 act
= atoi(xmlnode_get_attrib(xn_activity
, "aggregate"));
5993 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
);
5994 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
5995 if (user_avail
> res_avail
) {
5996 res_avail
= user_avail
;
5997 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
6000 if (xn_display_name
) {
6001 char *display_name
= g_strdup(xmlnode_get_attrib(xn_display_name
, "displayName"));
6002 char *email
= xn_email
? g_strdup(xmlnode_get_attrib(xn_email
, "email")) : NULL
;
6003 char *phone_label
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "label")) : NULL
;
6004 char *phone_number
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "number")) : NULL
;
6005 char *tel_uri
= sip_to_tel_uri(phone_number
);
6007 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6008 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
6009 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
6010 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, !is_empty(phone_label
) ? phone_label
: phone_number
);
6013 g_free(phone_label
);
6014 g_free(phone_number
);
6016 g_free(display_name
);
6021 for (node
= xmlnode_get_child(xn_contact
, "tel"); node
; node
= xmlnode_get_next_twin(node
))
6023 /* Ex.: <tel type="work">tel:+3222220000</tel> */
6024 const char *phone_type
= xmlnode_get_attrib(node
, "type");
6025 char* phone
= xmlnode_get_data(node
);
6027 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, NULL
);
6033 /* devicePresence */
6034 for (node
= xmlnode_get_descendant(xn_presentity
, "devices", "devicePresence", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
6035 xmlnode
*xn_device_name
;
6036 xmlnode
*xn_calendar_info
;
6041 if (!strcmp(xmlnode_get_attrib(node
, "epid"), epid
)) {
6042 xn_device_name
= xmlnode_get_child(node
, "deviceName");
6043 device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
6047 xn_calendar_info
= xmlnode_get_child(node
, "calendarInfo");
6048 if (xn_calendar_info
) {
6049 const char *cal_start_time_tmp
= xmlnode_get_attrib(xn_calendar_info
, "startTime");
6051 if (cal_start_time
) {
6052 time_t cal_start_time_t
= purple_str_to_time(cal_start_time
, FALSE
, NULL
, NULL
, NULL
);
6053 time_t cal_start_time_t_tmp
= purple_str_to_time(cal_start_time_tmp
, FALSE
, NULL
, NULL
, NULL
);
6055 if (cal_start_time_t_tmp
> cal_start_time_t
) {
6056 cal_start_time
= cal_start_time_tmp
;
6057 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6058 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6060 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
);
6063 cal_start_time
= cal_start_time_tmp
;
6064 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6065 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6067 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
);
6072 xn_state
= xmlnode_get_descendant(node
, "states", "state", NULL
);
6074 int dev_avail
= atoi(xmlnode_get_attrib(xn_state
, "avail"));
6075 time_t dev_avail_since
= purple_str_to_time(xmlnode_get_attrib(xn_state
, "since"), FALSE
, NULL
, NULL
, NULL
);
6077 state
= xmlnode_get_data(xn_state
);
6078 if (dev_avail_since
> user_avail_since
&&
6079 dev_avail
>= res_avail
)
6081 res_avail
= dev_avail
;
6082 if (!is_empty(state
))
6084 if (!strcmp(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
6085 activity
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
6086 } else if (!strcmp(state
, "presenting")) {
6087 activity
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
);
6089 activity
= free_activity
= state
;
6092 activity_since
= dev_avail_since
;
6094 status_id
= sipe_get_status_by_availability(res_avail
, activity
);
6101 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
6102 activity
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
);
6106 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
6109 g_free(sbuddy
->activity
);
6110 sbuddy
->activity
= NULL
;
6111 if (!is_empty(activity
)) { sbuddy
->activity
= g_strdup(activity
); }
6113 sbuddy
->activity_since
= activity_since
;
6115 sbuddy
->user_avail
= user_avail
;
6116 sbuddy
->user_avail_since
= user_avail_since
;
6118 g_free(sbuddy
->annotation
);
6119 sbuddy
->annotation
= NULL
;
6120 if (!is_empty(note
)) { sbuddy
->annotation
= g_strdup(note
); }
6122 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
6124 g_free(sbuddy
->device_name
);
6125 sbuddy
->device_name
= NULL
;
6126 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
6128 if (!is_empty(cal_free_busy_base64
)) {
6129 g_free(sbuddy
->cal_start_time
);
6130 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
6132 sbuddy
->cal_granularity
= !g_ascii_strcasecmp(cal_granularity
, "PT15M") ? 15 : 0;
6134 g_free(sbuddy
->cal_free_busy_base64
);
6135 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
6137 g_free(sbuddy
->cal_free_busy
);
6138 sbuddy
->cal_free_busy
= NULL
;
6141 sbuddy
->last_non_cal_status_id
= status_id
;
6142 g_free(sbuddy
->last_non_cal_activity
);
6143 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
6145 if (!strcmp(sbuddy
->name
, self_uri
)) {
6146 if (!(sbuddy
->annotation
&& sip
->note
&& !strcmp(sbuddy
->annotation
, sip
->note
))) /* not same */
6148 sip
->is_oof_note
= sbuddy
->is_oof_note
;
6151 sip
->note
= g_strdup(sbuddy
->annotation
);
6152 sip
->note_since
= time(NULL
);
6155 g_free(sip
->status
);
6156 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
6160 if (free_activity
) g_free(free_activity
);
6162 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", status_id
);
6163 sipe_got_user_status(sip
, uri
, status_id
);
6165 if (!sip
->ocs2007
&& !strcmp(self_uri
, uri
)) {
6166 sipe_user_info_has_updated(sip
, xn_userinfo
);
6170 xmlnode_free(xn_presentity
);
6175 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
6177 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6179 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
6181 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
6182 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
6184 const char *content
= msg
->body
;
6185 unsigned length
= msg
->bodylen
;
6186 PurpleMimeDocument
*mime
= NULL
;
6188 if (strstr(ctype
, "multipart"))
6190 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6191 const char *content_type
;
6193 mime
= purple_mime_document_parse(doc
);
6194 parts
= purple_mime_document_get_parts(mime
);
6196 content
= purple_mime_part_get_data(parts
->data
);
6197 length
= purple_mime_part_get_length(parts
->data
);
6198 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
6199 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
6201 process_incoming_notify_rlmi_resub(sip
, content
, length
);
6203 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
6205 process_incoming_notify_msrtc(sip
, content
, length
);
6209 process_incoming_notify_rlmi(sip
, content
, length
);
6211 parts
= parts
->next
;
6217 purple_mime_document_free(mime
);
6220 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
6222 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
6224 else if(strstr(ctype
, "application/rlmi+xml"))
6226 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
6229 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
6231 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
6235 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
6239 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
6241 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6242 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6244 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
6247 strstr(ctype
, "multipart") &&
6248 (strstr(ctype
, "application/rlmi+xml") ||
6249 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
6250 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6251 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
6252 GList
*parts
= purple_mime_document_get_parts(mime
);
6253 GSList
*buddies
= NULL
;
6254 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6257 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
6258 purple_mime_part_get_length(parts
->data
));
6260 if (strcmp(xml
->name
, "list")) {
6261 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
6263 buddies
= g_slist_append(buddies
, uri
);
6267 parts
= parts
->next
;
6270 if (mime
) purple_mime_document_free(mime
);
6272 payload
->host
= g_strdup(who
);
6273 payload
->buddies
= buddies
;
6274 sipe_schedule_action(action_name
, timeout
,
6275 sipe_subscribe_presence_batched_routed
,
6276 sipe_subscribe_presence_batched_routed_free
,
6278 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
6281 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6282 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
6284 g_free(action_name
);
6288 * Dispatcher for all incoming subscription information
6289 * whether it comes from NOTIFY, BENOTIFY requests or
6290 * piggy-backed to subscription's OK responce.
6292 * @param request whether initiated from BE/NOTIFY request or OK-response message.
6293 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
6295 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
6297 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
6298 gchar
*event
= sipmsg_find_header(msg
, "Event");
6299 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
6303 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n",
6305 tmp
= fix_newlines(msg
->body
));
6307 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n", subscription_state
? subscription_state
: "");
6309 /* implicit subscriptions */
6310 if (content_type
&& purple_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
6311 sipe_process_imdn(sip
, msg
);
6316 const gchar
*expires_header
;
6317 expires_header
= sipmsg_find_header(msg
, "Expires");
6318 timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
6319 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n", timeout
);
6320 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
; // 2 min ahead of expiration
6323 /* for one off subscriptions (send with Expire: 0) */
6324 if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning-v2"))
6326 sipe_process_provisioning_v2(sip
, msg
);
6328 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning"))
6330 sipe_process_provisioning(sip
, msg
);
6333 if (!subscription_state
|| strstr(subscription_state
, "active"))
6335 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
6337 sipe_process_presence(sip
, msg
);
6339 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
6341 sipe_process_roaming_contacts(sip
, msg
);
6343 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self"))
6345 sipe_process_roaming_self(sip
, msg
);
6347 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
6349 sipe_process_roaming_acl(sip
, msg
);
6351 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
6353 sipe_process_presence_wpending(sip
, msg
);
6355 else if (event
&& !g_ascii_strcasecmp(event
, "conference"))
6357 sipe_process_conference(sip
, msg
);
6361 /* The server sends status 'terminated' */
6362 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
6363 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
6364 gchar
*key
= sipe_get_subscription_key(event
, who
);
6366 purple_debug_info("sipe", "process_incoming_notify: server says that subscription to %s was terminated.\n", who
);
6369 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
6370 g_hash_table_remove(sip
->subscriptions
, key
);
6371 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
6377 if (timeout
&& event
) {// For LSC 2005 and OCS 2007
6378 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
6379 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
6381 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
6382 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_contacts, NULL, sip, msg);
6383 g_free(action_name);
6385 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
6386 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
6388 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
6389 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_acl, NULL, sip, msg);
6390 g_free(action_name);
6393 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
6394 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
6396 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
6397 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
6398 g_free(action_name
);
6400 else if (!g_ascii_strcasecmp(event
, "presence") &&
6401 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
6403 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
6404 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6405 if (sip
->batched_support
) {
6406 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
6409 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6410 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
, timeout
);
6412 g_free(action_name
);
6417 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
6419 sipe_process_registration_notify(sip
, msg
);
6422 /* The client responses on received a NOTIFY message */
6423 if (request
&& !benotify
)
6425 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
6430 * Whether user manually changed status or
6431 * it was changed automatically due to user
6432 * became inactive/active again
6435 sipe_is_user_state(struct sipe_account_data
*sip
)
6437 gboolean res
= (sip
->was_idle
== sip
->is_idle
);
6442 send_presence_soap0(struct sipe_account_data
*sip
,
6443 gboolean do_publish_calendar
,
6444 gboolean do_reset_status
)
6446 struct sipe_ews
* ews
= sip
->ews
;
6447 int availability
= 0;
6451 gchar
*res_note
= NULL
;
6452 gchar
*res_oof
= NULL
;
6453 const gchar
*note_pub
= NULL
;
6454 gchar
*states
= NULL
;
6455 gchar
*calendar_data
= NULL
;
6456 gchar
*epid
= get_epid(sip
);
6457 time_t now
= time(NULL
);
6458 gchar
*since_time_str
= g_strdup(purple_utf8_strftime(SIPE_XML_DATE_PATTERN
, gmtime(&now
)));
6459 const gchar
*oof_note
= sipe_ews_get_oof_note(ews
);
6460 const char *user_input
;
6461 gboolean pub_oof
= oof_note
&& (!sip
->note
|| sip
->note_since
< ews
->oof_start
);
6463 if (oof_note
&& sip
->note
) {
6464 purple_debug_info("sipe", "ews->oof_start : %s", asctime(localtime(&(ews
->oof_start
))));
6465 purple_debug_info("sipe", "sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
6468 if (!sip
->initial_state_published
||
6471 g_free(sip
->status
);
6472 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
6475 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
6479 note_pub
= oof_note
;
6480 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
6481 } else if (sip
->note
) {
6482 if (sip
->is_oof_note
) { /* stale OOF note, as it's not present in ews already (oof_note == NULL) */
6485 sip
->is_oof_note
= FALSE
;
6487 note_pub
= sip
->note
;
6493 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, note_pub
);
6497 if (!do_reset_status
) {
6498 if (sipe_is_user_state(sip
) && !do_publish_calendar
&& sip
->initial_state_published
)
6500 gchar
*activity_token
= NULL
;
6501 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
6503 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
6508 g_free(activity_token
);
6510 else /* preserve existing publication */
6513 if (sip
->user_info
&& (xn_states
= xmlnode_get_child(sip
->user_info
, "states"))) {
6514 states
= xmlnode_to_str(xn_states
, NULL
);
6515 /* this is a hack-around to remove added newline after inner element,
6516 * state in this case, where it shouldn't be.
6517 * After several use of xmlnode_to_str, amount of added newlines
6518 * grows significantly.
6520 purple_str_strip_char(states
, '\n');
6521 //purple_str_strip_char(states, '\r');
6525 /* do nothing - then User state will be erased */
6527 sip
->initial_state_published
= TRUE
;
6530 if (ews
&& (!is_empty(ews
->legacy_dn
) || !is_empty(ews
->email
)) && ews
->fb_start
&& !is_empty(ews
->free_busy
))
6532 char *fb_start_str
= g_strdup(purple_utf8_strftime(SIPE_XML_DATE_PATTERN
, gmtime(&ews
->fb_start
)));
6533 char *free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
6534 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
6535 !is_empty(ews
->legacy_dn
) ? ews
->legacy_dn
: ews
->email
,
6538 g_free(fb_start_str
);
6539 g_free(free_busy_base64
);
6542 user_input
= !sipe_is_user_state(sip
) && sip
->status
!= SIPE_STATUS_ID_AVAILABLE
? "idle" : "active";
6544 /* forming resulting XML */
6545 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
6549 (tmp
= g_ascii_strup(sipe_get_host_name(), -1)),
6550 res_note
? res_note
: "",
6551 res_oof
? res_oof
: "",
6552 states
? states
: "",
6553 calendar_data
? calendar_data
: "",
6561 g_free(calendar_data
);
6563 send_soap_request(sip
, body
);
6566 g_free(since_time_str
);
6571 send_presence_soap(struct sipe_account_data
*sip
,
6572 gboolean do_publish_calendar
)
6574 return send_presence_soap0(sip
, do_publish_calendar
, FALSE
);
6579 process_send_presence_category_publish_response(struct sipe_account_data
*sip
,
6581 struct transaction
*trans
)
6583 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
6585 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
6591 gboolean has_device_publication
= FALSE
;
6593 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
6595 /* test if version mismatch fault */
6596 fault_code
= xmlnode_get_data(xmlnode_get_child(xml
, "Faultcode"));
6597 if (strcmp(fault_code
, "Client.BadCall.WrongDelta")) {
6598 purple_debug_info("sipe", "process_send_presence_category_publish_response: unsupported fault code:%s returning.\n", fault_code
);
6605 /* accumulating information about faulty versions */
6606 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
6607 for (node
= xmlnode_get_descendant(xml
, "details", "operation", NULL
);
6609 node
= xmlnode_get_next_twin(node
))
6611 const gchar
*index
= xmlnode_get_attrib(node
, "index");
6612 const gchar
*curVersion
= xmlnode_get_attrib(node
, "curVersion");
6614 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
6615 purple_debug_info("sipe", "fault added: index:%s curVersion:%s\n", index
, curVersion
);
6619 /* here we are parsing own request to figure out what publication
6620 * referensed here only by index went wrong
6622 xml
= xmlnode_from_str(trans
->msg
->body
, trans
->msg
->bodylen
);
6625 for (node
= xmlnode_get_descendant(xml
, "publications", "publication", NULL
),
6626 index_our
= 1; /* starts with 1 - our first publication */
6628 node
= xmlnode_get_next_twin(node
), index_our
++)
6630 gchar
*idx
= g_strdup_printf("%d", index_our
);
6631 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
6632 const gchar
*categoryName
= xmlnode_get_attrib(node
, "categoryName");
6635 if (!strcmp("device", categoryName
)) {
6636 has_device_publication
= TRUE
;
6639 if (curVersion
) { /* fault exist on this index */
6640 const gchar
*container
= xmlnode_get_attrib(node
, "container");
6641 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
6642 /* key is <category><instance><container> */
6643 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
6644 struct sipe_publication
*publication
=
6645 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, categoryName
), key
);
6647 purple_debug_info("sipe", "key is %s\n", key
);
6650 purple_debug_info("sipe", "Updating %s with version %s. Was %d before.\n",
6651 key
, curVersion
, publication
->version
);
6652 /* updating publication's version to the correct one */
6653 publication
->version
= atoi(curVersion
);
6659 g_hash_table_destroy(faults
);
6661 /* rebublishing with right versions */
6662 if (has_device_publication
) {
6663 send_publish_category_initial(sip
);
6665 send_presence_status(sip
);
6672 * Returns 'device' XML part for publication.
6673 * Must be g_free'd after use.
6676 sipe_publish_get_category_device(struct sipe_account_data
*sip
)
6680 gchar
*epid
= get_epid(sip
);
6681 gchar
*uuid
= generateUUIDfromEPID(epid
);
6682 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
6683 /* key is <category><instance><container> */
6684 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
6685 struct sipe_publication
*publication
=
6686 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
6691 uri
= sip_uri_self(sip
);
6692 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
6694 publication
? publication
->version
: 0,
6697 "00:00:00+01:00", /* @TODO make timezone real*/
6698 sipe_get_host_name()
6708 * A service method - use
6709 * - send_publish_get_category_state_machine and
6710 * - send_publish_get_category_state_user instead.
6711 * Must be g_free'd after use.
6714 sipe_publish_get_category_state(struct sipe_account_data
*sip
,
6715 gboolean is_user_state
)
6717 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
6718 guint instance
= is_user_state
? sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
) :
6719 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
6720 /* key is <category><instance><container> */
6721 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
6722 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
6723 struct sipe_publication
*publication_2
=
6724 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
6725 struct sipe_publication
*publication_3
=
6726 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
6731 if (publication_2
&& (publication_2
->availability
== availability
))
6733 purple_debug_info("sipe", "sipe_publish_get_category_state: state has NOT changed. Exiting.\n");
6734 return NULL
; /* nothing to update */
6737 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
6739 publication_2
? publication_2
->version
: 0,
6742 publication_3
? publication_3
->version
: 0,
6747 * Only Busy and OOF calendar event are published.
6748 * Different instances are used for that.
6750 * Must be g_free'd after use.
6753 sipe_publish_get_category_state_calendar(struct sipe_account_data
*sip
,
6754 struct sipe_cal_event
*event
,
6758 gchar
*start_time_str
;
6759 int availability
= 0;
6761 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
6762 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
) :
6763 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
6765 /* key is <category><instance><container> */
6766 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
6767 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
6768 struct sipe_publication
*publication_2
=
6769 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
6770 struct sipe_publication
*publication_3
=
6771 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
6776 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
6777 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
6778 "Exiting as no publication and no event for cal_satus:%d\n", cal_satus
);
6784 (publication_3
->availability
== availability
) &&
6785 !strcmp(publication_3
->cal_event_hash
, sipe_cal_event_hash(event
)))
6787 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
6788 "cal state has NOT changed for cal_satus:%d. Exiting.\n", cal_satus
);
6789 return NULL
; /* nothing to update */
6793 (event
->cal_status
== SIPE_CAL_BUSY
||
6794 event
->cal_status
== SIPE_CAL_OOF
))
6796 gchar
*availability_xml_str
= NULL
;
6797 gchar
*activity_xml_str
= NULL
;
6799 if (event
->cal_status
== SIPE_CAL_BUSY
) {
6800 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
6803 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
6804 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
6805 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
6806 "minAvailability=\"6500\"",
6807 "maxAvailability=\"8999\"");
6808 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
6809 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
6810 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
6811 "minAvailability=\"12000\"",
6814 start_time_str
= g_strdup(purple_utf8_strftime(SIPE_XML_DATE_PATTERN
, gmtime(&event
->start_time
)));
6816 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
6818 publication_2
? publication_2
->version
: 0,
6821 availability_xml_str
? availability_xml_str
: "",
6822 activity_xml_str
? activity_xml_str
: "",
6823 event
->subject
? event
->subject
: "",
6824 event
->location
? event
->location
: "",
6827 publication_3
? publication_3
->version
: 0,
6830 availability_xml_str
? availability_xml_str
: "",
6831 activity_xml_str
? activity_xml_str
: "",
6832 event
->subject
? event
->subject
: "",
6833 event
->location
? event
->location
: ""
6835 g_free(start_time_str
);
6836 g_free(availability_xml_str
);
6837 g_free(activity_xml_str
);
6840 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
6842 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
6844 publication_2
? publication_2
->version
: 0,
6847 publication_3
? publication_3
->version
: 0
6855 * Returns 'machineState' XML part for publication.
6856 * Must be g_free'd after use.
6859 sipe_publish_get_category_state_machine(struct sipe_account_data
*sip
)
6861 return sipe_publish_get_category_state(sip
, FALSE
);
6865 * Returns 'userState' XML part for publication.
6866 * Must be g_free'd after use.
6869 sipe_publish_get_category_state_user(struct sipe_account_data
*sip
)
6871 return sipe_publish_get_category_state(sip
, TRUE
);
6875 * Compares two strings even in case both are NULL/empty
6878 sipe_is_equal(const char* n1
, const char* n2
) {
6879 return ((!n1
|| !strlen(n1
)) && (!n2
|| !strlen(n2
))) /* both empty */
6880 || (n1
&& n2
&& !strcmp(n1
, n2
)); /* or not empty and equal */
6884 * Returns 'note' XML part for publication.
6885 * Must be g_free'd after use.
6887 * @param note_type either personal or OOF
6890 sipe_publish_get_category_note(struct sipe_account_data
*sip
,
6892 const char *note_type
)
6894 guint instance
= !strcmp("OOF", note_type
) ? sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
) : 0;
6895 /* key is <category><instance><container> */
6896 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
6897 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
6898 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
6900 struct sipe_publication
*publication_note_200
=
6901 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
6902 struct sipe_publication
*publication_note_300
=
6903 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
6904 struct sipe_publication
*publication_note_400
=
6905 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
6907 const char *n1
= note
;
6908 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
6910 g_free(key_note_200
);
6911 g_free(key_note_300
);
6912 g_free(key_note_400
);
6914 if (sipe_is_equal(n1
, n2
))
6916 purple_debug_info("sipe", "sipe_publish_get_category_note: note has NOT changed. Exiting.\n");
6917 return NULL
; /* nothing to update */
6920 return g_markup_printf_escaped(SIPE_PUB_XML_NOTE
,
6922 publication_note_200
? publication_note_200
->version
: 0,
6927 publication_note_300
? publication_note_300
->version
: 0,
6932 publication_note_400
? publication_note_400
->version
: 0,
6938 * Returns 'calendarData' XML part with WorkingHours for publication.
6939 * Must be g_free'd after use.
6942 sipe_publish_get_category_cal_working_hours(struct sipe_account_data
*sip
)
6944 struct sipe_ews
* ews
= sip
->ews
;
6946 /* key is <category><instance><container> */
6947 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
6948 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
6949 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
6950 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
6951 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
6952 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
6954 struct sipe_publication
*publication_cal_1
=
6955 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
6956 struct sipe_publication
*publication_cal_100
=
6957 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
6958 struct sipe_publication
*publication_cal_200
=
6959 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
6960 struct sipe_publication
*publication_cal_300
=
6961 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
6962 struct sipe_publication
*publication_cal_400
=
6963 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
6964 struct sipe_publication
*publication_cal_32000
=
6965 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
6967 const char *n1
= ews
? ews
->working_hours_xml_str
: NULL
;
6968 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
6971 g_free(key_cal_100
);
6972 g_free(key_cal_200
);
6973 g_free(key_cal_300
);
6974 g_free(key_cal_400
);
6975 g_free(key_cal_32000
);
6977 if (!ews
|| is_empty(ews
->email
) || is_empty(ews
->working_hours_xml_str
)) {
6978 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: no data to publish, exiting\n");
6982 if (sipe_is_equal(n1
, n2
))
6984 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.\n");
6985 return NULL
; /* nothing to update */
6988 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
6990 publication_cal_1
? publication_cal_1
->version
: 0,
6992 ews
->working_hours_xml_str
,
6994 publication_cal_100
? publication_cal_100
->version
: 0,
6996 publication_cal_200
? publication_cal_200
->version
: 0,
6998 ews
->working_hours_xml_str
,
7000 publication_cal_300
? publication_cal_300
->version
: 0,
7002 ews
->working_hours_xml_str
,
7003 /* 400 - Personal */
7004 publication_cal_400
? publication_cal_400
->version
: 0,
7006 ews
->working_hours_xml_str
,
7007 /* 32000 - Blocked */
7008 publication_cal_32000
? publication_cal_32000
->version
: 0
7013 * Returns 'calendarData' XML part with FreeBusy for publication.
7014 * Must be g_free'd after use.
7017 sipe_publish_get_category_cal_free_busy(struct sipe_account_data
*sip
)
7019 struct sipe_ews
* ews
= sip
->ews
;
7020 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
7022 char *free_busy_base64
;
7027 /* key is <category><instance><container> */
7028 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
7029 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
7030 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
7031 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
7032 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
7033 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
7035 struct sipe_publication
*publication_cal_1
=
7036 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7037 struct sipe_publication
*publication_cal_100
=
7038 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7039 struct sipe_publication
*publication_cal_200
=
7040 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7041 struct sipe_publication
*publication_cal_300
=
7042 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7043 struct sipe_publication
*publication_cal_400
=
7044 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7045 struct sipe_publication
*publication_cal_32000
=
7046 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7049 g_free(key_cal_100
);
7050 g_free(key_cal_200
);
7051 g_free(key_cal_300
);
7052 g_free(key_cal_400
);
7053 g_free(key_cal_32000
);
7055 if (!ews
|| is_empty(ews
->email
) || !ews
->fb_start
|| is_empty(ews
->free_busy
)) {
7056 purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: no data to publish, exiting\n");
7060 fb_start_str
= g_strdup(purple_utf8_strftime(SIPE_XML_DATE_PATTERN
, gmtime(&ews
->fb_start
)));
7061 free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
7063 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
7064 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
7066 if (sipe_is_equal(st
, fb_start_str
) && sipe_is_equal(fb
, free_busy_base64
))
7068 purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.\n");
7069 g_free(fb_start_str
);
7070 g_free(free_busy_base64
);
7071 return NULL
; /* nothing to update */
7074 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
7077 publication_cal_1
? publication_cal_1
->version
: 0,
7080 publication_cal_100
? publication_cal_100
->version
: 0,
7083 publication_cal_200
? publication_cal_200
->version
: 0,
7089 publication_cal_300
? publication_cal_300
->version
: 0,
7093 /* 400 - Personal */
7095 publication_cal_400
? publication_cal_400
->version
: 0,
7099 /* 32000 - Blocked */
7101 publication_cal_32000
? publication_cal_32000
->version
: 0
7104 g_free(fb_start_str
);
7105 g_free(free_busy_base64
);
7109 static void send_presence_publish(struct sipe_account_data
*sip
, const char *publications
)
7116 uri
= sip_uri_self(sip
);
7117 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
7121 tmp
= get_contact(sip
);
7122 hdr
= g_strdup_printf("Contact: %s\r\n"
7123 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
7125 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
7134 send_publish_category_initial(struct sipe_account_data
*sip
)
7136 gchar
*pub_device
= sipe_publish_get_category_device(sip
);
7138 gchar
*publications
;
7140 g_free(sip
->status
);
7141 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
7143 pub_machine
= sipe_publish_get_category_state_machine(sip
);
7144 publications
= g_strdup_printf("%s%s",
7146 pub_machine
? pub_machine
: "");
7148 g_free(pub_machine
);
7150 send_presence_publish(sip
, publications
);
7151 g_free(publications
);
7155 send_presence_category_publish(struct sipe_account_data
*sip
,
7158 gchar
*pub_state
= sipe_is_user_state(sip
) ?
7159 sipe_publish_get_category_state_user(sip
) :
7160 sipe_publish_get_category_state_machine(sip
);
7161 gchar
*pub_note
= sipe_publish_get_category_note(sip
, note
, "personal");
7162 gchar
*publications
;
7164 if (!pub_state
&& !pub_note
) {
7165 purple_debug_info("sipe", "send_presence_category_publish: nothing has changed. Exiting.\n");
7169 publications
= g_strdup_printf("%s%s",
7170 pub_state
? pub_state
: "",
7171 pub_note
? pub_note
: "");
7173 purple_debug_info("sipe", "send_presence_category_publish: sip->status: %s sip->is_idle:%s sip->was_idle:%s\n",
7174 sip
->status
, sip
->is_idle
? "Y" : "N", sip
->was_idle
? "Y" : "N");
7179 send_presence_publish(sip
, publications
);
7180 g_free(publications
);
7184 * Publishes self status
7185 * based on own calendar information.
7190 publish_calendar_status_self(struct sipe_account_data
*sip
)
7192 struct sipe_cal_event
* event
= NULL
;
7193 gchar
*pub_cal_working_hours
= NULL
;
7194 gchar
*pub_cal_free_busy
= NULL
;
7195 gchar
*pub_calendar
= NULL
;
7196 gchar
*pub_calendar2
= NULL
;
7197 gchar
*pub_oof_note
= NULL
;
7198 const gchar
*oof_note
;
7201 purple_debug_info("sipe", "publish_calendar_status_self() no calendar data.\n");
7205 purple_debug_info("sipe", "publish_calendar_status_self() started.\n");
7206 if (sip
->ews
->cal_events
) {
7207 event
= sipe_cal_get_event(sip
->ews
->cal_events
, time(NULL
));
7211 purple_debug_info("sipe", "publish_calendar_status_self: current event is NULL\n");
7213 char *desc
= sipe_cal_event_describe(event
);
7214 purple_debug_info("sipe", "publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
7220 OOF publish, Busy clean
7222 OOF clean, Busy publish
7224 OOF clean, Busy clean
7226 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
7227 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_OOF
);
7228 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7229 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
7230 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7231 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7233 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7234 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7237 if ((oof_note
= sipe_ews_get_oof_note(sip
->ews
))) {
7238 pub_oof_note
= sipe_publish_get_category_note(sip
, oof_note
, "OOF");
7241 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sip
);
7242 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sip
);
7244 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
7245 purple_debug_info("sipe", "publish_calendar_status_self: nothing has changed.\n");
7247 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
7248 pub_cal_working_hours
? pub_cal_working_hours
: "",
7249 pub_cal_free_busy
? pub_cal_free_busy
: "",
7250 pub_calendar
? pub_calendar
: "",
7251 pub_calendar2
? pub_calendar2
: "",
7252 pub_oof_note
? pub_oof_note
: "");
7254 send_presence_publish(sip
, publications
);
7255 g_free(publications
);
7258 g_free(pub_cal_working_hours
);
7259 g_free(pub_cal_free_busy
);
7260 g_free(pub_calendar
);
7261 g_free(pub_calendar2
);
7262 g_free(pub_oof_note
);
7264 /* repeat scheduling */
7265 sipe_sched_calendar_status_self_publish(sip
, time(NULL
));
7268 static void send_presence_status(struct sipe_account_data
*sip
)
7270 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
7272 if (!status
) return;
7274 note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
7275 purple_debug_info("sipe", "send_presence_status: status: %s (%s)\n",
7276 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
7277 sipe_is_user_state(sip
) ? "USER" : "MACHINE");
7278 purple_debug_info("sipe", "send_presence_status: note: '%s'\n", note
? note
: "");
7281 send_presence_category_publish(sip
, note
);
7283 send_presence_soap(sip
, FALSE
);
7287 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
7289 gboolean found
= FALSE
;
7290 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
7291 if (msg
->response
== 0) { /* request */
7292 if (!strcmp(msg
->method
, "MESSAGE")) {
7293 process_incoming_message(sip
, msg
);
7295 } else if (!strcmp(msg
->method
, "NOTIFY")) {
7296 purple_debug_info("sipe","send->process_incoming_notify\n");
7297 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
7299 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
7300 purple_debug_info("sipe","send->process_incoming_benotify\n");
7301 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
7303 } else if (!strcmp(msg
->method
, "INVITE")) {
7304 process_incoming_invite(sip
, msg
);
7306 } else if (!strcmp(msg
->method
, "REFER")) {
7307 process_incoming_refer(sip
, msg
);
7309 } else if (!strcmp(msg
->method
, "OPTIONS")) {
7310 process_incoming_options(sip
, msg
);
7312 } else if (!strcmp(msg
->method
, "INFO")) {
7313 process_incoming_info(sip
, msg
);
7315 } else if (!strcmp(msg
->method
, "ACK")) {
7316 // ACK's don't need any response
7318 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
7319 // LCS 2005 sends us these - just respond 200 OK
7321 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
7322 } else if (!strcmp(msg
->method
, "BYE")) {
7323 process_incoming_bye(sip
, msg
);
7326 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
7328 } else { /* response */
7329 struct transaction
*trans
= transactions_find(sip
, msg
);
7331 if (msg
->response
== 407) {
7332 gchar
*resend
, *auth
, *ptmp
;
7334 if (sip
->proxy
.retries
> 30) return;
7335 sip
->proxy
.retries
++;
7336 /* do proxy authentication */
7338 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
7340 fill_auth(ptmp
, &sip
->proxy
);
7341 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
7342 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
7343 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
7345 resend
= sipmsg_to_string(trans
->msg
);
7346 /* resend request */
7347 sendout_pkt(sip
->gc
, resend
);
7350 if (msg
->response
< 200) {
7351 /* ignore provisional response */
7352 purple_debug_info("sipe", "got provisional (%d) response, ignoring\n", msg
->response
);
7354 sip
->proxy
.retries
= 0;
7355 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
7356 if (msg
->response
== 401)
7358 sip
->registrar
.retries
++;
7362 sip
->registrar
.retries
= 0;
7364 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\n", sip
->cseq
);
7366 if (msg
->response
== 401) {
7367 gchar
*resend
, *auth
, *ptmp
;
7369 if (sip
->registrar
.retries
> 4) return;
7370 sip
->registrar
.retries
++;
7373 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
7375 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
7378 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
7382 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\n", ptmp
);
7384 fill_auth(ptmp
, &sip
->registrar
);
7385 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
7386 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
7387 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
7389 //sipmsg_remove_header_now(trans->msg, "Authorization");
7390 //sipmsg_add_header(trans->msg, "Authorization", auth);
7392 resend
= sipmsg_to_string(trans
->msg
);
7393 /* resend request */
7394 sendout_pkt(sip
->gc
, resend
);
7399 if (trans
->callback
) {
7400 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\n");
7401 /* call the callback to process response*/
7402 (trans
->callback
)(sip
, msg
, trans
);
7405 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\n", sip
->cseq
);
7406 transactions_remove(sip
, trans
);
7412 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
7416 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
7420 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
7429 /* according to the RFC remove CRLF at the beginning */
7430 while (*cur
== '\r' || *cur
== '\n') {
7433 if (cur
!= conn
->inbuf
) {
7434 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
7435 conn
->inbufused
= strlen(conn
->inbuf
);
7438 /* Received a full Header? */
7439 sip
->processing_input
= TRUE
;
7440 while (sip
->processing_input
&&
7441 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
7442 time_t currtime
= time(NULL
);
7445 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), tmp
= fix_newlines(conn
->inbuf
));
7447 msg
= sipmsg_parse_header(conn
->inbuf
);
7450 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
7451 if (msg
&& restlen
>= msg
->bodylen
) {
7452 dummy
= g_malloc(msg
->bodylen
+ 1);
7453 memcpy(dummy
, cur
, msg
->bodylen
);
7454 dummy
[msg
->bodylen
] = '\0';
7456 cur
+= msg
->bodylen
;
7457 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
7458 conn
->inbufused
= strlen(conn
->inbuf
);
7461 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n", restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
7468 purple_debug_info("sipe", "body:\n%s", msg->body);
7471 // Verify the signature before processing it
7472 if (sip
->registrar
.gssapi_context
) {
7473 struct sipmsg_breakdown msgbd
;
7474 gchar
*signature_input_str
;
7477 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
7478 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
7480 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
7482 if (rspauth
!= NULL
) {
7483 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
7484 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
7485 process_input_message(sip
, msg
);
7487 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
7488 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
7489 sip
->gc
->wants_to_die
= TRUE
;
7491 } else if (msg
->response
== 401) {
7492 purple_connection_error(sip
->gc
, _("Wrong password"));
7493 sip
->gc
->wants_to_die
= TRUE
;
7495 g_free(signature_input_str
);
7498 sipmsg_breakdown_free(&msgbd
);
7500 process_input_message(sip
, msg
);
7507 static void sipe_udp_process(gpointer data
, gint source
,
7508 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
7510 PurpleConnection
*gc
= data
;
7511 struct sipe_account_data
*sip
= gc
->proto_data
;
7514 static char buffer
[65536];
7515 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
7516 time_t currtime
= time(NULL
);
7519 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), buffer
);
7520 msg
= sipmsg_parse_msg(buffer
);
7521 if (msg
) process_input_message(sip
, msg
);
7525 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
7527 struct sipe_account_data
*sip
= gc
->proto_data
;
7528 PurpleSslConnection
*gsc
= sip
->gsc
;
7530 purple_debug_error("sipe", "%s",debug
);
7531 purple_connection_error(gc
, msg
);
7533 /* Invalidate this connection. Next send will open a new one */
7535 connection_remove(sip
, gsc
->fd
);
7536 purple_ssl_close(gsc
);
7542 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
7543 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
7545 PurpleConnection
*gc
= data
;
7546 struct sipe_account_data
*sip
;
7547 struct sip_connection
*conn
;
7549 gboolean firstread
= TRUE
;
7551 /* NOTE: This check *IS* necessary */
7552 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
7553 purple_ssl_close(gsc
);
7557 sip
= gc
->proto_data
;
7558 conn
= connection_find(sip
, gsc
->fd
);
7560 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
7561 gc
->wants_to_die
= TRUE
;
7562 purple_connection_error(gc
, _("Connection not found. Please try to connect again"));
7566 /* Read all available data from the SSL connection */
7568 /* Increase input buffer size as needed */
7569 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
7570 conn
->inbuflen
+= SIMPLE_BUF_INC
;
7571 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
7572 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
7575 /* Try to read as much as there is space left in the buffer */
7576 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
7577 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
7579 if (len
< 0 && errno
== EAGAIN
) {
7580 /* Try again later */
7582 } else if (len
< 0) {
7583 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
7585 } else if (firstread
&& (len
== 0)) {
7586 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
7590 conn
->inbufused
+= len
;
7593 /* Equivalence indicates that there is possibly more data to read */
7594 } while (len
== readlen
);
7596 conn
->inbuf
[conn
->inbufused
] = '\0';
7597 process_input(sip
, conn
);
7601 static void sipe_input_cb(gpointer data
, gint source
,
7602 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
7604 PurpleConnection
*gc
= data
;
7605 struct sipe_account_data
*sip
= gc
->proto_data
;
7607 struct sip_connection
*conn
= connection_find(sip
, source
);
7609 purple_debug_error("sipe", "Connection not found!\n");
7613 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
7614 conn
->inbuflen
+= SIMPLE_BUF_INC
;
7615 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
7618 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
7620 if (len
< 0 && errno
== EAGAIN
)
7622 else if (len
<= 0) {
7623 purple_debug_info("sipe", "sipe_input_cb: read error\n");
7624 connection_remove(sip
, source
);
7625 if (sip
->fd
== source
) sip
->fd
= -1;
7629 conn
->inbufused
+= len
;
7630 conn
->inbuf
[conn
->inbufused
] = '\0';
7632 process_input(sip
, conn
);
7635 /* Callback for new connections on incoming TCP port */
7636 static void sipe_newconn_cb(gpointer data
, gint source
,
7637 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
7639 PurpleConnection
*gc
= data
;
7640 struct sipe_account_data
*sip
= gc
->proto_data
;
7641 struct sip_connection
*conn
;
7643 int newfd
= accept(source
, NULL
, NULL
);
7645 conn
= connection_create(sip
, newfd
);
7647 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
7650 static void login_cb(gpointer data
, gint source
,
7651 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
7653 PurpleConnection
*gc
= data
;
7654 struct sipe_account_data
*sip
;
7655 struct sip_connection
*conn
;
7657 if (!PURPLE_CONNECTION_IS_VALID(gc
))
7665 purple_connection_error(gc
, _("Could not connect"));
7669 sip
= gc
->proto_data
;
7671 sip
->last_keepalive
= time(NULL
);
7673 conn
= connection_create(sip
, source
);
7677 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
7680 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
7681 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
7683 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
7684 if (sip
== NULL
) return;
7689 static guint
sipe_ht_hash_nick(const char *nick
)
7691 char *lc
= g_utf8_strdown(nick
, -1);
7692 guint bucket
= g_str_hash(lc
);
7698 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
7700 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
7703 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
7705 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
7707 sip
->listen_data
= NULL
;
7709 if (listenfd
== -1) {
7710 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
7716 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
7717 sip
->listenfd
= sip
->fd
;
7719 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
7721 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
7725 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
7726 SIPE_UNUSED_PARAMETER
const char *error_message
)
7728 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
7730 sip
->query_data
= NULL
;
7732 if (!hosts
|| !hosts
->data
) {
7733 purple_connection_error(sip
->gc
, _("Could not resolve hostname"));
7737 hosts
= g_slist_remove(hosts
, hosts
->data
);
7738 g_free(sip
->serveraddr
);
7739 sip
->serveraddr
= hosts
->data
;
7740 hosts
= g_slist_remove(hosts
, hosts
->data
);
7742 hosts
= g_slist_remove(hosts
, hosts
->data
);
7743 g_free(hosts
->data
);
7744 hosts
= g_slist_remove(hosts
, hosts
->data
);
7747 /* create socket for incoming connections */
7748 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
7749 sipe_udp_host_resolved_listen_cb
, sip
);
7750 if (sip
->listen_data
== NULL
) {
7751 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
7756 static const struct sipe_service_data
*current_service
= NULL
;
7758 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
7759 PurpleSslErrorType error
,
7762 PurpleConnection
*gc
= data
;
7763 struct sipe_account_data
*sip
;
7765 /* If the connection is already disconnected, we don't need to do anything else */
7766 if (!PURPLE_CONNECTION_IS_VALID(gc
))
7769 sip
= gc
->proto_data
;
7770 current_service
= sip
->service_data
;
7771 if (current_service
) {
7772 purple_debug_info("sipe", "current_service: transport '%s' service '%s'\n",
7773 current_service
->transport
? current_service
->transport
: "NULL",
7774 current_service
->service
? current_service
->service
: "NULL");
7781 case PURPLE_SSL_CONNECT_FAILED
:
7782 purple_connection_error(gc
, _("Connection failed"));
7784 case PURPLE_SSL_HANDSHAKE_FAILED
:
7785 purple_connection_error(gc
, _("SSL handshake failed"));
7787 case PURPLE_SSL_CERTIFICATE_INVALID
:
7788 purple_connection_error(gc
, _("SSL certificate invalid"));
7794 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
7796 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
7797 PurpleProxyConnectData
*connect_data
;
7799 sip
->listen_data
= NULL
;
7801 sip
->listenfd
= listenfd
;
7802 if (sip
->listenfd
== -1) {
7803 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
7807 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
7808 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
7809 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
7810 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
7811 sipe_newconn_cb
, sip
->gc
);
7812 purple_debug_info("sipe", "connecting to %s port %d\n",
7813 sip
->realhostname
, sip
->realport
);
7814 /* open tcp connection to the server */
7815 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
7816 sip
->realport
, login_cb
, sip
->gc
);
7818 if (connect_data
== NULL
) {
7819 purple_connection_error(sip
->gc
, _("Could not create socket"));
7823 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
7825 PurpleAccount
*account
= sip
->account
;
7826 PurpleConnection
*gc
= sip
->gc
;
7829 port
= (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
7832 sip
->realhostname
= hostname
;
7833 sip
->realport
= port
;
7835 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
7838 /* TODO: is there a good default grow size? */
7839 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
7840 sip
->txbuf
= purple_circ_buffer_new(0);
7842 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
7844 if (!purple_ssl_is_supported()) {
7845 gc
->wants_to_die
= TRUE
;
7846 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor"));
7850 purple_debug_info("sipe", "using SSL\n");
7852 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
7853 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
7854 if (sip
->gsc
== NULL
) {
7855 purple_connection_error(gc
, _("Could not create SSL context"));
7858 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
7860 purple_debug_info("sipe", "using UDP\n");
7862 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
7863 if (sip
->query_data
== NULL
) {
7864 purple_connection_error(gc
, _("Could not resolve hostname"));
7868 purple_debug_info("sipe", "using TCP\n");
7869 /* create socket for incoming connections */
7870 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
7871 sipe_tcp_connect_listen_cb
, sip
);
7872 if (sip
->listen_data
== NULL
) {
7873 purple_connection_error(gc
, _("Could not create listen socket"));
7879 /* Service list for autodection */
7880 static const struct sipe_service_data service_autodetect
[] = {
7881 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
7882 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
7883 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
7884 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
7888 /* Service list for SSL/TLS */
7889 static const struct sipe_service_data service_tls
[] = {
7890 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
7891 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
7895 /* Service list for TCP */
7896 static const struct sipe_service_data service_tcp
[] = {
7897 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
7898 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
7902 /* Service list for UDP */
7903 static const struct sipe_service_data service_udp
[] = {
7904 { "sip", "udp", SIPE_TRANSPORT_UDP
},
7908 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
7909 static void resolve_next_service(struct sipe_account_data
*sip
,
7910 const struct sipe_service_data
*start
)
7913 sip
->service_data
= start
;
7915 sip
->service_data
++;
7916 if (sip
->service_data
->service
== NULL
) {
7918 /* Try connecting to the SIP hostname directly */
7919 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
7920 if (sip
->auto_transport
) {
7921 // If SSL is supported, default to using it; OCS servers aren't configured
7922 // by default to accept TCP
7923 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
7924 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
7925 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
7928 hostname
= g_strdup(sip
->sipdomain
);
7929 create_connection(sip
, hostname
, 0);
7934 /* Try to resolve next service */
7935 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
7936 sip
->service_data
->transport
,
7941 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
7943 struct sipe_account_data
*sip
= data
;
7945 sip
->srv_query_data
= NULL
;
7947 /* find the host to connect to */
7949 gchar
*hostname
= g_strdup(resp
->hostname
);
7950 int port
= resp
->port
;
7951 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
7955 sip
->transport
= sip
->service_data
->type
;
7957 create_connection(sip
, hostname
, port
);
7959 resolve_next_service(sip
, NULL
);
7963 static void sipe_login(PurpleAccount
*account
)
7965 PurpleConnection
*gc
;
7966 struct sipe_account_data
*sip
;
7967 gchar
**signinname_login
, **userserver
;
7968 const char *transport
;
7971 const char *username
= purple_account_get_username(account
);
7972 gc
= purple_account_get_connection(account
);
7974 purple_debug_info("sipe", "sipe_login: username '%s'\n", username
);
7976 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
7977 gc
->wants_to_die
= TRUE
;
7978 purple_connection_error(gc
, _("SIP Exchange user name contains invalid characters"));
7982 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
7983 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
7984 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
7986 sip
->account
= account
;
7987 sip
->reregister_set
= FALSE
;
7988 sip
->reauthenticate_set
= FALSE
;
7989 sip
->subscribed
= FALSE
;
7990 sip
->subscribed_buddies
= FALSE
;
7991 sip
->initial_state_published
= FALSE
;
7993 /* username format: <username>,[<optional login>] */
7994 signinname_login
= g_strsplit(username
, ",", 2);
7995 purple_debug_info("sipe", "sipe_login: signinname[0] '%s'\n", signinname_login
[0]);
7997 /* ensure that username format is name@domain */
7998 if (!strchr(signinname_login
[0], '@') || g_str_has_prefix(signinname_login
[0], "@") || g_str_has_suffix(signinname_login
[0], "@")) {
7999 g_strfreev(signinname_login
);
8000 gc
->wants_to_die
= TRUE
;
8001 purple_connection_error(gc
, _("User name should be a valid SIP URI\nExample: user@company.com"));
8004 sip
->username
= g_strdup(signinname_login
[0]);
8006 /* ensure that email format is name@domain if provided */
8007 email
= purple_account_get_string(sip
->account
, "email", NULL
);
8008 if (!is_empty(email
) &&
8009 (!strchr(email
, '@') || g_str_has_prefix(email
, "@") || g_str_has_suffix(email
, "@")))
8011 gc
->wants_to_die
= TRUE
;
8012 purple_connection_error(gc
, _("Email address should be valid if provided\nExample: user@company.com"));
8015 sip
->email
= !is_empty(email
) ? g_strdup(email
) : g_strdup(sip
->username
);
8017 /* login name specified? */
8018 if (signinname_login
[1] && strlen(signinname_login
[1])) {
8019 gchar
**domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
8020 gboolean has_domain
= domain_user
[1] != NULL
;
8021 purple_debug_info("sipe", "sipe_login: signinname[1] '%s'\n", signinname_login
[1]);
8022 sip
->authdomain
= has_domain
? g_strdup(domain_user
[0]) : NULL
;
8023 sip
->authuser
= g_strdup(domain_user
[has_domain
? 1 : 0]);
8024 purple_debug_info("sipe", "sipe_login: auth domain '%s' user '%s'\n",
8025 sip
->authdomain
? sip
->authdomain
: "", sip
->authuser
);
8026 g_strfreev(domain_user
);
8029 userserver
= g_strsplit(signinname_login
[0], "@", 2);
8030 purple_debug_info("sipe", "sipe_login: user '%s' server '%s'\n", userserver
[0], userserver
[1]);
8031 purple_connection_set_display_name(gc
, userserver
[0]);
8032 sip
->sipdomain
= g_strdup(userserver
[1]);
8033 g_strfreev(userserver
);
8034 g_strfreev(signinname_login
);
8036 if (strchr(sip
->username
, ' ') != NULL
) {
8037 gc
->wants_to_die
= TRUE
;
8038 purple_connection_error(gc
, _("SIP Exchange user name contains whitespace"));
8042 sip
->password
= g_strdup(purple_connection_get_password(gc
));
8044 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
8045 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8046 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
8047 sip
->subscriptions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8048 g_free
, (GDestroyNotify
)sipe_subscription_free
);
8050 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
8052 g_free(sip
->status
);
8053 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
8055 sip
->auto_transport
= FALSE
;
8056 transport
= purple_account_get_string(account
, "transport", "auto");
8057 userserver
= g_strsplit(purple_account_get_string(account
, "server", ""), ":", 2);
8058 if (userserver
[0]) {
8059 /* Use user specified server[:port] */
8063 port
= atoi(userserver
[1]);
8065 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login: user specified SIP server %s:%d\n",
8066 userserver
[0], port
);
8068 if (strcmp(transport
, "auto") == 0) {
8069 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8070 } else if (strcmp(transport
, "tls") == 0) {
8071 sip
->transport
= SIPE_TRANSPORT_TLS
;
8072 } else if (strcmp(transport
, "tcp") == 0) {
8073 sip
->transport
= SIPE_TRANSPORT_TCP
;
8075 sip
->transport
= SIPE_TRANSPORT_UDP
;
8078 create_connection(sip
, g_strdup(userserver
[0]), port
);
8080 /* Server auto-discovery */
8081 if (strcmp(transport
, "auto") == 0) {
8082 sip
->auto_transport
= TRUE
;
8083 if (current_service
&& current_service
->transport
!= NULL
&& current_service
->service
!= NULL
){
8085 resolve_next_service(sip
, current_service
);
8087 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
8089 } else if (strcmp(transport
, "tls") == 0) {
8090 resolve_next_service(sip
, service_tls
);
8091 } else if (strcmp(transport
, "tcp") == 0) {
8092 resolve_next_service(sip
, service_tcp
);
8094 resolve_next_service(sip
, service_udp
);
8097 g_strfreev(userserver
);
8100 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
8102 connection_free_all(sip
);
8107 if (sip
->query_data
!= NULL
)
8108 purple_dnsquery_destroy(sip
->query_data
);
8109 sip
->query_data
= NULL
;
8111 if (sip
->srv_query_data
!= NULL
)
8112 purple_srv_cancel(sip
->srv_query_data
);
8113 sip
->srv_query_data
= NULL
;
8115 if (sip
->listen_data
!= NULL
)
8116 purple_network_listen_cancel(sip
->listen_data
);
8117 sip
->listen_data
= NULL
;
8119 if (sip
->gsc
!= NULL
)
8120 purple_ssl_close(sip
->gsc
);
8123 sipe_auth_free(&sip
->registrar
);
8124 sipe_auth_free(&sip
->proxy
);
8127 purple_circ_buffer_destroy(sip
->txbuf
);
8130 g_free(sip
->realhostname
);
8131 sip
->realhostname
= NULL
;
8133 g_free(sip
->server_version
);
8134 sip
->server_version
= NULL
;
8137 purple_input_remove(sip
->listenpa
);
8139 if (sip
->tx_handler
)
8140 purple_input_remove(sip
->tx_handler
);
8141 sip
->tx_handler
= 0;
8142 if (sip
->resendtimeout
)
8143 purple_timeout_remove(sip
->resendtimeout
);
8144 sip
->resendtimeout
= 0;
8145 if (sip
->timeouts
) {
8146 GSList
*entry
= sip
->timeouts
;
8148 struct scheduled_action
*sched_action
= entry
->data
;
8149 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
8150 purple_timeout_remove(sched_action
->timeout_handler
);
8151 if (sched_action
->destroy
) {
8152 (*sched_action
->destroy
)(sched_action
->payload
);
8154 g_free(sched_action
->name
);
8155 g_free(sched_action
);
8156 entry
= entry
->next
;
8159 g_slist_free(sip
->timeouts
);
8161 if (sip
->allow_events
) {
8162 GSList
*entry
= sip
->allow_events
;
8164 g_free(entry
->data
);
8165 entry
= entry
->next
;
8168 g_slist_free(sip
->allow_events
);
8170 if (sip
->containers
) {
8171 GSList
*entry
= sip
->containers
;
8173 free_container((struct sipe_container
*)entry
->data
);
8174 entry
= entry
->next
;
8177 g_slist_free(sip
->containers
);
8180 g_free(sip
->contact
);
8181 sip
->contact
= NULL
;
8183 g_free(sip
->regcallid
);
8184 sip
->regcallid
= NULL
;
8186 if (sip
->serveraddr
)
8187 g_free(sip
->serveraddr
);
8188 sip
->serveraddr
= NULL
;
8190 if (sip
->focus_factory_uri
)
8191 g_free(sip
->focus_factory_uri
);
8192 sip
->focus_factory_uri
= NULL
;
8195 sip
->processing_input
= FALSE
;
8198 sipe_ews_free(sip
->ews
);
8204 * A callback for g_hash_table_foreach_remove
8206 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
8207 SIPE_UNUSED_PARAMETER gpointer user_data
)
8209 sipe_free_buddy((struct sipe_buddy
*) buddy
);
8211 /* We must return TRUE as the key/value have already been deleted */
8215 static void sipe_close(PurpleConnection
*gc
)
8217 struct sipe_account_data
*sip
= gc
->proto_data
;
8220 /* leave all conversations */
8221 sipe_session_close_all(sip
);
8222 sipe_session_remove_all(sip
);
8225 sip_csta_close(sip
);
8228 if (PURPLE_CONNECTION_IS_CONNECTED(sip
->gc
)) {
8229 /* unsubscribe all */
8230 g_hash_table_foreach(sip
->subscriptions
, sipe_unsubscribe_cb
, sip
);
8233 do_register_exp(sip
, 0);
8236 sipe_connection_cleanup(sip
);
8237 g_free(sip
->sipdomain
);
8238 g_free(sip
->username
);
8240 g_free(sip
->password
);
8241 g_free(sip
->authdomain
);
8242 g_free(sip
->authuser
);
8243 g_free(sip
->status
);
8246 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
8247 g_hash_table_destroy(sip
->buddies
);
8248 g_hash_table_destroy(sip
->our_publications
);
8249 g_hash_table_destroy(sip
->user_state_publications
);
8250 g_hash_table_destroy(sip
->subscriptions
);
8253 GSList
*entry
= sip
->groups
;
8255 struct sipe_group
*group
= entry
->data
;
8256 g_free(group
->name
);
8258 entry
= entry
->next
;
8261 g_slist_free(sip
->groups
);
8263 if (sip
->our_publication_keys
) {
8264 GSList
*entry
= sip
->our_publication_keys
;
8266 g_free(entry
->data
);
8267 entry
= entry
->next
;
8270 g_slist_free(sip
->our_publication_keys
);
8272 while (sip
->transactions
)
8273 transactions_remove(sip
, sip
->transactions
->data
);
8275 g_free(gc
->proto_data
);
8276 gc
->proto_data
= NULL
;
8279 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
8280 SIPE_UNUSED_PARAMETER
void *user_data
)
8282 PurpleAccount
*acct
= purple_connection_get_account(gc
);
8283 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
8284 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
8286 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
8287 purple_conversation_present(conv
);
8291 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
8292 SIPE_UNUSED_PARAMETER
void *user_data
)
8295 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
8296 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
8299 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
8300 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
8302 PurpleNotifySearchResults
*results
;
8303 PurpleNotifySearchColumn
*column
;
8304 xmlnode
*searchResults
;
8306 int match_count
= 0;
8307 gboolean more
= FALSE
;
8310 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
8312 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
8313 if (!searchResults
) {
8314 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
8318 results
= purple_notify_searchresults_new();
8320 if (results
== NULL
) {
8321 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
8322 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
8324 xmlnode_free(searchResults
);
8328 column
= purple_notify_searchresults_column_new(_("User name"));
8329 purple_notify_searchresults_column_add(results
, column
);
8331 column
= purple_notify_searchresults_column_new(_("Name"));
8332 purple_notify_searchresults_column_add(results
, column
);
8334 column
= purple_notify_searchresults_column_new(_("Company"));
8335 purple_notify_searchresults_column_add(results
, column
);
8337 column
= purple_notify_searchresults_column_new(_("Country"));
8338 purple_notify_searchresults_column_add(results
, column
);
8340 column
= purple_notify_searchresults_column_new(_("Email"));
8341 purple_notify_searchresults_column_add(results
, column
);
8343 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
8346 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
8347 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
8348 g_strfreev(uri_parts
);
8350 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
8351 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
8352 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
8353 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
8355 purple_notify_searchresults_row_add(results
, row
);
8359 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
8360 char *data
= xmlnode_get_data_unescaped(mrow
);
8361 more
= (g_strcasecmp(data
, "true") == 0);
8365 secondary
= g_strdup_printf(
8366 dngettext(GETTEXT_PACKAGE
,
8367 "Found %d contact%s:",
8368 "Found %d contacts%s:", match_count
),
8369 match_count
, more
? _(" (more matched your query)") : "");
8371 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
8372 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
8373 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
8376 xmlnode_free(searchResults
);
8380 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
8382 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
8383 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
8387 PurpleRequestField
*field
= entries
->data
;
8388 const char *id
= purple_request_field_get_id(field
);
8389 const char *value
= purple_request_field_string_get_value(field
);
8391 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
8393 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
8394 } while ((entries
= g_list_next(entries
)) != NULL
);
8398 struct sipe_account_data
*sip
= gc
->proto_data
;
8399 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
8400 gchar
*query
= g_strjoinv(NULL
, attrs
);
8401 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
8402 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
8403 send_soap_request_with_cb(sip
, domain_uri
, body
,
8404 (TransCallback
) process_search_contact_response
, NULL
);
8413 static void sipe_show_find_contact(PurplePluginAction
*action
)
8415 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8416 PurpleRequestFields
*fields
;
8417 PurpleRequestFieldGroup
*group
;
8418 PurpleRequestField
*field
;
8420 fields
= purple_request_fields_new();
8421 group
= purple_request_field_group_new(NULL
);
8422 purple_request_fields_add_group(fields
, group
);
8424 field
= purple_request_field_string_new("givenName", _("First name"), NULL
, FALSE
);
8425 purple_request_field_group_add_field(group
, field
);
8426 field
= purple_request_field_string_new("sn", _("Last name"), NULL
, FALSE
);
8427 purple_request_field_group_add_field(group
, field
);
8428 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
8429 purple_request_field_group_add_field(group
, field
);
8430 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
8431 purple_request_field_group_add_field(group
, field
);
8433 purple_request_fields(gc
,
8435 _("Search for a contact"),
8436 _("Enter the information for the person you wish to find. Empty fields will be ignored."),
8438 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
8440 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
8443 static void sipe_show_about_plugin(PurplePluginAction
*action
)
8445 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8447 "<b><font size=\"+1\">Sipe " SIPE_VERSION
"</font></b><br/>"
8449 "A third-party plugin implementing extended version of SIP/SIMPLE used by various products:<br/>"
8450 "<li> - MS Office Communications Server 2007 (R2)</li><br/>"
8451 "<li> - MS Live Communications Server 2005/2003</li><br/>"
8452 "<li> - Reuters Messaging</li><br/>"
8454 "Home: <a href=\"http://sipe.sourceforge.net\">http://sipe.sourceforge.net</a><br/>"
8455 "Support: <a href=\"http://sourceforge.net/projects/sipe/forums/forum/688534\">Help Forum</a><br/>"
8456 "License: GPLv2<br/>"
8458 "We support users in the following organizations to mention a few:<br/>"
8460 " - Reuters Messaging network<br/>"
8461 " - Deutsche Bank<br/>"
8462 " - Merrill Lynch<br/>"
8465 " - Alcatel-Lucent<br/>"
8470 "<b>Authors:</b><br/>"
8471 " - Anibal Avelar<br/>"
8472 " - Gabriel Burt<br/>"
8473 " - Stefan Becker<br/>"
8476 purple_notify_formatted(gc
, NULL
, " ", NULL
, txt
, NULL
, NULL
);
8479 static void sipe_republish_calendar(PurplePluginAction
*action
)
8481 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8482 struct sipe_account_data
*sip
= gc
->proto_data
;
8484 sipe_update_calendar(sip
);
8487 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
8491 struct sipe_publication
*publication
= value
;
8493 g_string_append_printf( str
,
8494 SIPE_PUB_XML_PUBLICATION_CLEAR
,
8495 publication
->category
,
8496 publication
->instance
,
8497 publication
->container
,
8498 publication
->version
,
8502 static void sipe_reset_status(PurplePluginAction
*action
)
8504 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8505 struct sipe_account_data
*sip
= gc
->proto_data
;
8507 if (sip
->ocs2007
) /* 2007+ */
8509 GString
* str
= g_string_new(NULL
);
8510 gchar
*publications
;
8512 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
8513 purple_debug_info("sipe", "sipe_reset_status: no userState publications, exiting.\n");
8517 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
8518 publications
= g_string_free(str
, FALSE
);
8520 send_presence_publish(sip
, publications
);
8521 g_free(publications
);
8525 send_presence_soap0(sip
, FALSE
, TRUE
);
8529 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
8532 PurpleConnection
*gc
= (PurpleConnection
*)context
;
8533 struct sipe_account_data
*sip
= gc
->proto_data
;
8535 PurplePluginAction
*act
;
8536 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
8538 act
= purple_plugin_action_new(_("About SIPE plugin"), sipe_show_about_plugin
);
8539 menu
= g_list_prepend(menu
, act
);
8541 act
= purple_plugin_action_new(_("Contact search..."), sipe_show_find_contact
);
8542 menu
= g_list_prepend(menu
, act
);
8544 if (!strcmp(calendar
, "EXCH")) {
8545 act
= purple_plugin_action_new(_("Republish Calendar"), sipe_republish_calendar
);
8546 menu
= g_list_prepend(menu
, act
);
8549 act
= purple_plugin_action_new(_("Reset status"), sipe_reset_status
);
8550 menu
= g_list_prepend(menu
, act
);
8552 menu
= g_list_reverse(menu
);
8557 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
8561 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
8567 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
8573 static char *sipe_status_text(PurpleBuddy
*buddy
)
8575 struct sipe_account_data
*sip
;
8576 struct sipe_buddy
*sbuddy
;
8579 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
8580 if (sip
) //happens on pidgin exit
8582 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
8584 if (!is_empty(sbuddy
->activity
) && !is_empty(sbuddy
->annotation
))
8586 text
= g_strdup_printf("%s - %s", sbuddy
->activity
, sbuddy
->annotation
);
8588 else if (!is_empty(sbuddy
->activity
))
8590 text
= g_strdup(sbuddy
->activity
);
8594 text
= g_strdup(sbuddy
->annotation
);
8602 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
8604 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
8605 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
8606 struct sipe_account_data
*sip
;
8607 struct sipe_buddy
*sbuddy
;
8608 char *annotation
= NULL
;
8609 gboolean is_oof_note
= FALSE
;
8610 char *activity
= NULL
;
8611 char *calendar
= NULL
;
8612 char *meeting_subject
= NULL
;
8613 char *meeting_location
= NULL
;
8615 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
8616 if (sip
) //happens on pidgin exit
8618 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
8621 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
8622 is_oof_note
= sbuddy
->is_oof_note
;
8623 activity
= sbuddy
->activity
;
8624 calendar
= sipe_cal_get_description(sbuddy
);
8625 meeting_subject
= sbuddy
->meeting_subject
;
8626 meeting_location
= sbuddy
->meeting_location
;
8631 if (purple_presence_is_online(presence
))
8633 const char *status_str
= activity
&& status
&& strcmp(purple_status_get_id(status
), SIPE_STATUS_ID_ON_PHONE
) ?
8635 purple_status_get_name(status
);
8637 purple_notify_user_info_add_pair(user_info
, _("Status"), status_str
);
8639 if (purple_presence_is_online(presence
) &&
8640 !is_empty(calendar
))
8642 purple_notify_user_info_add_pair(user_info
, _("Calendar"), calendar
);
8645 if (!is_empty(meeting_location
))
8647 purple_notify_user_info_add_pair(user_info
, _("Meeting in"), meeting_location
);
8649 if (!is_empty(meeting_subject
))
8651 purple_notify_user_info_add_pair(user_info
, _("Meeting about"), meeting_subject
);
8656 /* Tooltip does not know how to handle markup like <br> */
8657 gchar
*s
= annotation
;
8658 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, annotation
);
8659 while ((s
= strchr(s
, '<')) != NULL
) {
8660 if (!g_ascii_strncasecmp(s
, "<br>", 4)) {
8662 strcpy(s
+ 1, s
+ 4);
8666 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, annotation
);
8668 purple_notify_user_info_add_pair(user_info
, is_oof_note
? _("Out of office note") : _("Note"), annotation
);
8674 #if PURPLE_VERSION_CHECK(2,5,0)
8676 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
8679 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
8680 g_hash_table_insert(table
, "login_label", (gpointer
)_("user@company.com"));
8685 static PurpleBuddy
*
8686 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
8689 const gchar
*server_alias
, *email
;
8690 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
8692 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
8694 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
8696 server_alias
= purple_buddy_get_server_alias(buddy
);
8698 purple_blist_server_alias_buddy(clone
, server_alias
);
8701 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
8703 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
8706 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
8708 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
8713 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
8715 PurpleBuddy
*buddy
, *b
;
8716 PurpleConnection
*gc
;
8717 PurpleGroup
* group
= purple_find_group(group_name
);
8719 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
8721 buddy
= (PurpleBuddy
*)node
;
8723 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
8724 gc
= purple_account_get_connection(buddy
->account
);
8726 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
8728 purple_blist_add_buddy_clone(group
, buddy
);
8731 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
8735 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
8737 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
8739 purple_debug_info("sipe", "sipe_buddy_menu_chat_new_cb: buddy->name=%s\n", buddy
->name
);
8741 /* 2007+ conference */
8744 sipe_conf_add(sip
, buddy
->name
);
8746 else /* 2005- multiparty chat */
8748 gchar
*self
= sip_uri_self(sip
);
8749 struct sip_session
*session
;
8751 session
= sipe_session_add_chat(sip
);
8752 session
->chat_title
= sipe_chat_get_name(session
->callid
);
8753 session
->roster_manager
= g_strdup(self
);
8755 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, session
->chat_title
);
8756 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
8757 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
8758 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, FALSE
);
8765 sipe_is_election_finished(struct sip_session
*session
)
8767 gboolean res
= TRUE
;
8769 SIPE_DIALOG_FOREACH
{
8770 if (dialog
->election_vote
== 0) {
8774 } SIPE_DIALOG_FOREACH_END
;
8777 session
->is_voting_in_progress
= FALSE
;
8783 sipe_election_start(struct sipe_account_data
*sip
,
8784 struct sip_session
*session
)
8786 int election_timeout
;
8788 if (session
->is_voting_in_progress
) {
8789 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
8792 session
->is_voting_in_progress
= TRUE
;
8794 session
->bid
= rand();
8796 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
8798 SIPE_DIALOG_FOREACH
{
8799 /* reset election_vote for each chat participant */
8800 dialog
->election_vote
= 0;
8802 /* send RequestRM to each chat participant*/
8803 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
8804 } SIPE_DIALOG_FOREACH_END
;
8806 election_timeout
= 15; /* sec */
8807 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
8811 * @param who a URI to whom to invite to chat
8814 sipe_invite_to_chat(struct sipe_account_data
*sip
,
8815 struct sip_session
*session
,
8819 if (session
->focus_uri
)
8821 sipe_invite_conf(sip
, session
, who
);
8823 else /* a multi-party chat */
8825 gchar
*self
= sip_uri_self(sip
);
8826 if (session
->roster_manager
) {
8827 if (!strcmp(session
->roster_manager
, self
)) {
8828 sipe_invite(sip
, session
, who
, NULL
, NULL
, FALSE
);
8830 sipe_refer(sip
, session
, who
);
8833 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite: no RM available\n");
8835 session
->pending_invite_queue
= slist_insert_unique_sorted(
8836 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
8838 sipe_election_start(sip
, session
);
8845 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
8846 struct sip_session
*session
)
8849 GSList
*entry
= session
->pending_invite_queue
;
8852 invitee
= entry
->data
;
8853 sipe_invite_to_chat(sip
, session
, invitee
);
8854 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
8860 sipe_election_result(struct sipe_account_data
*sip
,
8863 struct sip_session
*session
= (struct sip_session
*)sess
;
8865 gboolean has_won
= TRUE
;
8867 if (session
->roster_manager
) {
8868 purple_debug_info("sipe",
8869 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
8873 session
->is_voting_in_progress
= FALSE
;
8875 SIPE_DIALOG_FOREACH
{
8876 if (dialog
->election_vote
< 0) {
8878 rival
= dialog
->with
;
8881 } SIPE_DIALOG_FOREACH_END
;
8884 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
8886 session
->roster_manager
= sip_uri_self(sip
);
8888 SIPE_DIALOG_FOREACH
{
8889 /* send SetRM to each chat participant*/
8890 sipe_send_election_set_rm(sip
, dialog
);
8891 } SIPE_DIALOG_FOREACH_END
;
8893 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
8897 sipe_process_pending_invite_queue(sip
, session
);
8901 * For 2007+ conference only.
8904 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_title
)
8906 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
8907 struct sip_session
*session
;
8909 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
8910 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_title=%s\n", chat_title
);
8912 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
8914 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
8918 * For 2007+ conference only.
8921 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_title
)
8923 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
8924 struct sip_session
*session
;
8926 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: buddy->name=%s\n", buddy
->name
);
8927 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: chat_title=%s\n", chat_title
);
8929 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
8931 sipe_conf_delete_user(sip
, session
, buddy
->name
);
8935 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, char *chat_title
)
8937 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
8938 struct sip_session
*session
;
8940 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: buddy->name=%s\n", buddy
->name
);
8941 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: chat_title=%s\n", chat_title
);
8943 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
8945 sipe_invite_to_chat(sip
, session
, buddy
->name
);
8949 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
8951 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
8953 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: buddy->name=%s\n", buddy
->name
);
8955 char *tel_uri
= sip_to_tel_uri(phone
);
8957 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: going to call number: %s\n", tel_uri
? tel_uri
: "");
8958 sip_csta_make_call(sip
, tel_uri
);
8965 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
8968 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
8970 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
8973 char *mailto
= g_strdup_printf("mailto:%s", email
);
8974 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
8978 char *const parmList
[] = {"xdg-email", mailto
, NULL
};
8979 if ((pid
= fork()) == -1)
8981 purple_debug_info("sipe", "fork() error\n");
8985 execvp(parmList
[0], parmList
);
8986 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
8994 //@TODO resolve env variable %WINDIR% first
8995 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
8998 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
9007 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
9012 * A menu which appear when right-clicking on buddy in contact list.
9015 sipe_buddy_menu(PurpleBuddy
*buddy
)
9017 PurpleBlistNode
*g_node
;
9018 PurpleGroup
*group
, *gr_parent
;
9019 PurpleMenuAction
*act
;
9021 GList
*menu_groups
= NULL
;
9022 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9025 const char *phone_disp_str
;
9026 gchar
*self
= sip_uri_self(sip
);
9028 SIPE_SESSION_FOREACH
{
9029 if (g_ascii_strcasecmp(self
, buddy
->name
) && session
->chat_title
&& session
->conv
)
9031 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
9033 PurpleConvChatBuddyFlags flags
;
9034 PurpleConvChatBuddyFlags flags_us
;
9036 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
9037 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9038 if (session
->focus_uri
9039 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
9040 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9042 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"), session
->chat_title
);
9043 act
= purple_menu_action_new(label
,
9044 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
9045 session
->chat_title
, NULL
);
9047 menu
= g_list_prepend(menu
, act
);
9050 if (session
->focus_uri
9051 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9053 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_title
);
9054 act
= purple_menu_action_new(label
,
9055 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
9056 session
->chat_title
, NULL
);
9058 menu
= g_list_prepend(menu
, act
);
9063 if (!session
->focus_uri
9064 || (session
->focus_uri
&& !session
->locked
))
9066 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_title
);
9067 act
= purple_menu_action_new(label
,
9068 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
9069 session
->chat_title
, NULL
);
9071 menu
= g_list_prepend(menu
, act
);
9075 } SIPE_SESSION_FOREACH_END
;
9077 act
= purple_menu_action_new(_("New chat"),
9078 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
9080 menu
= g_list_prepend(menu
, act
);
9082 if (sip
->csta
&& !sip
->csta
->line_status
) {
9085 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
9086 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
9088 gchar
*label
= g_strdup_printf(_("Work %s"),
9089 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9090 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9094 menu
= g_list_prepend(menu
, act
);
9098 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
9099 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
9101 gchar
*label
= g_strdup_printf(_("Mobile %s"),
9102 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9103 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9107 menu
= g_list_prepend(menu
, act
);
9111 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
9112 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
9114 gchar
*label
= g_strdup_printf(_("Home %s"),
9115 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9116 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9120 menu
= g_list_prepend(menu
, act
);
9124 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
9125 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
9127 gchar
*label
= g_strdup_printf(_("Other %s"),
9128 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9129 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9133 menu
= g_list_prepend(menu
, act
);
9137 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
9138 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
9140 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
9141 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9142 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9146 menu
= g_list_prepend(menu
, act
);
9150 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9152 act
= purple_menu_action_new(_("Send email..."),
9153 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
9155 menu
= g_list_prepend(menu
, act
);
9158 gr_parent
= purple_buddy_get_group(buddy
);
9159 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
9160 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
9163 group
= (PurpleGroup
*)g_node
;
9164 if (group
== gr_parent
)
9167 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
9170 act
= purple_menu_action_new(purple_group_get_name(group
),
9171 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
9173 menu_groups
= g_list_prepend(menu_groups
, act
);
9175 menu_groups
= g_list_reverse(menu_groups
);
9177 act
= purple_menu_action_new(_("Copy to"),
9180 menu
= g_list_prepend(menu
, act
);
9181 menu
= g_list_reverse(menu
);
9188 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
9190 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9191 struct sip_session
*session
;
9193 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9194 sipe_conf_modify_conference_lock(sip
, session
, locked
);
9198 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
9200 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
9201 sipe_conf_modify_lock(chat
, FALSE
);
9205 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
9207 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
9208 sipe_conf_modify_lock(chat
, TRUE
);
9212 sipe_chat_menu(PurpleChat
*chat
)
9214 PurpleMenuAction
*act
;
9215 PurpleConvChatBuddyFlags flags_us
;
9217 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9218 struct sip_session
*session
;
9221 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9222 if (!session
) return NULL
;
9224 self
= sip_uri_self(sip
);
9225 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9227 if (session
->focus_uri
9228 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9230 if (session
->locked
) {
9231 act
= purple_menu_action_new(_("Unlock"),
9232 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
9234 menu
= g_list_prepend(menu
, act
);
9236 act
= purple_menu_action_new(_("Lock"),
9237 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
9239 menu
= g_list_prepend(menu
, act
);
9243 menu
= g_list_reverse(menu
);
9250 sipe_blist_node_menu(PurpleBlistNode
*node
)
9252 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
9253 return sipe_buddy_menu((PurpleBuddy
*) node
);
9254 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
9255 return sipe_chat_menu((PurpleChat
*)node
);
9262 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
9264 char *uri
= trans
->payload
->data
;
9266 PurpleNotifyUserInfo
*info
;
9267 PurpleBuddy
*pbuddy
= NULL
;
9268 struct sipe_buddy
*sbuddy
;
9269 const char *alias
= NULL
;
9270 char *device_name
= NULL
;
9271 char *server_alias
= NULL
;
9272 char *phone_number
= NULL
;
9276 if (!sip
) return FALSE
;
9278 purple_debug_info("sipe", "Fetching %s's user info for %s\n", uri
, sip
->username
);
9280 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
9281 alias
= purple_buddy_get_local_alias(pbuddy
);
9283 //will query buddy UA's capabilities and send answer to log
9284 sipe_options_request(sip
, uri
);
9286 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
9288 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
9291 info
= purple_notify_user_info_new();
9293 if (msg
->response
!= 200) {
9294 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
9296 xmlnode
*searchResults
;
9299 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
9300 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
9301 if (!searchResults
) {
9302 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
9303 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
9305 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
9306 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
9307 phone_number
= g_strdup(xmlnode_get_attrib(mrow
, "phone"));
9309 /* For 2007 system we will take this from ContactCard -
9310 * it has cleaner tel: URIs at least
9312 if (!sip
->ocs2007
) {
9313 char *tel_uri
= sip_to_tel_uri(phone_number
);
9314 /* trims its parameters, so call first */
9315 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, server_alias
);
9316 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
9317 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
9318 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, phone_number
);
9322 if (server_alias
&& strlen(server_alias
) > 0) {
9323 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9325 if ((value
= xmlnode_get_attrib(mrow
, "title")) && strlen(value
) > 0) {
9326 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
9328 if ((value
= xmlnode_get_attrib(mrow
, "office")) && strlen(value
) > 0) {
9329 purple_notify_user_info_add_pair(info
, _("Office"), value
);
9331 if (phone_number
&& strlen(phone_number
) > 0) {
9332 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
9334 if ((value
= xmlnode_get_attrib(mrow
, "company")) && strlen(value
) > 0) {
9335 purple_notify_user_info_add_pair(info
, _("Company"), value
);
9337 if ((value
= xmlnode_get_attrib(mrow
, "city")) && strlen(value
) > 0) {
9338 purple_notify_user_info_add_pair(info
, _("City"), value
);
9340 if ((value
= xmlnode_get_attrib(mrow
, "state")) && strlen(value
) > 0) {
9341 purple_notify_user_info_add_pair(info
, _("State"), value
);
9343 if ((value
= xmlnode_get_attrib(mrow
, "country")) && strlen(value
) > 0) {
9344 purple_notify_user_info_add_pair(info
, _("Country"), value
);
9346 if (email
&& strlen(email
) > 0) {
9347 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9351 xmlnode_free(searchResults
);
9354 purple_notify_user_info_add_section_break(info
);
9356 if (!server_alias
|| !strcmp("", server_alias
)) {
9357 g_free(server_alias
);
9358 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
9360 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9364 /* present alias if it differs from server alias */
9365 if (alias
&& (!server_alias
|| strcmp(alias
, server_alias
)))
9367 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
9370 if (!email
|| !strcmp("", email
)) {
9372 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
9374 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9378 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
9380 purple_notify_user_info_add_pair(info
, _("Site"), site
);
9384 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
9387 /* show a buddy's user info in a nice dialog box */
9388 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
9389 uri
, /* buddy's URI */
9391 NULL
, /* callback called when dialog closed */
9392 NULL
); /* userdata for callback */
9394 g_free(phone_number
);
9395 g_free(server_alias
);
9397 g_free(device_name
);
9403 * AD search first, LDAP based
9405 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
9407 struct sipe_account_data
*sip
= gc
->proto_data
;
9408 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
9409 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
9410 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
9411 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
9413 payload
->destroy
= g_free
;
9414 payload
->data
= g_strdup(username
);
9416 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
9417 send_soap_request_with_cb(sip
, domain_uri
, body
,
9418 (TransCallback
) process_get_info_response
, payload
);
9424 static PurplePlugin
*my_protocol
= NULL
;
9426 static PurplePluginProtocolInfo prpl_info
=
9428 OPT_PROTO_CHAT_TOPIC
,
9429 NULL
, /* user_splits */
9430 NULL
, /* protocol_options */
9431 NO_BUDDY_ICONS
, /* icon_spec */
9432 sipe_list_icon
, /* list_icon */
9433 NULL
, /* list_emblems */
9434 sipe_status_text
, /* status_text */
9435 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
9436 sipe_status_types
, /* away_states */
9437 sipe_blist_node_menu
, /* blist_node_menu */
9438 NULL
, /* chat_info */
9439 NULL
, /* chat_info_defaults */
9440 sipe_login
, /* login */
9441 sipe_close
, /* close */
9442 sipe_im_send
, /* send_im */
9443 NULL
, /* set_info */ // TODO maybe
9444 sipe_send_typing
, /* send_typing */
9445 sipe_get_info
, /* get_info */
9446 sipe_set_status
, /* set_status */
9447 sipe_set_idle
, /* set_idle */
9448 NULL
, /* change_passwd */
9449 sipe_add_buddy
, /* add_buddy */
9450 NULL
, /* add_buddies */
9451 sipe_remove_buddy
, /* remove_buddy */
9452 NULL
, /* remove_buddies */
9453 sipe_add_permit
, /* add_permit */
9454 sipe_add_deny
, /* add_deny */
9455 sipe_add_deny
, /* rem_permit */
9456 sipe_add_permit
, /* rem_deny */
9457 dummy_permit_deny
, /* set_permit_deny */
9458 NULL
, /* join_chat */
9459 NULL
, /* reject_chat */
9460 NULL
, /* get_chat_name */
9461 sipe_chat_invite
, /* chat_invite */
9462 sipe_chat_leave
, /* chat_leave */
9463 NULL
, /* chat_whisper */
9464 sipe_chat_send
, /* chat_send */
9465 sipe_keep_alive
, /* keepalive */
9466 NULL
, /* register_user */
9467 NULL
, /* get_cb_info */ // deprecated
9468 NULL
, /* get_cb_away */ // deprecated
9469 sipe_alias_buddy
, /* alias_buddy */
9470 sipe_group_buddy
, /* group_buddy */
9471 sipe_rename_group
, /* rename_group */
9472 NULL
, /* buddy_free */
9473 sipe_convo_closed
, /* convo_closed */
9474 purple_normalize_nocase
, /* normalize */
9475 NULL
, /* set_buddy_icon */
9476 sipe_remove_group
, /* remove_group */
9477 NULL
, /* get_cb_real_name */ // TODO?
9478 NULL
, /* set_chat_topic */
9479 NULL
, /* find_blist_chat */
9480 NULL
, /* roomlist_get_list */
9481 NULL
, /* roomlist_cancel */
9482 NULL
, /* roomlist_expand_category */
9483 NULL
, /* can_receive_file */
9484 NULL
, /* send_file */
9485 NULL
, /* new_xfer */
9486 NULL
, /* offline_message */
9487 NULL
, /* whiteboard_prpl_ops */
9488 sipe_send_raw
, /* send_raw */
9489 NULL
, /* roomlist_room_serialize */
9490 NULL
, /* unregister_user */
9491 NULL
, /* send_attention */
9492 NULL
, /* get_attention_types */
9493 #if !PURPLE_VERSION_CHECK(2,5,0)
9494 /* Backward compatibility when compiling against 2.4.x API */
9495 (void (*)(void)) /* _purple_reserved4 */
9497 sizeof(PurplePluginProtocolInfo
), /* struct_size */
9498 #if PURPLE_VERSION_CHECK(2,5,0)
9499 sipe_get_account_text_table
, /* get_account_text_table */
9500 #if PURPLE_VERSION_CHECK(2,6,0)
9501 NULL
, /* initiate_media */
9502 NULL
, /* get_media_caps */
9508 static PurplePluginInfo info
= {
9509 PURPLE_PLUGIN_MAGIC
,
9510 PURPLE_MAJOR_VERSION
,
9511 PURPLE_MINOR_VERSION
,
9512 PURPLE_PLUGIN_PROTOCOL
, /**< type */
9513 NULL
, /**< ui_requirement */
9515 NULL
, /**< dependencies */
9516 PURPLE_PRIORITY_DEFAULT
, /**< priority */
9517 "prpl-sipe", /**< id */
9518 "Office Communicator", /**< name */
9519 SIPE_VERSION
, /**< version */
9520 "Microsoft Office Communicator Protocol Plugin", /**< summary */
9521 "A plugin for the extended SIP/SIMPLE protocol used by " /**< description */
9522 "Microsoft Live/Office Communications Server (LCS2005/OCS2007+)", /**< description */
9523 "Anibal Avelar <avelar@gmail.com>, " /**< author */
9524 "Gabriel Burt <gburt@novell.com>, " /**< author */
9525 "Stefan Becker <stefan.becker@nokia.com>, " /**< author */
9526 "pier11 <pier11@operamail.com>", /**< author */
9527 "http://sipe.sourceforge.net/", /**< homepage */
9528 sipe_plugin_load
, /**< load */
9529 sipe_plugin_unload
, /**< unload */
9530 sipe_plugin_destroy
, /**< destroy */
9531 NULL
, /**< ui_info */
9532 &prpl_info
, /**< extra_info */
9541 static void sipe_plugin_destroy(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9545 entry
= prpl_info
.protocol_options
;
9547 purple_account_option_destroy(entry
->data
);
9548 entry
= g_list_delete_link(entry
, entry
);
9550 prpl_info
.protocol_options
= NULL
;
9552 entry
= prpl_info
.user_splits
;
9554 purple_account_user_split_destroy(entry
->data
);
9555 entry
= g_list_delete_link(entry
, entry
);
9557 prpl_info
.user_splits
= NULL
;
9560 static void init_plugin(PurplePlugin
*plugin
)
9562 PurpleAccountUserSplit
*split
;
9563 PurpleAccountOption
*option
;
9568 purple_debug_info(PACKAGE
, "bindtextdomain = %s\n", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
9569 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s\n",
9570 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
9571 textdomain(GETTEXT_PACKAGE
);
9574 purple_plugin_register(plugin
);
9576 split
= purple_account_user_split_new(_("Login\n user or DOMAIN\\user or\n user@company.com"), NULL
, ',');
9577 purple_account_user_split_set_reverse(split
, FALSE
);
9578 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
9580 option
= purple_account_option_string_new(_("Server[:Port]\n(leave empty for auto-discovery)"), "server", "");
9581 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9583 option
= purple_account_option_list_new(_("Connection type"), "transport", NULL
);
9584 purple_account_option_add_list_item(option
, _("Auto"), "auto");
9585 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
9586 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
9587 purple_account_option_add_list_item(option
, _("UDP"), "udp");
9588 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9590 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
9591 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
9593 option
= purple_account_option_string_new(_("User Agent"), "useragent", "");
9594 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9597 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
9598 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9600 /* Suitable for sspi/NTLM, sspi/Kerberos and krb5 security mechanisms
9601 * No login/password is taken into account if this option present,
9602 * instead used default credentials stored in OS.
9604 option
= purple_account_option_bool_new(_("Use Single Sign-On"), "sso", TRUE
);
9605 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9608 option
= purple_account_option_list_new(_("Calendar source"), "calendar", NULL
);
9609 purple_account_option_add_list_item(option
, _("Exchange 2007/2010"), "EXCH");
9610 purple_account_option_add_list_item(option
, _("None"), "NONE");
9611 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9613 /** Example: https://server.company.com/EWS/Exchange.asmx */
9614 option
= purple_account_option_string_new(_("Email services URL\n(leave empty for auto-discovery)"), "email_url", "");
9615 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9617 option
= purple_account_option_string_new(_("Email address\n(if different from Username)"), "email", "");
9618 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9620 /** Example: DOMAIN\user or user@company.com */
9621 option
= purple_account_option_string_new(_("Email login\n(if different from Login)"), "email_login", "");
9622 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9624 option
= purple_account_option_string_new(_("Email password\n(if different from Password)"), "email_password", "");
9625 purple_account_option_set_masked(option
, TRUE
);
9626 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9628 my_protocol
= plugin
;
9631 PURPLE_INIT_PLUGIN(sipe
, init_plugin
, info
);