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 SIPE_IDLE_SET_DELAY 1 /* 1 sec */
97 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
98 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
100 /* Keep in sync with sipe_transport_type! */
101 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
102 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
104 /* Status identifiers (see also: sipe_status_types()) */
105 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
106 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
107 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
108 /* PURPLE_STATUS_UNAVAILABLE: */
109 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
110 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
111 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
112 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
113 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
114 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
115 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
116 /* PURPLE_STATUS_AWAY: */
117 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
118 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
119 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
120 /** Reuters status (user settable) */
121 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
122 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
123 /* ??? PURPLE_STATUS_MOBILE */
124 /* ??? PURPLE_STATUS_TUNE */
126 /* Status attributes (see also sipe_status_types() */
127 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
129 static struct sipe_activity_map_struct
134 const char *status_id
;
136 } const sipe_activity_map
[] =
138 /* This has nothing to do with Availability numbers, like 3500 (online).
139 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
141 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
142 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
143 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , NULL
},
144 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
145 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("Busy-Idle") , NULL
},
146 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
147 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
148 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
149 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , NULL
},
150 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
151 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , NULL
},
152 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , NULL
},
153 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , NULL
},
154 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
155 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
157 /** @param x is sipe_activity */
158 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
161 /* Action name templates */
162 #define ACTION_NAME_PRESENCE "<presence><%s>"
165 sipe_get_activity_by_token(const char *token
)
169 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
171 if (!strcmp(token
, sipe_activity_map
[i
].token
))
172 return sipe_activity_map
[i
].type
;
175 return sipe_activity_map
[0].type
;
179 sipe_get_activity_desc_by_token(const char *token
)
181 if (!token
) return NULL
;
183 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token
));
186 /** Allows to send typed messages from chat window again after account reinstantiation. */
188 sipe_rejoin_chat(PurpleConversation
*conv
)
190 if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
&&
191 PURPLE_CONV_CHAT(conv
)->left
)
193 PURPLE_CONV_CHAT(conv
)->left
= FALSE
;
194 purple_conversation_update(conv
, PURPLE_CONV_UPDATE_CHATLEFT
);
198 static char *genbranch()
200 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
201 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
202 rand() & 0xFFFF, rand() & 0xFFFF);
206 static char *default_ua
= NULL
;
208 sipe_get_useragent(struct sipe_account_data
*sip
)
210 const char *useragent
= purple_account_get_string(sip
->account
, "useragent", "");
211 if (is_empty(useragent
)) {
213 /*@TODO: better approach to define _user_ OS, it's version and host architecture */
215 #if defined(__linux__) || defined(__linux) || defined(__LINUX__)
216 #define SIPE_TARGET_PLATFORM "linux"
217 #elif defined(__NetBSD__) ||defined( __OpenBSD__) || defined(__FreeBSD__)
218 #define SIPE_TARGET_PLATFORM "bsd"
219 # elif defined(__APPLE__) || defined(__MACOS__)
220 #define SIPE_TARGET_PLATFORM "macosx"
221 #elif defined(__solaris__) || defined(__sun)
222 #define SIPE_TARGET_PLATFORM "sun"
223 #elif defined(_WIN32)
224 #define SIPE_TARGET_PLATFORM "win"
226 #define SIPE_TARGET_PLATFORM "generic"
229 #if defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)
230 #define SIPE_TARGET_ARCH "x86_64"
231 #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386)
232 #define SIPE_TARGET_ARCH "i386"
233 #elif defined(__ppc64__)
234 #define SIPE_TARGET_ARCH "ppc64"
235 #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR)
236 #define SIPE_TARGET_ARCH "ppc"
238 #define SIPE_TARGET_ARCH "other"
241 default_ua
= g_strdup_printf("Purple/%s Sipe/%s (%s-%s; %s)",
242 purple_core_get_version(),
244 SIPE_TARGET_PLATFORM
,
246 sip
->server_version
? sip
->server_version
: "");
248 useragent
= default_ua
;
253 static const char *sipe_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount
*a
,
254 SIPE_UNUSED_PARAMETER PurpleBuddy
*b
)
259 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
261 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
);
263 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
264 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
267 static void sipe_close(PurpleConnection
*gc
);
269 static void send_presence_status(struct sipe_account_data
*sip
);
271 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
273 static void sipe_keep_alive(PurpleConnection
*gc
)
275 struct sipe_account_data
*sip
= gc
->proto_data
;
276 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
277 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
278 gchar buf
[2] = {0, 0};
279 purple_debug_info("sipe", "sending keep alive\n");
280 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
282 time_t now
= time(NULL
);
283 if ((sip
->keepalive_timeout
> 0) &&
284 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
285 #if PURPLE_VERSION_CHECK(2,4,0)
286 && ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
289 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
290 sendout_pkt(gc
, "\r\n\r\n");
291 sip
->last_keepalive
= now
;
296 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
298 struct sip_connection
*ret
= NULL
;
299 GSList
*entry
= sip
->openconns
;
302 if (ret
->fd
== fd
) return ret
;
308 static void sipe_auth_free(struct sip_auth
*auth
)
310 g_free(auth
->opaque
);
314 g_free(auth
->target
);
316 auth
->type
= AUTH_TYPE_UNSET
;
319 g_free(auth
->gssapi_data
);
320 auth
->gssapi_data
= NULL
;
321 sip_sec_destroy_context(auth
->gssapi_context
);
322 auth
->gssapi_context
= NULL
;
325 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
327 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
329 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
333 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
335 struct sip_connection
*conn
= connection_find(sip
, fd
);
337 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
338 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
344 static void connection_free_all(struct sipe_account_data
*sip
)
346 struct sip_connection
*ret
= NULL
;
347 GSList
*entry
= sip
->openconns
;
350 connection_remove(sip
, ret
->fd
);
351 entry
= sip
->openconns
;
355 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
358 const char *authuser
= sip
->authuser
;
362 if (!authuser
|| strlen(authuser
) < 1) {
363 authuser
= sip
->username
;
366 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
367 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
369 // If we have a signature for the message, include that
370 if (msg
->signature
) {
371 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
);
374 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
375 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
379 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
382 purple_account_get_bool(sip
->account
, "sso", TRUE
),
383 sip
->authdomain
? sip
->authdomain
: "",
388 if (!gssapi_data
|| !auth
->gssapi_context
) {
389 sip
->gc
->wants_to_die
= TRUE
;
390 purple_connection_error(sip
->gc
, _("Failed to authenticate to server"));
394 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
395 ret
= g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth_protocol
, opaque
, auth
->realm
, auth
->target
, gssapi_data
);
401 return g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth_protocol
, auth
->realm
, auth
->target
);
403 } else { /* Digest */
405 /* Calculate new session key */
407 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
408 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
409 authuser
, auth
->realm
, sip
->password
,
410 auth
->gssapi_data
, NULL
);
413 sprintf(noncecount
, "%08d", auth
->nc
++);
414 response
= purple_cipher_http_digest_calculate_response("md5",
415 msg
->method
, msg
->target
, NULL
, NULL
,
416 auth
->gssapi_data
, noncecount
, NULL
,
418 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
420 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
);
426 static char *parse_attribute(const char *attrname
, const char *source
)
428 const char *tmp
, *tmp2
;
430 int len
= strlen(attrname
);
432 if (!strncmp(source
, attrname
, len
)) {
434 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
436 retval
= g_strndup(tmp
, tmp2
- tmp
);
438 retval
= g_strdup(tmp
);
444 static void fill_auth(gchar
*hdr
, struct sip_auth
*auth
)
450 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
454 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
455 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
456 auth
->type
= AUTH_TYPE_NTLM
;
459 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
460 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
461 auth
->type
= AUTH_TYPE_KERBEROS
;
465 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
466 auth
->type
= AUTH_TYPE_DIGEST
;
470 parts
= g_strsplit(hdr
, "\", ", 0);
471 for (i
= 0; parts
[i
]; i
++) {
474 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
476 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
477 g_free(auth
->gssapi_data
);
478 auth
->gssapi_data
= tmp
;
480 if (auth
->type
== AUTH_TYPE_NTLM
) {
481 /* NTLM module extracts nonce from gssapi-data */
485 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
486 /* Only used with AUTH_TYPE_DIGEST */
487 g_free(auth
->gssapi_data
);
488 auth
->gssapi_data
= tmp
;
489 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
490 g_free(auth
->opaque
);
492 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
496 if (auth
->type
== AUTH_TYPE_DIGEST
) {
497 /* Throw away old session key */
498 g_free(auth
->opaque
);
503 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
504 g_free(auth
->target
);
513 static void sipe_canwrite_cb(gpointer data
,
514 SIPE_UNUSED_PARAMETER gint source
,
515 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
517 PurpleConnection
*gc
= data
;
518 struct sipe_account_data
*sip
= gc
->proto_data
;
522 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
524 if (max_write
== 0) {
525 if (sip
->tx_handler
!= 0){
526 purple_input_remove(sip
->tx_handler
);
532 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
534 if (written
< 0 && errno
== EAGAIN
)
536 else if (written
<= 0) {
537 /*TODO: do we really want to disconnect on a failure to write?*/
538 purple_connection_error(gc
, _("Could not write"));
542 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
545 static void sipe_canwrite_cb_ssl(gpointer data
,
546 SIPE_UNUSED_PARAMETER gint src
,
547 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
549 PurpleConnection
*gc
= data
;
550 struct sipe_account_data
*sip
= gc
->proto_data
;
554 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
556 if (max_write
== 0) {
557 if (sip
->tx_handler
!= 0) {
558 purple_input_remove(sip
->tx_handler
);
564 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
566 if (written
< 0 && errno
== EAGAIN
)
568 else if (written
<= 0) {
569 /*TODO: do we really want to disconnect on a failure to write?*/
570 purple_connection_error(gc
, _("Could not write"));
574 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
577 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
579 static void send_later_cb(gpointer data
, gint source
,
580 SIPE_UNUSED_PARAMETER
const gchar
*error
)
582 PurpleConnection
*gc
= data
;
583 struct sipe_account_data
*sip
;
584 struct sip_connection
*conn
;
586 if (!PURPLE_CONNECTION_IS_VALID(gc
))
594 purple_connection_error(gc
, _("Could not connect"));
598 sip
= gc
->proto_data
;
600 sip
->connecting
= FALSE
;
601 sip
->last_keepalive
= time(NULL
);
603 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
605 /* If there is more to write now, we need to register a handler */
606 if (sip
->txbuf
->bufused
> 0)
607 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
609 conn
= connection_create(sip
, source
);
610 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
613 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
615 struct sipe_account_data
*sip
;
617 if (!PURPLE_CONNECTION_IS_VALID(gc
))
619 if (gsc
) purple_ssl_close(gsc
);
623 sip
= gc
->proto_data
;
626 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
627 sip
->connecting
= FALSE
;
628 sip
->last_keepalive
= time(NULL
);
630 connection_create(sip
, gsc
->fd
);
632 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
637 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
638 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
640 PurpleConnection
*gc
= data
;
641 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
642 if (sip
== NULL
) return;
644 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
646 /* If there is more to write now */
647 if (sip
->txbuf
->bufused
> 0) {
648 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
653 static void sendlater(PurpleConnection
*gc
, const char *buf
)
655 struct sipe_account_data
*sip
= gc
->proto_data
;
657 if (!sip
->connecting
) {
658 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
659 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
660 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
662 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
663 purple_connection_error(gc
, _("Could not create socket"));
666 sip
->connecting
= TRUE
;
669 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
670 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
672 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
675 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
677 struct sipe_account_data
*sip
= gc
->proto_data
;
678 time_t currtime
= time(NULL
);
679 int writelen
= strlen(buf
);
682 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sending - %s######\n%s######\n", ctime(&currtime
), tmp
= fix_newlines(buf
));
684 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
685 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
686 purple_debug_info("sipe", "could not send packet\n");
695 if (sip
->tx_handler
) {
700 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
702 ret
= write(sip
->fd
, buf
, writelen
);
706 if (ret
< 0 && errno
== EAGAIN
)
708 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
713 if (ret
< writelen
) {
714 if (!sip
->tx_handler
){
716 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
719 sip
->tx_handler
= purple_input_add(sip
->fd
,
720 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
725 /* XXX: is it OK to do this? You might get part of a request sent
726 with part of another. */
727 if (sip
->txbuf
->bufused
> 0)
728 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
730 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
736 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
738 sendout_pkt(gc
, buf
);
742 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
744 GSList
*tmp
= msg
->headers
;
747 GString
*outstr
= g_string_new("");
748 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
750 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
751 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
752 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
753 tmp
= g_slist_next(tmp
);
755 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
756 sendout_pkt(sip
->gc
, outstr
->str
);
757 g_string_free(outstr
, TRUE
);
760 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
764 if (sip
->registrar
.type
== AUTH_TYPE_UNSET
) {
768 if (sip
->registrar
.gssapi_context
) {
769 struct sipmsg_breakdown msgbd
;
770 gchar
*signature_input_str
;
772 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
773 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
774 sip
->registrar
.ntlm_num
++;
775 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
776 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
777 if (signature_input_str
!= NULL
) {
778 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
779 msg
->signature
= signature_hex
;
780 msg
->rand
= g_strdup(msgbd
.rand
);
781 msg
->num
= g_strdup(msgbd
.num
);
782 g_free(signature_input_str
);
784 sipmsg_breakdown_free(&msgbd
);
787 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
788 buf
= auth_header(sip
, &sip
->registrar
, msg
);
790 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
793 } 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")) {
794 sip
->registrar
.nc
= 3;
796 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
798 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
801 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
806 buf
= auth_header(sip
, &sip
->registrar
, msg
);
807 sipmsg_add_header_now_pos(msg
, "Proxy-Authorization", buf
, 5);
810 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
814 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
815 const char *text
, const char *body
)
819 GString
*outstr
= g_string_new("");
820 struct sipe_account_data
*sip
= gc
->proto_data
;
823 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
825 contact
= get_contact(sip
);
826 sipmsg_add_header(msg
, "Contact", contact
);
831 sprintf(len
, "%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
832 sipmsg_add_header(msg
, "Content-Length", len
);
834 sipmsg_add_header(msg
, "Content-Length", "0");
837 msg
->response
= code
;
839 sipmsg_strip_headers(msg
, keepers
);
840 sipmsg_merge_new_headers(msg
);
841 sign_outgoing_message(msg
, sip
, msg
->method
);
843 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
846 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
847 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
849 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
850 tmp
= g_slist_next(tmp
);
852 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
853 sendout_pkt(gc
, outstr
->str
);
854 g_string_free(outstr
, TRUE
);
857 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
859 if (sip
->transactions
) {
860 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
861 purple_debug_info("sipe", "sip->transactions count:%d after removal\n", g_slist_length(sip
->transactions
));
863 if (trans
->msg
) sipmsg_free(trans
->msg
);
864 if (trans
->payload
) {
865 (*trans
->payload
->destroy
)(trans
->payload
->data
);
866 g_free(trans
->payload
);
873 static struct transaction
*
874 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
876 gchar
*call_id
= NULL
;
878 struct transaction
*trans
= g_new0(struct transaction
, 1);
880 trans
->time
= time(NULL
);
881 trans
->msg
= (struct sipmsg
*)msg
;
882 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
883 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
884 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
885 trans
->callback
= callback
;
886 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
887 purple_debug_info("sipe", "sip->transactions count:%d after addition\n", g_slist_length(sip
->transactions
));
891 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
893 struct transaction
*trans
;
894 GSList
*transactions
= sip
->transactions
;
895 gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
896 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
897 gchar
*key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
899 while (transactions
) {
900 trans
= transactions
->data
;
901 if (!g_strcasecmp(trans
->key
, key
)) {
905 transactions
= transactions
->next
;
913 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
914 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
915 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
917 struct sipe_account_data
*sip
= gc
->proto_data
;
918 const char *addh
= "";
921 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
922 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
923 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
924 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
925 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
926 gchar
*route
= g_strdup("");
927 gchar
*epid
= get_epid(sip
);
928 int cseq
= dialog
? ++dialog
->cseq
: 1 /* as Call-Id is new in this case */;
929 struct transaction
*trans
= NULL
;
931 if (dialog
&& dialog
->routes
)
933 GSList
*iter
= dialog
->routes
;
938 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
940 iter
= g_slist_next(iter
);
944 if (!ourtag
&& !dialog
) {
948 if (!strcmp(method
, "REGISTER")) {
949 if (sip
->regcallid
) {
951 callid
= g_strdup(sip
->regcallid
);
953 sip
->regcallid
= g_strdup(callid
);
958 if (addheaders
) addh
= addheaders
;
960 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
961 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
962 "From: <sip:%s>%s%s;epid=%s\r\n"
963 "To: <%s>%s%s%s%s\r\n"
964 "Max-Forwards: 70\r\n"
969 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
971 dialog
&& dialog
->request
? dialog
->request
: url
,
972 TRANSPORT_DESCRIPTOR
,
973 purple_network_get_my_ip(-1),
975 branch
? ";branch=" : "",
976 branch
? branch
: "",
978 ourtag
? ";tag=" : "",
979 ourtag
? ourtag
: "",
982 theirtag
? ";tag=" : "",
983 theirtag
? theirtag
: "",
984 theirepid
? ";epid=" : "",
985 theirepid
? theirepid
: "",
988 sipe_get_useragent(sip
),
992 body
? (gsize
) strlen(body
) : 0,
996 //printf ("parsing msg buf:\n%s\n\n", buf);
997 msg
= sipmsg_parse_msg(buf
);
1008 sign_outgoing_message (msg
, sip
, method
);
1010 buf
= sipmsg_to_string (msg
);
1012 /* add to ongoing transactions */
1013 /* ACK isn't supposed to be answered ever. So we do not keep transaction for it. */
1014 if (strcmp(method
, "ACK")) {
1015 trans
= transactions_add_buf(sip
, msg
, tc
);
1019 sendout_pkt(gc
, buf
);
1026 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
1029 send_soap_request_with_cb(struct sipe_account_data
*sip
,
1032 TransCallback callback
,
1033 struct transaction_payload
*payload
)
1035 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sip
);
1036 gchar
*contact
= get_contact(sip
);
1037 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
1038 "Content-Type: application/SOAP+xml\r\n",contact
);
1040 struct transaction
*trans
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1041 trans
->payload
= payload
;
1048 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1050 send_soap_request_with_cb(sip
, NULL
, body
, NULL
, NULL
);
1053 static char *get_contact_register(struct sipe_account_data
*sip
)
1055 char *epid
= get_epid(sip
);
1056 char *uuid
= generateUUIDfromEPID(epid
);
1057 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
);
1063 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1071 if (!sip
->sipdomain
) return;
1073 uri
= sip_uri_from_name(sip
->sipdomain
);
1074 expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1075 to
= sip_uri_self(sip
);
1076 contact
= get_contact_register(sip
);
1077 hdr
= g_strdup_printf("Contact: %s\r\n"
1078 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1079 "Event: registration\r\n"
1080 "Allow-Events: presence\r\n"
1081 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1082 "%s", contact
, expires
);
1086 sip
->registerstatus
= 1;
1088 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1089 process_register_response
);
1096 static void do_register_cb(struct sipe_account_data
*sip
,
1097 SIPE_UNUSED_PARAMETER
void *unused
)
1099 do_register_exp(sip
, -1);
1100 sip
->reregister_set
= FALSE
;
1103 static void do_register(struct sipe_account_data
*sip
)
1105 do_register_exp(sip
, -1);
1109 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1111 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1112 send_soap_request(sip
, body
);
1117 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1120 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1122 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1125 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1129 void sipe_auth_user_cb(void * data
)
1131 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1134 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1139 void sipe_deny_user_cb(void * data
)
1141 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1144 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1149 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1151 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1152 sipe_contact_allow_deny(sip
, name
, TRUE
);
1156 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1158 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1159 sipe_contact_allow_deny(sip
, name
, FALSE
);
1163 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1165 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1166 sipe_contact_set_acl(sip, name, "");
1170 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1174 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1175 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1177 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1179 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1180 if (!watchers
) return;
1182 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1183 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1184 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1185 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1187 // TODO pull out optional displayName to pass as alias
1189 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1190 job
->who
= remote_user
;
1192 purple_account_request_authorization(
1206 xmlnode_free(watchers
);
1211 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1213 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1214 if (!purple_group
) {
1215 purple_group
= purple_group_new(group
->name
);
1216 purple_blist_add_group(purple_group
, NULL
);
1220 group
->purple_group
= purple_group
;
1221 sip
->groups
= g_slist_append(sip
->groups
, group
);
1222 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1224 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1228 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1230 struct sipe_group
*group
;
1236 entry
= sip
->groups
;
1238 group
= entry
->data
;
1239 if (group
->id
== id
) {
1242 entry
= entry
->next
;
1247 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1249 struct sipe_group
*group
;
1251 if (!sip
|| !name
) {
1255 entry
= sip
->groups
;
1257 group
= entry
->data
;
1258 if (!strcmp(group
->name
, name
)) {
1261 entry
= entry
->next
;
1267 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1270 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1271 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1272 send_soap_request(sip
, body
);
1274 g_free(group
->name
);
1275 group
->name
= g_strdup(name
);
1279 * Only appends if no such value already stored.
1282 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1283 GSList
* res
= list
;
1284 if (!g_slist_find_custom(list
, data
, func
)) {
1285 res
= g_slist_insert_sorted(list
, data
, func
);
1291 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1292 return group1
->id
- group2
->id
;
1296 * Returns string like "2 4 7 8" - group ids buddy belong to.
1299 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1302 //creating array from GList, converting int to gchar*
1303 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1304 GSList
*entry
= buddy
->groups
;
1306 struct sipe_group
* group
= entry
->data
;
1307 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1308 entry
= entry
->next
;
1312 res
= g_strjoinv(" ", ids_arr
);
1313 g_strfreev(ids_arr
);
1318 * Sends buddy update to server
1321 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1323 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1324 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1326 if (buddy
&& purple_buddy
) {
1327 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1329 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1330 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1332 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1333 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1335 send_soap_request(sip
, body
);
1341 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1343 if (msg
->response
== 200) {
1344 struct sipe_group
*group
;
1345 struct group_user_context
*ctx
= trans
->payload
->data
;
1349 struct sipe_buddy
*buddy
;
1351 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1356 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1362 group_id
= xmlnode_get_data(node
);
1368 group
= g_new0(struct sipe_group
, 1);
1369 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1371 group
->name
= g_strdup(ctx
->group_name
);
1373 sipe_group_add(sip
, group
);
1375 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1377 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1380 sipe_group_set_user(sip
, ctx
->user_name
);
1388 static void sipe_group_context_destroy(gpointer data
)
1390 struct group_user_context
*ctx
= data
;
1391 g_free(ctx
->group_name
);
1392 g_free(ctx
->user_name
);
1396 static void sipe_group_create (struct sipe_account_data
*sip
, const gchar
*name
, const gchar
* who
)
1398 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
1399 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
1401 ctx
->group_name
= g_strdup(name
);
1402 ctx
->user_name
= g_strdup(who
);
1403 payload
->destroy
= sipe_group_context_destroy
;
1404 payload
->data
= ctx
;
1406 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1407 send_soap_request_with_cb(sip
, NULL
, body
, process_add_group_response
, payload
);
1412 * Data structure for scheduled actions
1415 struct scheduled_action
{
1418 * Format is <Event>[<Data>...]
1419 * Example: <presence><sip:user@domain.com> or <registration>
1422 guint timeout_handler
;
1423 gboolean repetitive
;
1425 GDestroyNotify destroy
;
1426 struct sipe_account_data
*sip
;
1432 * Should return FALSE if repetitive action is not needed
1434 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1437 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1438 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1439 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1440 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1441 ret
= sched_action
->repetitive
;
1442 if (sched_action
->destroy
) {
1443 (*sched_action
->destroy
)(sched_action
->payload
);
1445 g_free(sched_action
->name
);
1446 g_free(sched_action
);
1451 * Kills action timer effectively cancelling
1454 * @param name of action
1456 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1460 if (!sip
->timeouts
|| !name
) return;
1462 entry
= sip
->timeouts
;
1464 struct scheduled_action
*sched_action
= entry
->data
;
1465 if(!strcmp(sched_action
->name
, name
)) {
1466 GSList
*to_delete
= entry
;
1467 entry
= entry
->next
;
1468 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1469 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1470 purple_timeout_remove(sched_action
->timeout_handler
);
1471 if (sched_action
->destroy
) {
1472 (*sched_action
->destroy
)(sched_action
->payload
);
1474 g_free(sched_action
->name
);
1475 g_free(sched_action
);
1477 entry
= entry
->next
;
1483 sipe_schedule_action0(const gchar
*name
,
1487 GDestroyNotify destroy
,
1488 struct sipe_account_data
*sip
,
1491 struct scheduled_action
*sched_action
;
1493 /* Make sure each action only exists once */
1494 sipe_cancel_scheduled_action(sip
, name
);
1496 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1497 sched_action
= g_new0(struct scheduled_action
, 1);
1498 sched_action
->repetitive
= FALSE
;
1499 sched_action
->name
= g_strdup(name
);
1500 sched_action
->action
= action
;
1501 sched_action
->destroy
= destroy
;
1502 sched_action
->sip
= sip
;
1503 sched_action
->payload
= payload
;
1504 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1505 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1506 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1507 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1511 sipe_schedule_action(const gchar
*name
,
1514 GDestroyNotify destroy
,
1515 struct sipe_account_data
*sip
,
1518 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1522 * Same as sipe_schedule_action() but timeout is in milliseconds.
1525 sipe_schedule_action_msec(const gchar
*name
,
1528 GDestroyNotify destroy
,
1529 struct sipe_account_data
*sip
,
1532 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1536 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1537 time_t calculate_from
);
1540 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
);
1543 sipe_get_status_by_availability(int avail
,
1547 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
1548 const char *status_id
,
1549 const char *message
,
1550 time_t do_not_publish
[]);
1553 sipe_apply_calendar_status(struct sipe_account_data
*sip
,
1554 struct sipe_buddy
*sbuddy
,
1555 const char *status_id
)
1557 time_t cal_avail_since
;
1558 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
1560 gchar
*self_uri
= sip_uri_self(sip
);
1562 if (!sbuddy
) return;
1564 if (cal_status
< SIPE_CAL_NO_DATA
) {
1565 purple_debug_info("sipe", "update_calendar_status_cb: cal_status : %d for %s\n", cal_status
, sbuddy
->name
);
1566 purple_debug_info("sipe", "update_calendar_status_cb: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
1569 /* scheduled Cal update call */
1571 status_id
= sbuddy
->last_non_cal_status_id
;
1572 g_free(sbuddy
->activity
);
1573 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
1576 /* adjust to calendar status */
1577 if (cal_status
!= SIPE_CAL_NO_DATA
) {
1578 purple_debug_info("sipe", "update_calendar_status_cb: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
1580 if (cal_status
== SIPE_CAL_BUSY
1581 && cal_avail_since
> sbuddy
->user_avail_since
1582 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
1584 status_id
= SIPE_STATUS_ID_BUSY
;
1585 g_free(sbuddy
->activity
);
1586 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
));
1588 avail
= sipe_get_availability_by_status(status_id
, NULL
);
1590 purple_debug_info("sipe", "update_calendar_status_cb: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
1591 if (cal_avail_since
> sbuddy
->activity_since
) {
1592 if (cal_status
== SIPE_CAL_OOF
1593 && avail
>= 15000) /* 12000 in 2007 */
1595 g_free(sbuddy
->activity
);
1596 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
1601 /* then set status_id actually */
1602 purple_debug_info("sipe", "sipe_got_user_status: to %s for %s\n", status_id
, sbuddy
->name
);
1603 purple_prpl_got_user_status(sip
->account
, sbuddy
->name
, status_id
, NULL
);
1605 /* set our account state to the one in roaming (including calendar info) */
1606 if (sip
->initial_state_published
&& !strcmp(sbuddy
->name
, self_uri
)) {
1607 if (!strcmp(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
1608 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
1611 purple_debug_info("sipe", "sipe_got_user_status: switch to '%s' for the account\n", sip
->status
);
1612 sipe_set_purple_account_status_and_note(sip
->account
, status_id
, sip
->note
, sip
->do_not_publish
);
1618 sipe_got_user_status(struct sipe_account_data
*sip
,
1620 const char *status_id
)
1622 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
1624 if (!sbuddy
) return;
1626 /* Check if on 2005 system contact's calendar,
1627 * then set/preserve it.
1629 if (!sip
->ocs2007
) {
1630 sipe_apply_calendar_status(sip
, sbuddy
, status_id
);
1632 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
1637 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
1638 struct sipe_buddy
*sbuddy
,
1639 struct sipe_account_data
*sip
)
1641 sipe_apply_calendar_status(sip
, sbuddy
, NULL
);
1645 * Updates contact's status
1646 * based on their calendar information.
1648 * Applicability: 2005 systems
1651 update_calendar_status(struct sipe_account_data
*sip
)
1653 purple_debug_info("sipe", "update_calendar_status() started.\n");
1654 g_hash_table_foreach(sip
->buddies
, (GHFunc
)update_calendar_status_cb
, (gpointer
)sip
);
1656 /* repeat scheduling */
1657 sipe_sched_calendar_status_update(sip
, time(NULL
) + 3*60 /* 3 min */);
1661 * Schedules process of contacts' status update
1662 * based on their calendar information.
1663 * Should be scheduled to the beginning of every
1664 * 15 min interval, like:
1665 * 13:00, 13:15, 13:30, 13:45, etc.
1667 * Applicability: 2005 systems
1670 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1671 time_t calculate_from
)
1673 int interval
= 15*60;
1674 /** start of the beginning of closest 15 min interval. */
1675 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1677 purple_debug_info("sipe", "sipe_sched_calendar_status_update: calculate_from time: %s",
1678 asctime(localtime(&calculate_from
)));
1679 purple_debug_info("sipe", "sipe_sched_calendar_status_update: next start time : %s",
1680 asctime(localtime(&next_start
)));
1682 sipe_schedule_action("<+2005-cal-status>",
1683 (int)(next_start
- time(NULL
)),
1684 (Action
)update_calendar_status
,
1691 * Schedules process of self status publish
1692 * based on own calendar information.
1693 * Should be scheduled to the beginning of every
1694 * 15 min interval, like:
1695 * 13:00, 13:15, 13:30, 13:45, etc.
1697 * Applicability: 2007+ systems
1700 sipe_sched_calendar_status_self_publish(struct sipe_account_data
*sip
,
1701 time_t calculate_from
)
1703 int interval
= 5*60;
1704 /** start of the beginning of closest 5 min interval. */
1705 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1707 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: calculate_from time: %s",
1708 asctime(localtime(&calculate_from
)));
1709 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: next start time : %s",
1710 asctime(localtime(&next_start
)));
1712 sipe_schedule_action("<+2007-cal-status>",
1713 (int)(next_start
- time(NULL
)),
1714 (Action
)publish_calendar_status_self
,
1720 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1722 /** Should be g_free()'d
1725 sipe_get_subscription_key(gchar
*event
,
1730 if (is_empty(event
)) return NULL
;
1732 if (event
&& !g_ascii_strcasecmp(event
, "presence")) {
1733 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1734 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, with
);
1736 /* @TODO drop participated buddies' just_added flag */
1738 /* Subscription is identified by <event> key */
1739 key
= g_strdup_printf("<%s>", event
);
1745 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
1746 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
1748 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
1749 gchar
*event
= sipmsg_find_header(msg
, "Event");
1752 /* The case with 2005 Public IM Connectivity (PIC) - no Event header */
1754 struct sipmsg
*request_msg
= trans
->msg
;
1755 event
= sipmsg_find_header(request_msg
, "Event");
1758 key
= sipe_get_subscription_key(event
, with
);
1760 /* 200 OK; 481 Call Leg Does Not Exist */
1761 if (key
&& (msg
->response
== 200 || msg
->response
== 481)) {
1762 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
1763 g_hash_table_remove(sip
->subscriptions
, key
);
1764 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
1768 /* create/store subscription dialog if not yet */
1769 if (msg
->response
== 200) {
1770 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1771 gchar
*cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
1774 struct sip_subscription
*subscription
= g_new0(struct sip_subscription
, 1);
1775 g_hash_table_insert(sip
->subscriptions
, g_strdup(key
), subscription
);
1777 subscription
->dialog
.callid
= g_strdup(callid
);
1778 subscription
->dialog
.cseq
= atoi(cseq
);
1779 subscription
->dialog
.with
= g_strdup(with
);
1780 subscription
->event
= g_strdup(event
);
1781 sipe_dialog_parse(&subscription
->dialog
, msg
, TRUE
);
1783 purple_debug_info("sipe", "process_subscribe_response: subscription dialog added for: %s\n", key
);
1792 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1794 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1799 static void sipe_subscribe_resource_uri(const char *name
,
1800 SIPE_UNUSED_PARAMETER gpointer value
,
1801 gchar
**resources_uri
)
1803 gchar
*tmp
= *resources_uri
;
1804 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1808 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1810 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1811 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1812 gchar
*tmp
= *resources_uri
;
1814 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
1816 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
1821 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1822 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1823 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1824 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1825 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1828 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1831 gchar
*contact
= get_contact(sip
);
1834 gchar
*require
= "";
1836 gchar
*autoextend
= "";
1837 gchar
*content_type
;
1838 struct sip_dialog
*dialog
;
1841 require
= ", categoryList";
1842 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1843 content_type
= "application/msrtc-adrl-categorylist+xml";
1844 content
= g_strdup_printf(
1845 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1846 "<action name=\"subscribe\" id=\"63792024\">\n"
1847 "<adhocList>\n%s</adhocList>\n"
1848 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1849 "<category name=\"calendarData\"/>\n"
1850 "<category name=\"contactCard\"/>\n"
1851 "<category name=\"note\"/>\n"
1852 "<category name=\"state\"/>\n"
1855 "</batchSub>", sip
->username
, resources_uri
);
1857 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1858 content_type
= "application/adrl+xml";
1859 content
= g_strdup_printf(
1860 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1861 "<create xmlns=\"\">\n%s</create>\n"
1862 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1864 g_free(resources_uri
);
1866 request
= g_strdup_printf(
1867 "Require: adhoclist%s\r\n"
1868 "Supported: eventlist\r\n"
1869 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1870 "Supported: ms-piggyback-first-notify\r\n"
1871 "%sSupported: ms-benotify\r\n"
1872 "Proxy-Require: ms-benotify\r\n"
1873 "Event: presence\r\n"
1874 "Content-Type: %s\r\n"
1875 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1878 /* subscribe to buddy presence */
1879 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1880 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1881 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1882 purple_debug_info("sipe", "sipe_subscribe_presence_batched_to: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
1884 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
1892 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
,
1893 SIPE_UNUSED_PARAMETER
void *unused
)
1895 gchar
*to
= sip_uri_self(sip
);
1896 gchar
*resources_uri
= g_strdup("");
1898 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1900 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1903 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1906 struct presence_batched_routed
{
1911 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1913 struct presence_batched_routed
*data
= payload
;
1914 GSList
*buddies
= data
->buddies
;
1916 g_free(buddies
->data
);
1917 buddies
= buddies
->next
;
1919 g_slist_free(data
->buddies
);
1924 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
1926 struct presence_batched_routed
*data
= payload
;
1927 GSList
*buddies
= data
->buddies
;
1928 gchar
*resources_uri
= g_strdup("");
1930 gchar
*tmp
= resources_uri
;
1931 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
1933 buddies
= buddies
->next
;
1935 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
1936 g_strdup(data
->host
));
1940 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1941 * The user sends a single SUBSCRIBE request to the subscribed contact.
1942 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1946 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
1950 gchar
*to
= sip_uri((char *)buddy_name
);
1951 gchar
*tmp
= get_contact(sip
);
1953 gchar
*content
= NULL
;
1954 gchar
*autoextend
= "";
1955 gchar
*content_type
= "";
1956 struct sip_dialog
*dialog
;
1957 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, to
);
1958 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1960 if (sbuddy
) sbuddy
->just_added
= FALSE
;
1963 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
1965 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1968 request
= g_strdup_printf(
1969 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1970 "Supported: ms-piggyback-first-notify\r\n"
1971 "%s%sSupported: ms-benotify\r\n"
1972 "Proxy-Require: ms-benotify\r\n"
1973 "Event: presence\r\n"
1974 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
1977 content
= g_strdup_printf(
1978 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1979 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1980 "<resource uri=\"%s\"%s\n"
1982 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1983 "<category name=\"calendarData\"/>\n"
1984 "<category name=\"contactCard\"/>\n"
1985 "<category name=\"note\"/>\n"
1986 "<category name=\"state\"/>\n"
1989 "</batchSub>", sip
->username
, to
, context
);
1994 /* subscribe to buddy presence */
1995 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1996 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1997 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1998 purple_debug_info("sipe", "sipe_subscribe_presence_single: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2000 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
2008 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
2010 purple_debug_info("sipe", "sipe_set_status: status=%s\n", purple_status_get_id(status
));
2012 if (!purple_status_is_active(status
))
2016 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
2021 time_t now
= time(NULL
);
2022 const char *status_id
= purple_status_get_id(status
);
2023 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
2024 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
2025 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
2027 /* when other point of presence clears note, but we are keeping
2028 * state if OOF note.
2030 if (do_not_publish
&& !note
&& sip
->ews
&& sip
->ews
->oof_note
) {
2031 purple_debug_info("sipe", "sipe_set_status: enabling publication as OOF note keepers.\n");
2032 do_not_publish
= FALSE
;
2035 purple_debug_info("sipe", "sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d\n",
2036 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
2038 sip
->do_not_publish
[activity
] = 0;
2039 purple_debug_info("sipe", "sipe_set_status: set: sip->do_not_publish[%s]=%d [0]\n",
2040 status_id
, (int)sip
->do_not_publish
[activity
]);
2044 purple_debug_info("sipe", "sipe_set_status: publication was switched off, exiting.\n");
2048 g_free(sip
->status
);
2049 sip
->status
= g_strdup(status_id
);
2051 /* hack to escape apostrof before comparison */
2052 tmp
= note
? purple_strreplace(note
, "'", "'") : NULL
;
2054 /* this will preserve OOF flag as well */
2055 if (!(tmp
&& sip
->note
&& !strcmp(tmp
, sip
->note
))) {
2056 sip
->is_oof_note
= FALSE
;
2058 sip
->note
= g_strdup(note
);
2059 sip
->note_since
= time(NULL
);
2063 /* schedule 2 sec to capture idle flag */
2064 action_name
= g_strdup_printf("<%s>", "+set-status");
2065 sipe_schedule_action(action_name
, SIPE_IDLE_SET_DELAY
, (Action
)send_presence_status
, NULL
, sip
, NULL
);
2066 g_free(action_name
);
2071 sipe_set_idle(PurpleConnection
* gc
,
2074 purple_debug_info("sipe", "sipe_set_idle: interval=%d\n", interval
);
2077 struct sipe_account_data
*sip
= gc
->proto_data
;
2080 sip
->idle_switch
= time(NULL
);
2081 purple_debug_info("sipe", "sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
2087 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
,
2088 SIPE_UNUSED_PARAMETER
const char *alias
)
2090 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2091 sipe_group_set_user(sip
, name
);
2095 sipe_group_buddy(PurpleConnection
*gc
,
2097 const char *old_group_name
,
2098 const char *new_group_name
)
2100 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2101 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
2102 struct sipe_group
* old_group
= NULL
;
2103 struct sipe_group
* new_group
;
2105 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
2106 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
2108 if(!buddy
) { // buddy not in roaming list
2112 if (old_group_name
) {
2113 old_group
= sipe_group_find_by_name(sip
, old_group_name
);
2115 new_group
= sipe_group_find_by_name(sip
, new_group_name
);
2118 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
2119 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
2123 sipe_group_create(sip
, new_group_name
, who
);
2125 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
2126 sipe_group_set_user(sip
, who
);
2130 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2132 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2134 /* libpurple can call us with undefined buddy or group */
2135 if (buddy
&& group
) {
2136 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2138 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2139 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
2140 purple_blist_rename_buddy(buddy
, buddy_name
);
2143 /* Prepend sip: if needed */
2144 if (strncmp("sip:", buddy
->name
, 4)) {
2145 gchar
*buf
= sip_uri_from_name(buddy
->name
);
2146 purple_blist_rename_buddy(buddy
, buf
);
2150 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
2151 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
2152 purple_debug_info("sipe", "sipe_add_buddy: adding %s\n", buddy
->name
);
2153 b
->name
= g_strdup(buddy
->name
);
2154 b
->just_added
= TRUE
;
2155 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
2156 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
2157 /* @TODO should go to callback */
2158 sipe_subscribe_presence_single(sip
, b
->name
);
2160 purple_debug_info("sipe", "sipe_add_buddy: buddy %s already in internal list\n", buddy
->name
);
2165 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
2169 * We are calling g_hash_table_foreach_steal(). That means that no
2170 * key/value deallocation functions are called. Therefore the glib
2171 * hash code does not touch the key (buddy->name) or value (buddy)
2172 * of the to-be-deleted hash node at all. It follows that we
2174 * - MUST free the memory for the key ourselves and
2175 * - ARE allowed to do it in this function
2177 * Conclusion: glib must be broken on the Windows platform if sipe
2178 * crashes with SIGTRAP when closing. You'll have to live
2179 * with the memory leak until this is fixed.
2181 g_free(buddy
->name
);
2183 g_free(buddy
->activity
);
2184 g_free(buddy
->meeting_subject
);
2185 g_free(buddy
->meeting_location
);
2186 g_free(buddy
->note
);
2188 g_free(buddy
->cal_start_time
);
2189 g_free(buddy
->cal_free_busy_base64
);
2190 g_free(buddy
->cal_free_busy
);
2191 g_free(buddy
->last_non_cal_activity
);
2193 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
2195 g_free(buddy
->device_name
);
2196 g_slist_free(buddy
->groups
);
2201 * Unassociates buddy from group first.
2202 * Then see if no groups left, removes buddy completely.
2203 * Otherwise updates buddy groups on server.
2205 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2207 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2208 struct sipe_buddy
*b
;
2209 struct sipe_group
*g
= NULL
;
2211 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2214 b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
2218 g
= sipe_group_find_by_name(sip
, group
->name
);
2222 b
->groups
= g_slist_remove(b
->groups
, g
);
2223 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
2226 if (g_slist_length(b
->groups
) < 1) {
2227 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
2228 sipe_cancel_scheduled_action(sip
, action_name
);
2229 g_free(action_name
);
2231 g_hash_table_remove(sip
->buddies
, buddy
->name
);
2234 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
2235 send_soap_request(sip
, body
);
2241 //updates groups on server
2242 sipe_group_set_user(sip
, b
->name
);
2248 sipe_rename_group(PurpleConnection
*gc
,
2249 const char *old_name
,
2251 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
2253 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2254 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, old_name
);
2256 sipe_group_rename(sip
, s_group
, group
->name
);
2258 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
2263 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
2265 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2266 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
2269 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
2270 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
2271 send_soap_request(sip
, body
);
2274 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
2275 g_free(s_group
->name
);
2278 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
2282 /** All statuses need message attribute to pass Note */
2283 static GList
*sipe_status_types(SIPE_UNUSED_PARAMETER PurpleAccount
*acc
)
2285 PurpleStatusType
*type
;
2286 GList
*types
= NULL
;
2288 /* Macros to reduce code repetition.
2289 Translators: noun */
2290 #define SIPE_ADD_STATUS(prim,id,name,user) type = purple_status_type_new_with_attrs( \
2292 TRUE, user, FALSE, \
2293 SIPE_STATUS_ATTR_ID_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), \
2295 types = g_list_append(types, type);
2298 SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE
,
2304 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2305 sipe_activity_map
[SIPE_ACTIVITY_BUSY
].status_id
,
2306 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSY
),
2309 /* Do Not Disturb */
2310 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2311 sipe_activity_map
[SIPE_ACTIVITY_DND
].status_id
,
2316 /* Goes first in the list as
2317 * purple picks the first status with the AWAY type
2320 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2326 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2327 sipe_activity_map
[SIPE_ACTIVITY_BRB
].status_id
,
2328 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BRB
),
2331 /* Appear Offline */
2332 SIPE_ADD_STATUS(PURPLE_STATUS_INVISIBLE
,
2337 /* Offline (not user settable) */
2338 SIPE_ADD_STATUS(PURPLE_STATUS_OFFLINE
,
2347 * A callback for g_hash_table_foreach
2350 sipe_buddy_subscribe_cb(char *buddy_name
,
2351 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
2352 struct sipe_account_data
*sip
)
2354 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy_name
);
2355 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
2356 guint time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
2357 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
2359 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy_name
));
2360 g_free(action_name
);
2364 * Removes entries from purple buddy list
2365 * that does not correspond ones in the roaming contact list.
2367 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
2368 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
2369 GSList
*entry
= buddies
;
2370 struct sipe_buddy
*buddy
;
2374 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
2375 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
2378 g
= purple_buddy_get_group(b
);
2379 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
2381 gboolean in_sipe_groups
= FALSE
;
2382 GSList
*entry2
= buddy
->groups
;
2384 struct sipe_group
*group
= entry2
->data
;
2385 if (!strcmp(group
->name
, g
->name
)) {
2386 in_sipe_groups
= TRUE
;
2389 entry2
= entry2
->next
;
2391 if(!in_sipe_groups
) {
2392 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
2393 purple_blist_remove_buddy(b
);
2396 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
2397 purple_blist_remove_buddy(b
);
2399 entry
= entry
->next
;
2401 g_slist_free(buddies
);
2404 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2406 int len
= msg
->bodylen
;
2408 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
2411 const gchar
*contacts_delta
;
2412 xmlnode
*group_node
;
2413 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
2417 /* Convert the contact from XML to Purple Buddies */
2418 isc
= xmlnode_from_str(msg
->body
, len
);
2423 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
2424 if (contacts_delta
) {
2425 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2428 if (!strcmp(isc
->name
, "contactList")) {
2431 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
2432 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2433 const char *name
= xmlnode_get_attrib(group_node
, "name");
2435 if (!strncmp(name
, "~", 1)) {
2436 name
= _("Other Contacts");
2438 group
->name
= g_strdup(name
);
2439 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
2441 sipe_group_add(sip
, group
);
2444 // Make sure we have at least one group
2445 if (g_slist_length(sip
->groups
) == 0) {
2446 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2447 PurpleGroup
*purple_group
;
2448 group
->name
= g_strdup(_("Other Contacts"));
2450 purple_group
= purple_group_new(group
->name
);
2451 purple_blist_add_group(purple_group
, NULL
);
2452 sip
->groups
= g_slist_append(sip
->groups
, group
);
2455 /* Parse contacts */
2456 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
2457 const gchar
*uri
= xmlnode_get_attrib(item
, "uri");
2458 const gchar
*name
= xmlnode_get_attrib(item
, "name");
2460 struct sipe_buddy
*buddy
= NULL
;
2462 gchar
**item_groups
;
2465 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2466 tmp
= sip_uri_from_name(uri
);
2467 buddy_name
= g_ascii_strdown(tmp
, -1);
2470 /* assign to group Other Contacts if nothing else received */
2471 tmp
= g_strdup(xmlnode_get_attrib(item
, "groups"));
2472 if(!tmp
|| !strcmp("", tmp
) ) {
2473 struct sipe_group
*group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
2475 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
2477 item_groups
= g_strsplit(tmp
, " ", 0);
2480 while (item_groups
[i
]) {
2481 struct sipe_group
*group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2483 // If couldn't find the right group for this contact, just put them in the first group we have
2484 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2485 group
= sip
->groups
->data
;
2488 if (group
!= NULL
) {
2489 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2491 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2492 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2494 purple_debug_info("sipe", "Created new buddy %s with alias %s\n", buddy_name
, uri
);
2497 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
2498 if (name
!= NULL
&& strlen(name
) != 0) {
2499 purple_blist_alias_buddy(b
, name
);
2501 purple_debug_info("sipe", "Replaced buddy %s alias with %s\n", buddy_name
, name
);
2506 buddy
= g_new0(struct sipe_buddy
, 1);
2507 buddy
->name
= g_strdup(b
->name
);
2508 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2511 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2513 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2515 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2520 } // while, contact groups
2521 g_strfreev(item_groups
);
2526 sipe_cleanup_local_blist(sip
);
2528 /* Add self-contact if not there yet. 2005 systems. */
2529 /* This will resemble subscription to roaming_self in 2007 systems */
2530 if (!sip
->ocs2007
) {
2531 gchar
*self_uri
= sip_uri_self(sip
);
2532 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, self_uri
);
2535 buddy
= g_new0(struct sipe_buddy
, 1);
2536 buddy
->name
= g_strdup(self_uri
);
2537 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2544 /* subscribe to buddies */
2545 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2546 if (sip
->batched_support
) {
2547 sipe_subscribe_presence_batched(sip
, NULL
);
2549 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2551 sip
->subscribed_buddies
= TRUE
;
2553 /* for 2005 systems schedule contacts' status update
2554 * based on their calendar information
2556 if (!sip
->ocs2007
) {
2557 sipe_sched_calendar_status_update(sip
, time(NULL
));
2564 * Subscribe roaming contacts
2566 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
)
2568 gchar
*to
= sip_uri_self(sip
);
2569 gchar
*tmp
= get_contact(sip
);
2570 gchar
*hdr
= g_strdup_printf(
2571 "Event: vnd-microsoft-roaming-contacts\r\n"
2572 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2573 "Supported: com.microsoft.autoextend\r\n"
2574 "Supported: ms-benotify\r\n"
2575 "Proxy-Require: ms-benotify\r\n"
2576 "Supported: ms-piggyback-first-notify\r\n"
2577 "Contact: %s\r\n", tmp
);
2580 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2585 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
,
2586 SIPE_UNUSED_PARAMETER
void *unused
)
2589 struct sip_dialog
*dialog
;
2590 gchar
*to
= sip_uri_self(sip
);
2591 gchar
*tmp
= get_contact(sip
);
2592 gchar
*hdr
= g_strdup_printf(
2593 "Event: presence.wpending\r\n"
2594 "Accept: text/xml+msrtc.wpending\r\n"
2595 "Supported: com.microsoft.autoextend\r\n"
2596 "Supported: ms-benotify\r\n"
2597 "Proxy-Require: ms-benotify\r\n"
2598 "Supported: ms-piggyback-first-notify\r\n"
2599 "Contact: %s\r\n", tmp
);
2602 /* Subscription is identified by <event> key */
2603 key
= g_strdup_printf("<%s>", "presence.wpending");
2604 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2605 purple_debug_info("sipe", "sipe_subscribe_presence_wpending: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2607 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", dialog
, process_subscribe_response
);
2615 * Fires on deregistration event initiated by server.
2616 * [MS-SIPREGE] SIP extension.
2621 // Content-Type: text/registration-event
2622 // subscription-state: terminated;expires=0
2623 // ms-diagnostics-public: 4141;reason="User disabled"
2625 // deregistered;event=rejected
2627 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2629 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2630 gchar
*event
= NULL
;
2631 gchar
*reason
= NULL
;
2632 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2634 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2635 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2637 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2638 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2639 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2640 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2642 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2646 if (warning
!= NULL
) {
2647 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2648 } else { // for LCS2005
2650 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2651 error_id
= 4140; // [MS-SIPREGE]
2652 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2653 reason
= g_strdup(_("you are already signed in at another location"));
2654 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2656 reason
= g_strdup(_("user disabled")); // [MS-OCER]
2657 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2659 reason
= g_strdup(_("user moved")); // [MS-OCER]
2663 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
2666 sip
->gc
->wants_to_die
= TRUE
;
2667 purple_connection_error(sip
->gc
, warning
);
2672 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2674 xmlnode
*xn_provision_group_list
;
2677 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2679 /* provisionGroup */
2680 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2681 if (!strcmp("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2682 g_free(sip
->focus_factory_uri
);
2683 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2684 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2685 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2689 xmlnode_free(xn_provision_group_list
);
2692 /** for 2005 system */
2694 sipe_process_provisioning(struct sipe_account_data
*sip
,
2697 xmlnode
*xn_provision
;
2700 xn_provision
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2701 if ((node
= xmlnode_get_child(xn_provision
, "user"))) {
2702 purple_debug_info("sipe", "sipe_process_provisioning: uri=%s\n", xmlnode_get_attrib(node
, "uri"));
2703 if ((node
= xmlnode_get_child(node
, "line"))) {
2704 const gchar
*line_uri
= xmlnode_get_attrib(node
, "uri");
2705 const gchar
*server
= xmlnode_get_attrib(node
, "server");
2706 purple_debug_info("sipe", "sipe_process_provisioning: line_uri=%s server=%s\n", line_uri
, server
);
2707 sip_csta_open(sip
, line_uri
, server
);
2710 xmlnode_free(xn_provision
);
2713 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2715 const gchar
*contacts_delta
;
2718 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2724 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2727 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2734 free_container(struct sipe_container
*container
)
2738 if (!container
) return;
2740 entry
= container
->members
;
2742 g_free(entry
->data
);
2743 entry
= g_slist_remove(entry
, entry
->data
);
2749 * Finds locally stored MS-PRES container member
2751 static struct sipe_container_member
*
2752 sipe_find_container_member(struct sipe_container
*container
,
2756 struct sipe_container_member
*member
;
2759 if (container
== NULL
|| type
== NULL
) {
2763 entry
= container
->members
;
2765 member
= entry
->data
;
2766 if (!g_strcasecmp(member
->type
, type
)
2767 && ((!member
->value
&& !value
)
2768 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2772 entry
= entry
->next
;
2778 * Finds locally stored MS-PRES container by id
2780 static struct sipe_container
*
2781 sipe_find_container(struct sipe_account_data
*sip
,
2784 struct sipe_container
*container
;
2791 entry
= sip
->containers
;
2793 container
= entry
->data
;
2794 if (id
== container
->id
) {
2797 entry
= entry
->next
;
2811 sipe_find_access_level(struct sipe_account_data
*sip
,
2815 guint containers
[] = {32000, 400, 300, 200, 100};
2818 for (i
= 0; i
< 5; i
++) {
2819 struct sipe_container_member
*member
;
2820 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2821 if (!container
) continue;
2823 member
= sipe_find_container_member(container
, type
, value
);
2825 return containers
[i
];
2833 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2835 guint container_version
,
2836 const gchar
* action
,
2840 gchar
*self
= sip_uri_self(sip
);
2841 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2844 gchar
*body
= g_strdup_printf(
2845 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2846 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2847 "</setContainerMembers>",
2855 contact
= get_contact(sip
);
2856 hdr
= g_strdup_printf("Contact: %s\r\n"
2857 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2860 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2868 free_publication(struct sipe_publication
*publication
)
2870 g_free(publication
->category
);
2871 g_free(publication
->cal_event_hash
);
2872 g_free(publication
->note
);
2874 g_free(publication
->working_hours_xml_str
);
2875 g_free(publication
->fb_start_str
);
2876 g_free(publication
->free_busy_base64
);
2878 g_free(publication
);
2881 /* key is <category><instance><container> */
2883 sipe_is_our_publication(struct sipe_account_data
*sip
,
2888 /* filling keys for our publications if not yet cached */
2889 if (!sip
->our_publication_keys
) {
2890 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
2891 guint machine_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
2892 guint user_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
);
2893 guint calendar_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
2894 guint cal_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
);
2895 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
2896 guint note_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
);
2898 purple_debug_info("sipe", "* Out Publication Instances *\n");
2899 purple_debug_info("sipe", "\tDevice : %u\t0x%08X\n", device_instance
, device_instance
);
2900 purple_debug_info("sipe", "\tMachine State : %u\t0x%08X\n", machine_instance
, machine_instance
);
2901 purple_debug_info("sipe", "\tUser Stare : %u\t0x%08X\n", user_instance
, user_instance
);
2902 purple_debug_info("sipe", "\tCalendar State : %u\t0x%08X\n", calendar_instance
, calendar_instance
);
2903 purple_debug_info("sipe", "\tCalendar OOF State : %u\t0x%08X\n", cal_oof_instance
, cal_oof_instance
);
2904 purple_debug_info("sipe", "\tCalendar FreeBusy : %u\t0x%08X\n", cal_data_instance
, cal_data_instance
);
2905 purple_debug_info("sipe", "\tOOF Note : %u\t0x%08X\n", note_oof_instance
, note_oof_instance
);
2906 purple_debug_info("sipe", "\tNote : %u\n", 0);
2907 purple_debug_info("sipe", "\tCalendar WorkingHours: %u\n", 0);
2910 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2911 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
2913 /* state:machineState */
2914 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2915 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
2916 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2917 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
2919 /* state:userState */
2920 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2921 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
2922 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2923 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
2925 /* state:calendarState */
2926 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2927 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
2928 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2929 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
2931 /* state:calendarState OOF */
2932 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2933 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
2934 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2935 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
2938 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2939 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
2940 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2941 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
2942 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2943 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
2946 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2947 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
2948 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2949 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
2950 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2951 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
2953 /* calendarData:WorkingHours */
2954 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2955 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
2956 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2957 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
2958 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2959 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
2960 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2961 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
2962 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2963 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
2964 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2965 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
2967 /* calendarData:FreeBusy */
2968 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2969 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
2970 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2971 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
2972 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2973 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
2974 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2975 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
2976 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2977 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
2978 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2979 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
2981 //purple_debug_info("sipe", "sipe_is_our_publication: sip->our_publication_keys length=%d\n",
2982 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
2985 //purple_debug_info("sipe", "sipe_is_our_publication: key=%s\n", key);
2987 entry
= sip
->our_publication_keys
;
2989 //purple_debug_info("sipe", " sipe_is_our_publication: entry->data=%s\n", entry->data);
2990 if (!strcmp(entry
->data
, key
)) {
2993 entry
= entry
->next
;
2998 /** Property names to store in blist.xml */
2999 #define ALIAS_PROP "alias"
3000 #define EMAIL_PROP "email"
3001 #define PHONE_PROP "phone"
3002 #define PHONE_DISPLAY_PROP "phone-display"
3003 #define PHONE_MOBILE_PROP "phone-mobile"
3004 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
3005 #define PHONE_HOME_PROP "phone-home"
3006 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
3007 #define PHONE_OTHER_PROP "phone-other"
3008 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
3009 #define PHONE_CUSTOM1_PROP "phone-custom1"
3010 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
3011 #define SITE_PROP "site"
3012 #define COMPANY_PROP "company"
3013 #define DEPARTMENT_PROP "department"
3014 #define TITLE_PROP "title"
3015 #define OFFICE_PROP "office"
3016 /** implies work address */
3017 #define ADDRESS_STREET_PROP "address-street"
3018 #define ADDRESS_CITY_PROP "address-city"
3019 #define ADDRESS_STATE_PROP "address-state"
3020 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
3021 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
3023 * Update user information
3025 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3026 * @param property_name
3027 * @param property_value may be modified to strip white space
3030 sipe_update_user_info(struct sipe_account_data
*sip
,
3032 const char *property_name
,
3033 char *property_value
)
3035 GSList
*buddies
, *entry
;
3037 if (!property_name
|| strlen(property_name
) == 0) return;
3040 property_value
= g_strstrip(property_value
);
3042 entry
= buddies
= purple_find_buddies(sip
->account
, uri
); /* all buddies in different groups */
3044 const char *prop_str
;
3045 const char *server_alias
;
3046 PurpleBuddy
*p_buddy
= entry
->data
;
3048 /* for Display Name */
3049 if (!strcmp(property_name
, ALIAS_PROP
)) {
3050 if (property_value
&& sipe_is_bad_alias(uri
, purple_buddy_get_alias(p_buddy
))) {
3051 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, property_value
);
3052 purple_blist_alias_buddy(p_buddy
, property_value
);
3055 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3056 if (property_value
&& strlen(property_value
) > 0 &&
3057 ( (server_alias
&& strcmp(property_value
, server_alias
))
3058 || !server_alias
|| strlen(server_alias
) == 0 )
3060 purple_blist_server_alias_buddy(p_buddy
, property_value
);
3063 /* for other properties */
3065 if (property_value
&& strlen(property_value
) > 0) {
3066 prop_str
= purple_blist_node_get_string(&p_buddy
->node
, property_name
);
3067 if (!prop_str
|| g_ascii_strcasecmp(prop_str
, property_value
)) {
3068 purple_blist_node_set_string(&p_buddy
->node
, property_name
, property_value
);
3073 entry
= entry
->next
;
3075 g_slist_free(buddies
);
3080 * Suitable for both 2005 and 2007 systems.
3082 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3084 * @param phone may be modified to strip white space
3085 * @param phone_display_string may be modified to strip white space
3088 sipe_update_user_phone(struct sipe_account_data
*sip
,
3090 const gchar
*phone_type
,
3092 gchar
*phone_display_string
)
3094 const char *phone_node
= PHONE_PROP
; /* work phone by default */
3095 const char *phone_display_node
= PHONE_DISPLAY_PROP
; /* work phone by default */
3097 if(!phone
|| strlen(phone
) == 0) return;
3099 if (phone_type
&& (!strcmp(phone_type
, "mobile") || !strcmp(phone_type
, "cell"))) {
3100 phone_node
= PHONE_MOBILE_PROP
;
3101 phone_display_node
= PHONE_MOBILE_DISPLAY_PROP
;
3102 } else if (phone_type
&& !strcmp(phone_type
, "home")) {
3103 phone_node
= PHONE_HOME_PROP
;
3104 phone_display_node
= PHONE_HOME_DISPLAY_PROP
;
3105 } else if (phone_type
&& !strcmp(phone_type
, "other")) {
3106 phone_node
= PHONE_OTHER_PROP
;
3107 phone_display_node
= PHONE_OTHER_DISPLAY_PROP
;
3108 } else if (phone_type
&& !strcmp(phone_type
, "custom1")) {
3109 phone_node
= PHONE_CUSTOM1_PROP
;
3110 phone_display_node
= PHONE_CUSTOM1_DISPLAY_PROP
;
3113 sipe_update_user_info(sip
, uri
, phone_node
, phone
);
3114 if (phone_display_string
) {
3115 sipe_update_user_info(sip
, uri
, phone_display_node
, phone_display_string
);
3120 sipe_update_calendar(struct sipe_account_data
*sip
)
3122 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
3124 purple_debug_info("sipe", "sipe_update_calendar: started.\n");
3126 if (!strcmp(calendar
, "EXCH")) {
3127 sipe_ews_update_calendar(sip
);
3130 /* schedule repeat */
3131 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_INTERVAL
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3133 purple_debug_info("sipe", "sipe_update_calendar: finished.\n");
3137 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
3138 * by using standard Purple's means of signals and saved statuses.
3140 * Thus all UI elements get updated: Status Button with Note, docklet.
3141 * This is ablolutely important as both our status and note can come
3142 * inbound (roaming) or be updated programmatically (e.g. based on our
3146 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
3147 const char *status_id
,
3148 const char *message
,
3149 time_t do_not_publish
[])
3151 PurpleStatus
*status
= purple_account_get_active_status(account
);
3152 gboolean changed
= TRUE
;
3154 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
3155 purple_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
3161 PurpleSavedStatus
*saved_status
;
3162 const PurpleStatusType
*acct_status_type
=
3163 purple_status_type_find_with_id(account
->status_types
, status_id
);
3164 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
3165 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
3167 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
3169 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
3172 /* If this type+message is unique then create a new transient saved status
3173 * Ref: gtkstatusbox.c
3175 if (!saved_status
) {
3177 GList
*active_accts
= purple_accounts_get_all_active();
3179 saved_status
= purple_savedstatus_new(NULL
, primitive
);
3180 purple_savedstatus_set_message(saved_status
, message
);
3182 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
3183 purple_savedstatus_set_substatus(saved_status
,
3184 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
3186 g_list_free(active_accts
);
3189 do_not_publish
[activity
] = time(NULL
);
3190 purple_debug_info("sipe", "sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]\n",
3191 status_id
, (int)do_not_publish
[activity
]);
3193 /* Set the status for each account */
3194 purple_savedstatus_activate(saved_status
);
3198 struct hash_table_delete_payload
{
3199 GHashTable
*hash_table
;
3204 sipe_remove_category_container_publications_cb(const char *name
,
3205 struct sipe_publication
*publication
,
3206 struct hash_table_delete_payload
*payload
)
3208 if (publication
->container
== payload
->container
) {
3209 g_hash_table_remove(payload
->hash_table
, name
);
3213 sipe_remove_category_container_publications(GHashTable
*our_publications
,
3214 const char *category
,
3217 struct hash_table_delete_payload payload
;
3218 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
3220 if (!payload
.hash_table
) return;
3222 payload
.container
= container
;
3223 g_hash_table_foreach(payload
.hash_table
, (GHFunc
)sipe_remove_category_container_publications_cb
, &payload
);
3227 send_publish_category_initial(struct sipe_account_data
*sip
);
3230 * When we receive some self (BE) NOTIFY with a new subscriber
3231 * we sends a setSubscribers request to him [SIP-PRES] 4.8
3234 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3241 char *display_name
= NULL
;
3243 GSList
*category_names
= NULL
;
3244 int aggreg_avail
= 0;
3245 static sipe_activity aggreg_activity
= SIPE_ACTIVITY_UNSET
;
3246 gboolean do_update_status
= FALSE
;
3247 gboolean has_note_cleaned
= FALSE
;
3249 purple_debug_info("sipe", "sipe_process_roaming_self\n");
3251 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3254 contact
= get_contact(sip
);
3255 to
= sip_uri_self(sip
);
3259 /* set list of categories participating in this XML */
3260 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3261 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3262 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
3264 purple_debug_info("sipe", "sipe_process_roaming_self: category_names length=%d\n",
3265 category_names
? (int) g_slist_length(category_names
) : -1);
3266 /* drop category information */
3267 if (category_names
) {
3268 GSList
*entry
= category_names
;
3270 GHashTable
*cat_publications
;
3271 const gchar
*category
= entry
->data
;
3272 entry
= entry
->next
;
3273 purple_debug_info("sipe", "sipe_process_roaming_self: dropping category: %s\n", category
);
3274 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
3275 if (cat_publications
) {
3276 g_hash_table_remove(sip
->our_publications
, category
);
3277 purple_debug_info("sipe", " sipe_process_roaming_self: dropped category: %s\n", category
);
3281 g_slist_free(category_names
);
3282 /* filling our categories reflected in roaming data */
3283 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3285 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3286 guint container
= xmlnode_get_int_attrib(node
, "container", -1);
3287 guint instance
= xmlnode_get_int_attrib(node
, "instance", -1);
3288 guint version
= xmlnode_get_int_attrib(node
, "version", 0);
3289 time_t publish_time
= (tmp
= xmlnode_get_attrib(node
, "publishTime")) ?
3290 sipe_utils_str_to_time(tmp
) : 0;
3292 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
3294 /* Ex. clear note: <category name="note"/> */
3295 if (container
== (guint
)-1) {
3298 do_update_status
= TRUE
;
3302 /* Ex. clear note: <category name="note" container="200"/> */
3303 if (instance
== (guint
)-1) {
3304 if (container
== 200) {
3307 do_update_status
= TRUE
;
3309 purple_debug_info("sipe", "sipe_process_roaming_self: removing publications for: %s/%u\n", name
, container
);
3310 sipe_remove_category_container_publications(
3311 sip
->our_publications
, name
, container
);
3315 /* key is <category><instance><container> */
3316 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
3317 purple_debug_info("sipe", "sipe_process_roaming_self: key=%s version=%d\n", key
, version
);
3319 /* capture all userState publication for later clean up if required */
3320 if (!strcmp(name
, "state") && (container
== 2 || container
== 3)) {
3321 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3323 if (xn_state
&& !strcmp(xmlnode_get_attrib(xn_state
, "type"), "userState")) {
3324 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3325 publication
->category
= g_strdup(name
);
3326 publication
->instance
= instance
;
3327 publication
->container
= container
;
3328 publication
->version
= version
;
3330 if (!sip
->user_state_publications
) {
3331 sip
->user_state_publications
= g_hash_table_new_full(
3332 g_str_hash
, g_str_equal
,
3333 g_free
, (GDestroyNotify
)free_publication
);
3335 g_hash_table_insert(sip
->user_state_publications
, g_strdup(key
), publication
);
3336 purple_debug_info("sipe", "sipe_process_roaming_self: added to user_state_publications key=%s version=%d\n",
3341 if (sipe_is_our_publication(sip
, key
)) {
3342 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3344 publication
->category
= g_strdup(name
);
3345 publication
->instance
= instance
;
3346 publication
->container
= container
;
3347 publication
->version
= version
;
3349 /* filling publication->availability */
3350 if (!strcmp(name
, "state")) {
3351 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3352 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3355 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3357 publication
->availability
= atoi(avail_str
);
3361 /* for calendarState */
3362 if (xn_state
&& !strcmp(xmlnode_get_attrib(xn_state
, "type"), "calendarState")) {
3363 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3364 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
3366 event
->start_time
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "startTime"));
3368 if (!strcmp(xmlnode_get_attrib(xn_activity
, "token"),
3369 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
))
3371 event
->is_meeting
= TRUE
;
3374 event
->subject
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingSubject"));
3375 event
->location
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingLocation"));
3377 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
3378 purple_debug_info("sipe", "sipe_process_roaming_self: hash=%s\n",
3379 publication
->cal_event_hash
);
3380 sipe_cal_event_free(event
);
3383 /* filling publication->note */
3384 if (!strcmp(name
, "note")) {
3385 xmlnode
*xn_body
= xmlnode_get_descendant(node
, "note", "body", NULL
);
3387 if (!has_note_cleaned
) {
3388 has_note_cleaned
= TRUE
;
3392 sip
->note_since
= publish_time
;
3394 do_update_status
= TRUE
;
3397 g_free(publication
->note
);
3398 publication
->note
= NULL
;
3402 publication
->note
= g_markup_escape_text((tmp
= xmlnode_get_data(xn_body
)), -1);
3404 if (publish_time
>= sip
->note_since
) {
3406 sip
->note
= g_strdup(publication
->note
);
3407 sip
->note_since
= publish_time
;
3408 sip
->is_oof_note
= !strcmp(xmlnode_get_attrib(xn_body
, "type"), "OOF");
3410 do_update_status
= TRUE
;
3415 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
3416 if (!strcmp(name
, "calendarData") && (publication
->container
== 300)) {
3417 xmlnode
*xn_free_busy
= xmlnode_get_descendant(node
, "calendarData", "freeBusy", NULL
);
3418 xmlnode
*xn_working_hours
= xmlnode_get_descendant(node
, "calendarData", "WorkingHours", NULL
);
3420 publication
->fb_start_str
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
3421 publication
->free_busy_base64
= xmlnode_get_data(xn_free_busy
);
3423 if (xn_working_hours
) {
3424 publication
->working_hours_xml_str
= xmlnode_to_str(xn_working_hours
, NULL
);
3428 if (!cat_publications
) {
3429 cat_publications
= g_hash_table_new_full(
3430 g_str_hash
, g_str_equal
,
3431 g_free
, (GDestroyNotify
)free_publication
);
3432 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
3433 purple_debug_info("sipe", "sipe_process_roaming_self: added GHashTable cat=%s\n", name
);
3435 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
3436 purple_debug_info("sipe", "sipe_process_roaming_self: added key=%s version=%d\n", key
, version
);
3440 /* aggregateState (not an our publication) from 2-nd container */
3441 if (!strcmp(name
, "state") && container
== 2) {
3442 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3444 if (xn_state
&& !strcmp(xmlnode_get_attrib(xn_state
, "type"), "aggregateState")) {
3445 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3446 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3449 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3451 aggreg_avail
= atoi(avail_str
);
3457 const char *activity_token
= xmlnode_get_attrib(xn_activity
, "token");
3459 aggreg_activity
= sipe_get_activity_by_token(activity_token
);
3462 do_update_status
= TRUE
;
3466 /* userProperties published by server from AD */
3467 if (!sip
->csta
&& !strcmp(name
, "userProperties")) {
3469 /* line, for Remote Call Control (RCC) */
3470 for (line
= xmlnode_get_descendant(node
, "userProperties", "lines", "line", NULL
); line
; line
= xmlnode_get_next_twin(line
)) {
3471 const gchar
*line_server
= xmlnode_get_attrib(line
, "lineServer");
3472 const gchar
*line_type
= xmlnode_get_attrib(line
, "lineType");
3475 if (!line_server
|| (strcmp(line_type
, "Rcc") && strcmp(line_type
, "Dual"))) continue;
3477 line_uri
= xmlnode_get_data(line
);
3479 purple_debug_info("sipe", "sipe_process_roaming_self: line_uri=%s server=%s\n", line_uri
, line_server
);
3480 sip_csta_open(sip
, line_uri
, line_server
);
3488 purple_debug_info("sipe", "sipe_process_roaming_self: sip->our_publications size=%d\n",
3489 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
3492 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3493 guint id
= xmlnode_get_int_attrib(node
, "id", 0);
3494 struct sipe_container
*container
= sipe_find_container(sip
, id
);
3497 sip
->containers
= g_slist_remove(sip
->containers
, container
);
3498 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
3499 free_container(container
);
3501 container
= g_new0(struct sipe_container
, 1);
3503 container
->version
= xmlnode_get_int_attrib(node
, "version", 0);
3504 sip
->containers
= g_slist_append(sip
->containers
, container
);
3505 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
3507 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
3508 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
3509 member
->type
= xmlnode_get_attrib(node2
, "type");
3510 member
->value
= xmlnode_get_attrib(node2
, "value");
3511 container
->members
= g_slist_append(container
->members
, member
);
3512 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
3513 member
->type
, member
->value
? member
->value
: "");
3517 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
3518 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
3519 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
3520 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
3521 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
3522 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
3523 /* initial set-up to let counterparties see your status */
3524 if (sameEnterpriseAL
< 0) {
3525 struct sipe_container
*container
= sipe_find_container(sip
, 200);
3526 guint version
= container
? container
->version
: 0;
3527 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
3529 if (federatedAL
< 0) {
3530 struct sipe_container
*container
= sipe_find_container(sip
, 100);
3531 guint version
= container
? container
->version
: 0;
3532 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
3534 sip
->access_level_set
= TRUE
;
3538 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3540 const char *acknowledged
;
3544 user
= xmlnode_get_attrib(node
, "user"); /* without 'sip:' prefix */
3545 if (!user
) continue;
3546 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
3547 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
3548 uri
= sip_uri_from_name(user
);
3550 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
3552 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
3553 if(!g_ascii_strcasecmp(acknowledged
,"false")){
3554 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
3555 if (!purple_find_buddy(sip
->account
, uri
)) {
3556 purple_account_request_add(sip
->account
, uri
, _("you"), display_name
, NULL
);
3559 hdr
= g_strdup_printf(
3561 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
3563 body
= g_strdup_printf(
3564 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
3565 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
3566 "</setSubscribers>", user
);
3568 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
3572 g_free(display_name
);
3579 /* Publish initial state if not yet.
3580 * Assuming this happens on initial responce to subscription to roaming-self
3581 * so we've already updated our roaming data in full.
3584 if (!sip
->initial_state_published
) {
3585 send_publish_category_initial(sip
);
3586 sip
->initial_state_published
= TRUE
;
3588 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3589 do_update_status
= FALSE
;
3590 } else if (aggreg_avail
) {
3592 g_free(sip
->status
);
3593 if (aggreg_avail
&& aggreg_avail
< 18000) { /* not offline */
3594 sip
->status
= g_strdup(sipe_get_status_by_availability(aggreg_avail
, NULL
));
3596 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
3600 if (do_update_status
) {
3601 purple_debug_info("sipe", "sipe_process_roaming_self: switch to '%s' for the account\n", sip
->status
);
3602 sipe_set_purple_account_status_and_note(sip
->account
, sip
->status
, sip
->note
, sip
->do_not_publish
);
3608 static void sipe_subscribe_roaming_acl(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-roaming-ACL\r\n"
3614 "Accept: application/vnd-microsoft-roaming-acls+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"
3619 "Contact: %s\r\n", tmp
);
3622 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
3628 * To request for presence information about the user, access level settings that have already been configured by the user
3629 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
3630 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
3633 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
)
3635 gchar
*to
= sip_uri_self(sip
);
3636 gchar
*tmp
= get_contact(sip
);
3637 gchar
*hdr
= g_strdup_printf(
3638 "Event: vnd-microsoft-roaming-self\r\n"
3639 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
3640 "Supported: ms-benotify\r\n"
3641 "Proxy-Require: ms-benotify\r\n"
3642 "Supported: ms-piggyback-first-notify\r\n"
3644 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
3646 gchar
*body
=g_strdup(
3647 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
3648 "<roaming type=\"categories\"/>"
3649 "<roaming type=\"containers\"/>"
3650 "<roaming type=\"subscribers\"/></roamingList>");
3653 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3662 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
)
3664 gchar
*to
= sip_uri_self(sip
);
3665 gchar
*tmp
= get_contact(sip
);
3666 gchar
*hdr
= g_strdup_printf(
3667 "Event: vnd-microsoft-provisioning\r\n"
3668 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
3669 "Supported: com.microsoft.autoextend\r\n"
3670 "Supported: ms-benotify\r\n"
3671 "Proxy-Require: ms-benotify\r\n"
3672 "Supported: ms-piggyback-first-notify\r\n"
3674 "Contact: %s\r\n", tmp
);
3677 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
3682 /** Subscription for provisioning information to help with initial
3683 * configuration. This subscription is a one-time query (denoted by the Expires header,
3684 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
3685 * configuration, meeting policies, and policy settings that Communicator must enforce.
3686 * TODO: for what we need this information.
3689 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
)
3691 gchar
*to
= sip_uri_self(sip
);
3692 gchar
*tmp
= get_contact(sip
);
3693 gchar
*hdr
= g_strdup_printf(
3694 "Event: vnd-microsoft-provisioning-v2\r\n"
3695 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
3696 "Supported: com.microsoft.autoextend\r\n"
3697 "Supported: ms-benotify\r\n"
3698 "Proxy-Require: ms-benotify\r\n"
3699 "Supported: ms-piggyback-first-notify\r\n"
3702 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
3703 gchar
*body
= g_strdup(
3704 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
3705 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
3706 "<provisioningGroup name=\"ucPolicy\"/>"
3707 "</provisioningGroupList>");
3710 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3717 sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key
,
3718 gpointer value
, gpointer user_data
)
3720 struct sip_subscription
*subscription
= value
;
3721 struct sip_dialog
*dialog
= &subscription
->dialog
;
3722 struct sipe_account_data
*sip
= user_data
;
3723 gchar
*tmp
= get_contact(sip
);
3724 gchar
*hdr
= g_strdup_printf(
3727 "Contact: %s\r\n", subscription
->event
, tmp
);
3730 /* Rate limit to max. 25 requests per seconds */
3731 g_usleep(1000000 / 25);
3733 send_sip_request(sip
->gc
, "SUBSCRIBE", dialog
->with
, dialog
->with
, hdr
, NULL
, dialog
, NULL
);
3737 /* IM Session (INVITE and MESSAGE methods) */
3739 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
3741 get_end_points (struct sipe_account_data
*sip
,
3742 struct sip_session
*session
)
3746 if (session
== NULL
) {
3750 res
= g_strdup_printf("<sip:%s>", sip
->username
);
3752 SIPE_DIALOG_FOREACH
{
3754 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
3757 if (dialog
->theirepid
) {
3759 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
3762 } SIPE_DIALOG_FOREACH_END
;
3768 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_account_data
*sip
,
3770 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3772 gboolean ret
= TRUE
;
3774 if (msg
->response
!= 200) {
3775 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
3779 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
3785 * Asks UA/proxy about its capabilities.
3787 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
3789 gchar
*to
= sip_uri(who
);
3790 gchar
*contact
= get_contact(sip
);
3791 gchar
*request
= g_strdup_printf(
3792 "Accept: application/sdp\r\n"
3793 "Contact: %s\r\n", contact
);
3796 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
3803 sipe_notify_user(struct sipe_account_data
*sip
,
3804 struct sip_session
*session
,
3805 PurpleMessageFlags flags
,
3806 const gchar
*message
)
3808 PurpleConversation
*conv
;
3810 if (!session
->conv
) {
3811 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
3813 conv
= session
->conv
;
3815 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
3819 sipe_present_info(struct sipe_account_data
*sip
,
3820 struct sip_session
*session
,
3821 const gchar
*message
)
3823 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
3827 sipe_present_err(struct sipe_account_data
*sip
,
3828 struct sip_session
*session
,
3829 const gchar
*message
)
3831 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_ERROR
, message
);
3835 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
3836 struct sip_session
*session
,
3839 const gchar
*message
)
3841 char *msg
, *msg_tmp
, *msg_tmp2
;
3844 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
3845 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
3847 /* Service unavailable; Server Internal Error; Server Time-out */
3848 if (sip_error
== 503 || sip_error
== 500 || sip_error
== 504) {
3849 label
= _("This message was not delivered to %s because the service is not available");
3850 } else if (sip_error
== 486) { /* Busy Here */
3851 label
= _("This message was not delivered to %s because one or more recipients do not want to be disturbed");
3853 label
= _("This message was not delivered to %s because one or more recipients are offline");
3856 msg_tmp
= g_strdup_printf( "%s:\n%s" ,
3857 msg_tmp2
= g_strdup_printf(label
, who
? who
: ""), msg
? msg
: "");
3858 sipe_present_err(sip
, session
, msg_tmp
);
3865 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
);
3868 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
3869 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3871 gboolean ret
= TRUE
;
3872 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3873 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
3874 struct sip_dialog
*dialog
;
3880 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
3885 dialog
= sipe_dialog_find(session
, with
);
3887 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
3892 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3893 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
3895 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
3897 if (msg
->response
>= 400) {
3898 PurpleBuddy
*pbuddy
;
3899 gchar
*alias
= with
;
3901 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
3903 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
3904 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
3907 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, alias
, message
);
3910 gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
3912 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
));
3913 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
3914 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
3917 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3918 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
3919 key
, g_hash_table_size(session
->unconfirmed_messages
));
3925 if (ret
) sipe_im_process_queue(sip
, session
);
3930 sipe_is_election_finished(struct sip_session
*session
);
3933 sipe_election_result(struct sipe_account_data
*sip
,
3937 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
3938 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3940 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
3941 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3942 struct sip_dialog
*dialog
;
3943 struct sip_session
*session
;
3945 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3947 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
3951 if (msg
->response
== 200 && !strncmp(contenttype
, "application/x-ms-mim", 20)) {
3952 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3953 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
3954 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
3956 if (xn_request_rm_response
) {
3957 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
3958 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
3960 dialog
= sipe_dialog_find(session
, with
);
3962 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
3966 if (allow
&& !g_strcasecmp(allow
, "true")) {
3967 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
3968 dialog
->election_vote
= 1;
3969 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
3970 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
3971 dialog
->election_vote
= -1;
3974 if (sipe_is_election_finished(session
)) {
3975 sipe_election_result(sip
, session
);
3978 } else if (xn_set_rm_response
) {
3981 xmlnode_free(xn_action
);
3988 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
)
3997 sipe_parse_html(msg
, &msgformat
, &msgtext
);
3998 purple_debug_info("sipe", "sipe_send_message: msgformat=%s\n", msgformat
);
4000 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4003 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
4006 msgr
= g_strdup("");
4009 tmp
= get_contact(sip
);
4010 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
4011 //hdr = g_strdup("Content-Type: text/rtf\r\n");
4012 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
4013 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n", tmp
, msgr
);
4017 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
4024 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
4026 GSList
*entry2
= session
->outgoing_message_queue
;
4028 char *queued_msg
= entry2
->data
;
4030 /* for multiparty chat or conference */
4031 if (session
->is_multiparty
|| session
->focus_uri
) {
4032 gchar
*who
= sip_uri_self(sip
);
4033 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
4034 PURPLE_MESSAGE_SEND
, queued_msg
, time(NULL
));
4038 SIPE_DIALOG_FOREACH
{
4041 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
4043 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
4044 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
4045 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
4046 key
, g_hash_table_size(session
->unconfirmed_messages
));
4049 sipe_send_message(sip
, dialog
, queued_msg
);
4050 } SIPE_DIALOG_FOREACH_END
;
4052 entry2
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
4058 sipe_refer_notify(struct sipe_account_data
*sip
,
4059 struct sip_session
*session
,
4066 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4068 hdr
= g_strdup_printf(
4070 "Subscription-State: %s\r\n"
4071 "Content-Type: message/sipfrag\r\n",
4072 status
>= 200 ? "terminated" : "active");
4074 body
= g_strdup_printf(
4075 "SIP/2.0 %d %s\r\n",
4078 send_sip_request(sip
->gc
, "NOTIFY", who
, who
, hdr
, body
, dialog
, NULL
);
4085 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
4087 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
4088 struct sip_session
*session
;
4089 struct sip_dialog
*dialog
;
4093 struct sipmsg
*request_msg
= trans
->msg
;
4095 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4098 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4100 session
= sipe_session_find_im(sip
, with
);
4103 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
4108 dialog
= sipe_dialog_find(session
, with
);
4110 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
4115 sipe_dialog_parse(dialog
, msg
, TRUE
);
4117 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4118 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
4120 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4122 if (msg
->response
!= 200) {
4123 PurpleBuddy
*pbuddy
;
4124 gchar
*alias
= with
;
4126 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
4128 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4129 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
4133 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, alias
, message
);
4135 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
4136 sipe_present_err(sip
, session
, tmp_msg
);
4140 sipe_dialog_remove(session
, with
);
4148 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4149 dialog
->outgoing_invite
= NULL
;
4150 dialog
->is_established
= TRUE
;
4152 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
4154 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
4155 g_free(referred_by
);
4158 /* add user to chat if it is a multiparty session */
4159 if (session
->is_multiparty
) {
4160 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4162 PURPLE_CBFLAGS_NONE
, TRUE
);
4165 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
4166 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
4167 if (session
->outgoing_message_queue
) {
4168 char *queued_msg
= session
->outgoing_message_queue
->data
;
4169 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
4174 sipe_im_process_queue(sip
, session
);
4176 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4177 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
4178 key
, g_hash_table_size(session
->unconfirmed_messages
));
4187 sipe_invite(struct sipe_account_data
*sip
,
4188 struct sip_session
*session
,
4190 const gchar
*msg_body
,
4191 const gchar
*referred_by
,
4192 const gboolean is_triggered
)
4199 char *ms_text_format
= NULL
;
4200 gchar
*roster_manager
;
4202 gchar
*referred_by_str
;
4203 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4205 if (dialog
&& dialog
->is_established
) {
4206 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
4211 dialog
= sipe_dialog_add(session
);
4212 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
4213 dialog
->with
= g_strdup(who
);
4216 if (!(dialog
->ourtag
)) {
4217 dialog
->ourtag
= gentag();
4230 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
4231 purple_debug_info("sipe", "sipe_invite: msgformat=%s\n", msgformat
);
4233 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4237 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
4241 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
4242 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
4247 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
4248 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
4249 purple_debug_info("sipe", "sipe_invite: added message %s to unconfirmed_messages(count=%d)\n",
4250 key
, g_hash_table_size(session
->unconfirmed_messages
));
4254 contact
= get_contact(sip
);
4255 end_points
= get_end_points(sip
, session
);
4256 self
= sip_uri_self(sip
);
4257 roster_manager
= g_strdup_printf(
4258 "Roster-Manager: %s\r\n"
4259 "EndPoints: %s\r\n",
4262 referred_by_str
= referred_by
?
4264 "Referred-By: %s\r\n",
4267 hdr
= g_strdup_printf(
4268 "Supported: ms-sender\r\n"
4274 "Content-Type: application/sdp\r\n",
4275 (session
->roster_manager
&& !strcmp(session
->roster_manager
, self
)) ? roster_manager
: "",
4277 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
4278 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
4280 ms_text_format
? ms_text_format
: "");
4281 g_free(ms_text_format
);
4284 body
= g_strdup_printf(
4286 "o=- 0 0 IN IP4 %s\r\n"
4290 "m=%s %d sip null\r\n"
4291 "a=accept-types:text/plain text/html image/gif "
4292 "multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
4293 purple_network_get_my_ip(-1),
4294 purple_network_get_my_ip(-1),
4295 sip
->ocs2007
? "message" : "x-ms-message",
4298 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
4299 to
, to
, hdr
, body
, dialog
, process_invite_response
);
4302 g_free(roster_manager
);
4304 g_free(referred_by_str
);
4311 sipe_refer(struct sipe_account_data
*sip
,
4312 struct sip_session
*session
,
4317 gchar
*epid
= get_epid(sip
);
4318 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
4319 session
->roster_manager
);
4320 const char *ourtag
= dialog
&& dialog
->ourtag
? dialog
->ourtag
: NULL
;
4322 contact
= get_contact(sip
);
4323 hdr
= g_strdup_printf(
4325 "Refer-to: <%s>\r\n"
4326 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
4327 "Require: com.microsoft.rtc-multiparty\r\n",
4331 ourtag
? ";tag=" : "",
4332 ourtag
? ourtag
: "",
4336 send_sip_request(sip
->gc
, "REFER",
4337 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
4344 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
4345 struct sip_dialog
*dialog
,
4348 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4350 gchar
*body
= g_strdup_printf(
4351 "<?xml version=\"1.0\"?>\r\n"
4352 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4353 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
4354 sip
->username
, bid
);
4356 send_sip_request(sip
->gc
, "INFO",
4357 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4363 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
4364 struct sip_dialog
*dialog
)
4366 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4368 gchar
*body
= g_strdup_printf(
4369 "<?xml version=\"1.0\"?>\r\n"
4370 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4371 "<SetRM uri=\"sip:%s\"/></action>\r\n",
4374 send_sip_request(sip
->gc
, "INFO",
4375 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4381 sipe_session_close(struct sipe_account_data
*sip
,
4382 struct sip_session
* session
)
4384 if (session
&& session
->focus_uri
) {
4385 sipe_conf_immcu_closed(sip
, session
);
4386 conf_session_close(sip
, session
);
4390 SIPE_DIALOG_FOREACH
{
4391 /* @TODO slow down BYE message sending rate */
4392 /* @see single subscription code */
4393 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4394 } SIPE_DIALOG_FOREACH_END
;
4396 sipe_session_remove(sip
, session
);
4401 sipe_session_close_all(struct sipe_account_data
*sip
)
4404 while ((entry
= sip
->sessions
) != NULL
) {
4405 sipe_session_close(sip
, entry
->data
);
4410 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
4412 struct sipe_account_data
*sip
= gc
->proto_data
;
4414 purple_debug_info("sipe", "conversation with %s closed\n", who
);
4415 sipe_session_close(sip
, sipe_session_find_im(sip
, who
));
4419 sipe_chat_leave (PurpleConnection
*gc
, int id
)
4421 struct sipe_account_data
*sip
= gc
->proto_data
;
4422 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
4424 sipe_session_close(sip
, session
);
4427 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
4428 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4430 struct sipe_account_data
*sip
= gc
->proto_data
;
4431 struct sip_session
*session
;
4432 struct sip_dialog
*dialog
;
4433 gchar
*uri
= sip_uri(who
);
4435 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
4437 session
= sipe_session_find_or_add_im(sip
, uri
);
4438 dialog
= sipe_dialog_find(session
, uri
);
4440 // Queue the message
4441 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
4443 if (dialog
&& !dialog
->outgoing_invite
) {
4444 sipe_im_process_queue(sip
, session
);
4445 } else if (!dialog
|| !dialog
->outgoing_invite
) {
4446 // Need to send the INVITE to get the outgoing dialog setup
4447 sipe_invite(sip
, session
, uri
, what
, NULL
, FALSE
);
4454 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
4455 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4457 struct sipe_account_data
*sip
= gc
->proto_data
;
4458 struct sip_session
*session
;
4460 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
4462 session
= sipe_session_find_chat_by_id(sip
, id
);
4464 // Queue the message
4465 if (session
&& session
->dialogs
) {
4466 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
4468 sipe_im_process_queue(sip
, session
);
4470 gchar
*chat_name
= purple_find_chat(sip
->gc
, id
)->name
;
4471 const gchar
*proto_chat_id
= sipe_chat_find_name(chat_name
);
4473 purple_debug_info("sipe", "sipe_chat_send: chat_name='%s'\n", chat_name
? chat_name
: "NULL");
4474 purple_debug_info("sipe", "sipe_chat_send: proto_chat_id='%s'\n", proto_chat_id
? proto_chat_id
: "NULL");
4477 struct sip_session
*session
= sipe_session_add_chat(sip
);
4479 session
->is_multiparty
= FALSE
;
4480 session
->focus_uri
= g_strdup(proto_chat_id
);
4481 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
4483 sipe_invite_conf_focus(sip
, session
);
4490 /* End IM Session (INVITE and MESSAGE methods) */
4492 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4494 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4495 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4497 struct sip_session
*session
;
4499 purple_debug_info("sipe", "process_incoming_info: \n%s\n", msg
->body
? msg
->body
: "");
4501 /* Call Control protocol */
4502 if (g_str_has_prefix(contenttype
, "application/csta+xml"))
4504 process_incoming_info_csta(sip
, msg
);
4508 from
= parse_from(sipmsg_find_header(msg
, "From"));
4509 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4511 session
= sipe_session_find_im(sip
, from
);
4518 if (g_str_has_prefix(contenttype
, "application/x-ms-mim"))
4520 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4521 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
4522 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
4524 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
4526 if (xn_request_rm
) {
4527 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
4528 int bid
= xmlnode_get_int_attrib(xn_request_rm
, "bid", 0);
4529 gchar
*body
= g_strdup_printf(
4530 "<?xml version=\"1.0\"?>\r\n"
4531 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4532 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
4534 session
->bid
< bid
? "true" : "false");
4535 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4537 } else if (xn_set_rm
) {
4539 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
4540 g_free(session
->roster_manager
);
4541 session
->roster_manager
= g_strdup(rm
);
4543 body
= g_strdup_printf(
4544 "<?xml version=\"1.0\"?>\r\n"
4545 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4546 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
4548 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4551 xmlnode_free(xn_action
);
4556 /* looks like purple lacks typing notification for chat */
4557 if (!session
->is_multiparty
&& !session
->focus_uri
) {
4558 xmlnode
*xn_keyboard_activity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4559 const char *status
= xmlnode_get_attrib(xmlnode_get_child(xn_keyboard_activity
, "status"),
4561 if (status
&& !strcmp(status
, "type")) {
4562 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
4563 } else if (status
&& !strcmp(status
, "idle")) {
4564 serv_got_typing_stopped(sip
->gc
, from
);
4566 xmlnode_free(xn_keyboard_activity
);
4569 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4574 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4576 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4577 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4578 struct sip_session
*session
;
4579 struct sip_dialog
*dialog
;
4581 /* collect dialog identification
4582 * we need callid, ourtag and theirtag to unambiguously identify dialog
4584 /* take data before 'msg' will be modified by send_sip_response */
4585 dialog
= g_new0(struct sip_dialog
, 1);
4586 dialog
->callid
= g_strdup(callid
);
4587 dialog
->cseq
= parse_cseq(sipmsg_find_header(msg
, "CSeq"));
4588 dialog
->with
= g_strdup(from
);
4589 sipe_dialog_parse(dialog
, msg
, FALSE
);
4591 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4593 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4595 session
= sipe_session_find_im(sip
, from
);
4598 sipe_dialog_free(dialog
);
4603 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
4604 g_free(session
->roster_manager
);
4605 session
->roster_manager
= NULL
;
4608 /* This what BYE is essentially for - terminating dialog */
4609 sipe_dialog_remove_3(session
, dialog
);
4610 sipe_dialog_free(dialog
);
4611 if (session
->focus_uri
&& !g_strcasecmp(from
, session
->im_mcu_uri
)) {
4612 sipe_conf_immcu_closed(sip
, session
);
4613 } else if (session
->is_multiparty
) {
4614 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
4620 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4622 gchar
*self
= sip_uri_self(sip
);
4623 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4624 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4625 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
4626 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
4627 struct sip_session
*session
;
4628 struct sip_dialog
*dialog
;
4630 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4631 dialog
= sipe_dialog_find(session
, from
);
4633 if (!session
|| !dialog
|| !session
->roster_manager
|| strcmp(session
->roster_manager
, self
)) {
4634 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
4636 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
4638 sipe_invite(sip
, session
, refer_to
, NULL
, referred_by
, FALSE
);
4644 g_free(referred_by
);
4648 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
4650 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
4651 struct sip_session
*session
;
4652 struct sip_dialog
*dialog
;
4654 if (state
== PURPLE_NOT_TYPING
)
4657 session
= sipe_session_find_im(sip
, who
);
4658 dialog
= sipe_dialog_find(session
, who
);
4660 if (session
&& dialog
&& dialog
->is_established
) {
4661 send_sip_request(gc
, "INFO", who
, who
,
4662 "Content-Type: application/xml\r\n",
4663 SIPE_SEND_TYPING
, dialog
, NULL
);
4665 return SIPE_TYPING_SEND_TIMEOUT
;
4668 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
4670 GSList
*tmp
= sip
->transactions
;
4671 time_t currtime
= time(NULL
);
4673 struct transaction
*trans
= tmp
->data
;
4675 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
4676 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
4679 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
4681 sendout_sipmsg(sip
, trans
->msg
);
4688 static void do_reauthenticate_cb(struct sipe_account_data
*sip
,
4689 SIPE_UNUSED_PARAMETER
void *unused
)
4691 /* register again when security token expires */
4692 /* we have to start a new authentication as the security token
4693 * is almost expired by sending a not signed REGISTER message */
4694 purple_debug_info("sipe", "do a full reauthentication\n");
4695 sipe_auth_free(&sip
->registrar
);
4696 sipe_auth_free(&sip
->proxy
);
4697 sip
->registerstatus
= 0;
4699 sip
->reauthenticate_set
= FALSE
;
4702 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4706 gboolean found
= FALSE
;
4708 from
= parse_from(sipmsg_find_header(msg
, "From"));
4712 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
4714 contenttype
= sipmsg_find_header(msg
, "Content-Type");
4715 if (!strncmp(contenttype
, "text/plain", 10)
4716 || !strncmp(contenttype
, "text/html", 9)
4717 || !strncmp(contenttype
, "multipart/related", 17)
4718 || !strncmp(contenttype
, "multipart/alternative", 21))
4720 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4721 gchar
*html
= get_html_message(contenttype
, msg
->body
);
4723 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
4725 session
= sipe_session_find_im(sip
, from
);
4728 if (session
&& session
->focus_uri
) { /* a conference */
4729 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
4730 gchar
*sender
= parse_from(tmp
);
4732 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
4733 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4735 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
4736 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
4737 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4739 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
4742 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4745 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
4746 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4751 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
4755 state
= xmlnode_get_child(isc
, "state");
4758 purple_debug_info("sipe", "process_incoming_message: no state found\n");
4763 statedata
= xmlnode_get_data(state
);
4765 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
4766 else serv_got_typing_stopped(sip
->gc
, from
);
4771 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4775 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4776 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
4778 session
= sipe_session_find_im(sip
, from
);
4781 gchar
*msg
= g_strdup_printf(_("Received a message with unrecognized contents from %s"),
4783 sipe_present_err(sip
, session
, msg
);
4787 purple_debug_info("sipe", "got unknown mime-type '%s'\n", contenttype
);
4788 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
4793 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4799 gboolean is_multiparty
= FALSE
;
4800 gboolean is_triggered
= FALSE
;
4801 gboolean was_multiparty
= TRUE
;
4802 gboolean just_joined
= FALSE
;
4804 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4805 gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
4806 gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
4807 gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
4808 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4809 GSList
*end_points
= NULL
;
4811 struct sip_session
*session
;
4813 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? tmp
= fix_newlines(msg
->body
) : "");
4816 /* Invitation to join conference */
4817 if (!strncmp(content_type
, "application/ms-conf-invite+xml", 30)) {
4818 process_incoming_invite_conf(sip
, msg
);
4822 /* Only accept text invitations */
4823 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
4824 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
4828 // TODO There *must* be a better way to clean up the To header to add a tag...
4829 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
4830 oldHeader
= sipmsg_find_header(msg
, "To");
4832 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
4833 sipmsg_remove_header_now(msg
, "To");
4834 sipmsg_add_header_now(msg
, "To", newHeader
);
4837 if (end_points_hdr
) {
4838 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
4840 if (g_slist_length(end_points
) > 2) {
4841 is_multiparty
= TRUE
;
4844 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
4845 is_triggered
= TRUE
;
4846 is_multiparty
= TRUE
;
4849 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4850 /* Convert to multiparty */
4851 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
4852 g_free(session
->with
);
4853 session
->with
= NULL
;
4854 was_multiparty
= FALSE
;
4855 session
->is_multiparty
= TRUE
;
4856 session
->chat_id
= rand();
4859 if (!session
&& is_multiparty
) {
4860 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
4863 from
= parse_from(sipmsg_find_header(msg
, "From"));
4865 session
= sipe_session_find_or_add_im(sip
, from
);
4869 g_free(session
->callid
);
4870 session
->callid
= g_strdup(callid
);
4872 session
->is_multiparty
= is_multiparty
;
4873 if (roster_manager
) {
4874 session
->roster_manager
= g_strdup(roster_manager
);
4878 if (is_multiparty
&& end_points
) {
4879 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
4880 GSList
*entry
= end_points
;
4882 struct sip_dialog
*dialog
;
4883 struct sipendpoint
*end_point
= entry
->data
;
4884 entry
= entry
->next
;
4886 if (!g_strcasecmp(from
, end_point
->contact
) ||
4887 !g_strcasecmp(to
, end_point
->contact
))
4890 dialog
= sipe_dialog_find(session
, end_point
->contact
);
4892 g_free(dialog
->theirepid
);
4893 dialog
->theirepid
= end_point
->epid
;
4894 end_point
->epid
= NULL
;
4896 dialog
= sipe_dialog_add(session
);
4898 dialog
->callid
= g_strdup(session
->callid
);
4899 dialog
->with
= end_point
->contact
;
4900 end_point
->contact
= NULL
;
4901 dialog
->theirepid
= end_point
->epid
;
4902 end_point
->epid
= NULL
;
4906 /* send triggered INVITE */
4907 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, TRUE
);
4914 GSList
*entry
= end_points
;
4916 struct sipendpoint
*end_point
= entry
->data
;
4917 entry
= entry
->next
;
4918 g_free(end_point
->contact
);
4919 g_free(end_point
->epid
);
4922 g_slist_free(end_points
);
4926 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
4928 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
4930 dialog
= sipe_dialog_add(session
);
4932 dialog
->callid
= g_strdup(session
->callid
);
4933 dialog
->with
= g_strdup(from
);
4934 sipe_dialog_parse(dialog
, msg
, FALSE
);
4936 if (!dialog
->ourtag
) {
4937 dialog
->ourtag
= newTag
;
4944 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
4948 if (is_multiparty
&& !session
->conv
) {
4949 gchar
*chat_title
= sipe_chat_get_name(callid
);
4950 gchar
*self
= sip_uri_self(sip
);
4951 /* create prpl chat */
4952 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_title
);
4953 session
->chat_title
= g_strdup(chat_title
);
4954 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
4956 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4958 PURPLE_CBFLAGS_NONE
, FALSE
);
4963 if (is_multiparty
&& !was_multiparty
) {
4964 /* add current IM counterparty to chat */
4965 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4966 sipe_dialog_first(session
)->with
, NULL
,
4967 PURPLE_CBFLAGS_NONE
, FALSE
);
4970 /* add inviting party to chat */
4971 if (just_joined
&& session
->conv
) {
4972 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4974 PURPLE_CBFLAGS_NONE
, TRUE
);
4977 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
4979 /* This used only in 2005 official client, not 2007 or Reuters.
4980 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
4981 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
4983 if (is_multiparty
) {
4984 /* please do not optimize logic inside as this code may be re-enabled for other cases */
4985 gchar
*ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
4986 if (ms_text_format
) {
4987 if (g_str_has_prefix(ms_text_format
, "text/plain") || g_str_has_prefix(ms_text_format
, "text/html")) {
4989 gchar
*html
= get_html_message(ms_text_format
, NULL
);
4991 if (is_multiparty
) {
4992 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
4993 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4995 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
4998 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5007 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
5008 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5009 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5011 body
= g_strdup_printf(
5013 "o=- 0 0 IN IP4 %s\r\n"
5017 "m=%s %d sip sip:%s\r\n"
5018 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
5019 purple_network_get_my_ip(-1),
5020 purple_network_get_my_ip(-1),
5021 sip
->ocs2007
? "message" : "x-ms-message",
5024 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5028 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5032 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
5033 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5034 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5036 body
= g_strdup_printf(
5038 "o=- 0 0 IN IP4 0.0.0.0\r\n"
5040 "c=IN IP4 0.0.0.0\r\n"
5042 "m=%s %d sip sip:%s\r\n"
5043 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
5044 sip
->ocs2007
? "message" : "x-ms-message",
5047 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5051 static void sipe_connection_cleanup(struct sipe_account_data
*);
5052 static void create_connection(struct sipe_account_data
*, gchar
*, int);
5054 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
5055 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5058 const gchar
*expires_header
;
5060 GSList
*hdr
= msg
->headers
;
5061 struct siphdrelement
*elem
;
5063 expires_header
= sipmsg_find_header(msg
, "Expires");
5064 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
5065 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
5067 switch (msg
->response
) {
5070 sip
->registerstatus
= 0;
5072 gchar
*contact_hdr
= NULL
;
5077 gchar
*server_hdr
= sipmsg_find_header(msg
, "Server");
5079 if (!sip
->reregister_set
) {
5080 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
5081 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
5082 g_free(action_name
);
5083 sip
->reregister_set
= TRUE
;
5086 sip
->registerstatus
= 3;
5088 if (server_hdr
&& !sip
->server_version
) {
5089 sip
->server_version
= g_strdup(server_hdr
);
5095 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5097 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
5100 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
5104 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
5105 fill_auth(tmp
, &sip
->registrar
);
5108 if (!sip
->reauthenticate_set
) {
5109 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
5110 guint reauth_timeout
;
5111 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
5112 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
5113 reauth_timeout
= sip
->registrar
.expires
- 300;
5115 /* NTLM: we have to reauthenticate as our security token expires
5116 after eight hours (be five minutes early) */
5117 reauth_timeout
= (8 * 3600) - 300;
5119 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
5120 g_free(action_name
);
5121 sip
->reauthenticate_set
= TRUE
;
5124 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
5126 epid
= get_epid(sip
);
5127 uuid
= generateUUIDfromEPID(epid
);
5130 // There can be multiple Contact headers (one per location where the user is logged in) so
5131 // make sure to only get the one for this uuid
5132 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
5133 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
5134 if (valid_contact
) {
5135 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
5136 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
5137 g_free(valid_contact
);
5140 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
5145 g_free(sip
->contact
);
5147 sip
->contact
= g_strdup_printf("<%s>", gruu
);
5150 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
5151 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
);
5153 sip
->ocs2007
= FALSE
;
5154 sip
->batched_support
= FALSE
;
5159 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
5160 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
5161 /* We interpret this as OCS2007+ indicator */
5162 sip
->ocs2007
= TRUE
;
5163 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s (indicates OCS2007+)\n", elem
->value
);
5165 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
5166 sip
->batched_support
= TRUE
;
5167 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s\n", elem
->value
);
5170 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
5171 gchar
**caps
= g_strsplit(elem
->value
,",",0);
5174 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
5175 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
5180 hdr
= g_slist_next(hdr
);
5183 /* rejoin open chats to be able to use them by continue to send messages */
5184 purple_conversation_foreach(sipe_rejoin_chat
);
5187 if (!sip
->subscribed
) { //do it just once, not every re-register
5189 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
5190 (GCompareFunc
)g_ascii_strcasecmp
)) {
5191 sipe_subscribe_roaming_contacts(sip
);
5194 /* For 2007+ it does not make sence to subscribe to:
5195 * vnd-microsoft-roaming-ACL
5196 * vnd-microsoft-provisioning (not v2)
5198 * These are for backward compatibility.
5202 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
5203 (GCompareFunc
)g_ascii_strcasecmp
)) {
5204 sipe_subscribe_roaming_self(sip
);
5206 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
5207 (GCompareFunc
)g_ascii_strcasecmp
)) {
5208 sipe_subscribe_roaming_provisioning_v2(sip
);
5211 /* For 2005- servers */
5214 //sipe_options_request(sip, sip->sipdomain);
5216 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
5217 (GCompareFunc
)g_ascii_strcasecmp
)) {
5218 sipe_subscribe_roaming_acl(sip
);
5220 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
5221 (GCompareFunc
)g_ascii_strcasecmp
)) {
5222 sipe_subscribe_roaming_provisioning(sip
);
5224 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
5225 (GCompareFunc
)g_ascii_strcasecmp
)) {
5226 sipe_subscribe_presence_wpending(sip
, msg
);
5229 /* For 2007+ we publish our initial statuses and calendar data only after
5230 * received our existing publications in sipe_process_roaming_self()
5231 * Only in this case we know versions of current publications made
5234 /* For 2005- we publish our initial statuses only after
5235 * received our existing UserInfo data in response to
5236 * self subscription.
5237 * Only in this case we won't override existing UserInfo data
5238 * set earlier or by other client on our behalf.
5242 sip
->subscribed
= TRUE
;
5245 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
5246 "timeout=", ";", NULL
);
5247 if (timeout
!= NULL
) {
5248 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
5249 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
5250 sip
->keepalive_timeout
);
5254 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\n", sip
->cseq
);
5259 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
5261 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
5262 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
5266 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
5269 tmp
= g_strsplit(parts
[0], ":", 0);
5270 hostname
= g_strdup(tmp
[0]);
5271 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
5275 tmp
= g_strsplit(parts
[i
], "=", 0);
5277 if (g_strcasecmp("transport", tmp
[0]) == 0) {
5278 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
5279 transport
= SIPE_TRANSPORT_TCP
;
5280 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
5281 transport
= SIPE_TRANSPORT_UDP
;
5290 /* Close old connection */
5291 sipe_connection_cleanup(sip
);
5293 /* Create new connection */
5294 sip
->transport
= transport
;
5295 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
5296 hostname
, port
, TRANSPORT_DESCRIPTOR
);
5297 create_connection(sip
, hostname
, port
);
5303 if (sip
->registerstatus
!= 2) {
5304 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
5305 if (sip
->registrar
.retries
> 3) {
5306 sip
->gc
->wants_to_die
= TRUE
;
5307 purple_connection_error(sip
->gc
, _("Wrong password"));
5311 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5313 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
5316 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
5319 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
5320 fill_auth(tmp
, &sip
->registrar
);
5321 sip
->registerstatus
= 2;
5322 if (sip
->account
->disconnecting
) {
5323 do_register_exp(sip
, 0);
5331 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
5332 gchar
**reason
= NULL
;
5333 if (warning
!= NULL
) {
5335 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
5337 reason
= g_strsplit(warning
, "\"", 0);
5339 warning
= g_strdup_printf(_("You have been rejected by the server: %s"),
5340 (reason
&& reason
[1]) ? reason
[1] : _("no reason given"));
5343 sip
->gc
->wants_to_die
= TRUE
;
5344 purple_connection_error(sip
->gc
, warning
);
5351 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
5352 gchar
*reason
= NULL
;
5353 if (warning
!= NULL
) {
5354 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
5356 warning
= g_strdup_printf(_("Not found: %s. Please contact your Administrator"),
5357 warning
? (reason
? reason
: _("no reason given")) :
5358 _("SIP is either not enabled for the destination URI or it does not exist"));
5361 sip
->gc
->wants_to_die
= TRUE
;
5362 purple_connection_error(sip
->gc
, warning
);
5368 case 504: /* Server time-out */
5370 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
5371 gchar
*reason
= NULL
;
5372 if (warning
!= NULL
) {
5373 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
5375 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: "<a href=\"http://www.reuters.com\">http://www.reuters.com</a>"/*_("no reason given")*/);
5378 sip
->gc
->wants_to_die
= TRUE
;
5379 purple_connection_error(sip
->gc
, warning
);
5389 * Returns 2005-style activity and Availability.
5391 * @param status Sipe statis id.
5394 sipe_get_act_avail_by_status_2005(const char *status
,
5398 int avail
= 300; /* online */
5399 int act
= 400; /* Available */
5401 if (!strcmp(status
, SIPE_STATUS_ID_AWAY
)) {
5403 //} else if (!strcmp(status, SIPE_STATUS_ID_LUNCH)) {
5405 } else if (!strcmp(status
, SIPE_STATUS_ID_BRB
)) {
5407 } else if (!strcmp(status
, SIPE_STATUS_ID_AVAILABLE
)) {
5409 //} else if (!strcmp(status, SIPE_STATUS_ID_ON_PHONE)) {
5411 } else if (!strcmp(status
, SIPE_STATUS_ID_BUSY
) ||
5412 !strcmp(status
, SIPE_STATUS_ID_DND
)) {
5414 } else if (!strcmp(status
, SIPE_STATUS_ID_INVISIBLE
) ||
5415 !strcmp(status
, SIPE_STATUS_ID_OFFLINE
)) {
5416 avail
= 0; /* offline */
5419 act
= 400; /* Available */
5422 if (activity
) *activity
= act
;
5423 if (availability
) *availability
= avail
;
5429 * @param activity 2005 aggregated activity. Ex.: 600
5430 * @param availablity 2005 aggregated availablity. Ex.: 300
5433 sipe_get_status_by_act_avail_2005(const int activity
,
5434 const int availablity
,
5435 char **activity_desc
)
5437 const char *status_id
= NULL
;
5438 const char *act
= NULL
;
5440 if (activity
< 150) {
5441 status_id
= SIPE_STATUS_ID_AWAY
;
5442 } else if (activity
< 200) {
5443 //status_id = SIPE_STATUS_ID_LUNCH;
5444 status_id
= SIPE_STATUS_ID_AWAY
;
5445 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
5446 } else if (activity
< 300) {
5447 //status_id = SIPE_STATUS_ID_IDLE;
5448 status_id
= SIPE_STATUS_ID_AWAY
;
5449 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5450 } else if (activity
< 400) {
5451 status_id
= SIPE_STATUS_ID_BRB
;
5452 } else if (activity
< 500) {
5453 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5454 } else if (activity
< 600) {
5455 //status_id = SIPE_STATUS_ID_ON_PHONE;
5456 status_id
= SIPE_STATUS_ID_BUSY
;
5457 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
5458 } else if (activity
< 700) {
5459 status_id
= SIPE_STATUS_ID_BUSY
;
5460 } else if (activity
< 800) {
5461 status_id
= SIPE_STATUS_ID_AWAY
;
5463 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5466 if (availablity
< 100)
5467 status_id
= SIPE_STATUS_ID_OFFLINE
;
5469 if (activity_desc
&& act
) {
5470 g_free(*activity_desc
);
5471 *activity_desc
= g_strdup(act
);
5478 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
5481 sipe_get_status_by_availability(int avail
,
5482 char** activity_desc
)
5485 const char *act
= NULL
;
5488 status
= SIPE_STATUS_ID_OFFLINE
;
5489 } else if (avail
< 4500) {
5490 status
= SIPE_STATUS_ID_AVAILABLE
;
5491 } else if (avail
< 6000) {
5492 //status = SIPE_STATUS_ID_IDLE;
5493 status
= SIPE_STATUS_ID_AVAILABLE
;
5494 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5495 } else if (avail
< 7500) {
5496 status
= SIPE_STATUS_ID_BUSY
;
5497 } else if (avail
< 9000) {
5498 //status = SIPE_STATUS_ID_BUSYIDLE;
5499 status
= SIPE_STATUS_ID_BUSY
;
5500 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
5501 } else if (avail
< 12000) {
5502 status
= SIPE_STATUS_ID_DND
;
5503 } else if (avail
< 15000) {
5504 status
= SIPE_STATUS_ID_BRB
;
5505 } else if (avail
< 18000) {
5506 status
= SIPE_STATUS_ID_AWAY
;
5508 status
= SIPE_STATUS_ID_OFFLINE
;
5511 if (activity_desc
&& act
) {
5512 g_free(*activity_desc
);
5513 *activity_desc
= g_strdup(act
);
5520 * Returns 2007-style availability value
5522 * @param sipe_status_id (in)
5523 * @param activity_token (out) Must be g_free()'d after use if consumed.
5526 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
5529 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
5531 if (!strcmp(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
5532 availability
= 15500;
5533 if (!activity_token
|| !(*activity_token
)) {
5534 activity
= SIPE_ACTIVITY_AWAY
;
5536 } else if (!strcmp(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
5537 availability
= 12500;
5538 activity
= SIPE_ACTIVITY_BRB
;
5539 } else if (!strcmp(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
5540 availability
= 9500;
5541 activity
= SIPE_ACTIVITY_DND
;
5542 } else if (!strcmp(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
5543 availability
= 6500;
5544 if (!activity_token
|| !(*activity_token
)) {
5545 activity
= SIPE_ACTIVITY_BUSY
;
5547 } else if (!strcmp(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
5548 availability
= 3500;
5549 activity
= SIPE_ACTIVITY_ONLINE
;
5550 } else if (!strcmp(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
5553 // Offline or invisible
5554 availability
= 18500;
5555 activity
= SIPE_ACTIVITY_OFFLINE
;
5558 if (activity_token
) {
5559 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
5561 return availability
;
5564 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5567 xmlnode
*xn_categories
;
5568 xmlnode
*xn_category
;
5570 const char *status
= NULL
;
5571 gboolean do_update_status
= FALSE
;
5572 gboolean has_note_cleaned
= FALSE
;
5573 gboolean has_free_busy_cleaned
= FALSE
;
5575 xn_categories
= xmlnode_from_str(data
, len
);
5576 uri
= xmlnode_get_attrib(xn_categories
, "uri"); /* with 'sip:' prefix */
5578 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
5580 xn_category
= xmlnode_get_next_twin(xn_category
) )
5583 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
5584 time_t publish_time
= (tmp
= xmlnode_get_attrib(xn_category
, "publishTime")) ?
5585 sipe_utils_str_to_time(tmp
) : 0;
5588 if (!strcmp(attrVar
, "contactCard"))
5591 /* identity - Display Name and email */
5592 node
= xmlnode_get_descendant(xn_category
, "contactCard", "identity", NULL
);
5594 char* display_name
= xmlnode_get_data(
5595 xmlnode_get_descendant(node
, "name", "displayName", NULL
));
5596 char* email
= xmlnode_get_data(
5597 xmlnode_get_child(node
, "email"));
5599 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5600 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
5602 g_free(display_name
);
5606 node
= xmlnode_get_descendant(xn_category
, "contactCard", "company", NULL
);
5608 char* company
= xmlnode_get_data(node
);
5609 sipe_update_user_info(sip
, uri
, COMPANY_PROP
, company
);
5613 node
= xmlnode_get_descendant(xn_category
, "contactCard", "department", NULL
);
5615 char* department
= xmlnode_get_data(node
);
5616 sipe_update_user_info(sip
, uri
, DEPARTMENT_PROP
, department
);
5620 node
= xmlnode_get_descendant(xn_category
, "contactCard", "title", NULL
);
5622 char* title
= xmlnode_get_data(node
);
5623 sipe_update_user_info(sip
, uri
, TITLE_PROP
, title
);
5627 node
= xmlnode_get_descendant(xn_category
, "contactCard", "office", NULL
);
5629 char* office
= xmlnode_get_data(node
);
5630 sipe_update_user_info(sip
, uri
, OFFICE_PROP
, office
);
5634 node
= xmlnode_get_descendant(xn_category
, "contactCard", "url", NULL
);
5636 char* site
= xmlnode_get_data(node
);
5637 sipe_update_user_info(sip
, uri
, SITE_PROP
, site
);
5641 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "phone", NULL
);
5643 node
= xmlnode_get_next_twin(node
))
5645 const char *phone_type
= xmlnode_get_attrib(node
, "type");
5646 char* phone
= xmlnode_get_data(xmlnode_get_child(node
, "uri"));
5647 char* phone_display_string
= xmlnode_get_data(xmlnode_get_child(node
, "displayString"));
5649 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, phone_display_string
);
5652 g_free(phone_display_string
);
5655 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "address", NULL
);
5657 node
= xmlnode_get_next_twin(node
))
5659 if (!strcmp(xmlnode_get_attrib(node
, "type"), "work")) {
5660 char* street
= xmlnode_get_data(xmlnode_get_child(node
, "street"));
5661 char* city
= xmlnode_get_data(xmlnode_get_child(node
, "city"));
5662 char* state
= xmlnode_get_data(xmlnode_get_child(node
, "state"));
5663 char* zipcode
= xmlnode_get_data(xmlnode_get_child(node
, "zipcode"));
5664 char* country_code
= xmlnode_get_data(xmlnode_get_child(node
, "countryCode"));
5666 sipe_update_user_info(sip
, uri
, ADDRESS_STREET_PROP
, street
);
5667 sipe_update_user_info(sip
, uri
, ADDRESS_CITY_PROP
, city
);
5668 sipe_update_user_info(sip
, uri
, ADDRESS_STATE_PROP
, state
);
5669 sipe_update_user_info(sip
, uri
, ADDRESS_ZIPCODE_PROP
, zipcode
);
5670 sipe_update_user_info(sip
, uri
, ADDRESS_COUNTRYCODE_PROP
, country_code
);
5676 g_free(country_code
);
5683 else if (!strcmp(attrVar
, "note"))
5686 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
5688 if (!has_note_cleaned
) {
5689 has_note_cleaned
= TRUE
;
5691 g_free(sbuddy
->note
);
5692 sbuddy
->note
= NULL
;
5693 sbuddy
->is_oof_note
= FALSE
;
5694 sbuddy
->note_since
= publish_time
;
5696 do_update_status
= TRUE
;
5698 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
5699 /* clean up in case no 'note' element is supplied
5700 * which indicate note removal in client
5702 g_free(sbuddy
->note
);
5703 sbuddy
->note
= NULL
;
5704 sbuddy
->is_oof_note
= FALSE
;
5705 sbuddy
->note_since
= publish_time
;
5707 xn_node
= xmlnode_get_descendant(xn_category
, "note", "body", NULL
);
5710 sbuddy
->note
= g_markup_escape_text((tmp
= xmlnode_get_data(xn_node
)), -1);
5712 sbuddy
->is_oof_note
= !strcmp(xmlnode_get_attrib(xn_node
, "type"), "OOF");
5713 sbuddy
->note_since
= publish_time
;
5715 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s), note(%s)\n",
5716 uri
, sbuddy
->note
? sbuddy
->note
: "");
5718 /* to trigger UI refresh in case no status info is supplied in this update */
5719 do_update_status
= TRUE
;
5724 else if(!strcmp(attrVar
, "state"))
5728 xmlnode
*xn_availability
;
5729 xmlnode
*xn_activity
;
5730 xmlnode
*xn_meeting_subject
;
5731 xmlnode
*xn_meeting_location
;
5732 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
5734 xn_node
= xmlnode_get_child(xn_category
, "state");
5735 if (!xn_node
) continue;
5736 xn_availability
= xmlnode_get_child(xn_node
, "availability");
5737 if (!xn_availability
) continue;
5738 xn_activity
= xmlnode_get_child(xn_node
, "activity");
5739 xn_meeting_subject
= xmlnode_get_child(xn_node
, "meetingSubject");
5740 xn_meeting_location
= xmlnode_get_child(xn_node
, "meetingLocation");
5742 data
= xmlnode_get_data(xn_availability
);
5743 availability
= atoi(data
);
5746 /* activity, meeting_subject, meeting_location */
5751 g_free(sbuddy
->activity
);
5752 sbuddy
->activity
= NULL
;
5754 const char *token
= xmlnode_get_attrib(xn_activity
, "token");
5755 xmlnode
*xn_custom
= xmlnode_get_child(xn_activity
, "custom");
5758 if (!is_empty(token
)) {
5759 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
5761 /* from custom element */
5763 char *custom
= xmlnode_get_data(xn_custom
);
5765 if (!is_empty(custom
)) {
5766 sbuddy
->activity
= custom
;
5772 /* meeting_subject */
5773 g_free(sbuddy
->meeting_subject
);
5774 sbuddy
->meeting_subject
= NULL
;
5775 if (xn_meeting_subject
) {
5776 char *meeting_subject
= xmlnode_get_data(xn_meeting_subject
);
5778 if (!is_empty(meeting_subject
)) {
5779 sbuddy
->meeting_subject
= meeting_subject
;
5780 meeting_subject
= NULL
;
5782 g_free(meeting_subject
);
5784 /* meeting_location */
5785 g_free(sbuddy
->meeting_location
);
5786 sbuddy
->meeting_location
= NULL
;
5787 if (xn_meeting_location
) {
5788 char *meeting_location
= xmlnode_get_data(xn_meeting_location
);
5790 if (!is_empty(meeting_location
)) {
5791 sbuddy
->meeting_location
= meeting_location
;
5792 meeting_location
= NULL
;
5794 g_free(meeting_location
);
5797 status
= sipe_get_status_by_availability(availability
, &tmp
);
5798 if (sbuddy
->activity
&& tmp
) {
5799 char *tmp2
= sbuddy
->activity
;
5801 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
5805 sbuddy
->activity
= tmp
;
5809 do_update_status
= TRUE
;
5812 else if(!strcmp(attrVar
, "calendarData"))
5814 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
5815 xmlnode
*xn_free_busy
= xmlnode_get_descendant(xn_category
, "calendarData", "freeBusy", NULL
);
5816 xmlnode
*xn_working_hours
= xmlnode_get_descendant(xn_category
, "calendarData", "WorkingHours", NULL
);
5818 if (sbuddy
&& xn_free_busy
) {
5819 if (!has_free_busy_cleaned
) {
5820 has_free_busy_cleaned
= TRUE
;
5822 g_free(sbuddy
->cal_start_time
);
5823 sbuddy
->cal_start_time
= NULL
;
5825 g_free(sbuddy
->cal_free_busy_base64
);
5826 sbuddy
->cal_free_busy_base64
= NULL
;
5828 g_free(sbuddy
->cal_free_busy
);
5829 sbuddy
->cal_free_busy
= NULL
;
5831 sbuddy
->cal_free_busy_published
= publish_time
;
5834 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
5835 g_free(sbuddy
->cal_start_time
);
5836 sbuddy
->cal_start_time
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
5838 sbuddy
->cal_granularity
= !g_ascii_strcasecmp(xmlnode_get_attrib(xn_free_busy
, "granularity"), "PT15M") ?
5841 g_free(sbuddy
->cal_free_busy_base64
);
5842 sbuddy
->cal_free_busy_base64
= xmlnode_get_data(xn_free_busy
);
5844 g_free(sbuddy
->cal_free_busy
);
5845 sbuddy
->cal_free_busy
= NULL
;
5847 sbuddy
->cal_free_busy_published
= publish_time
;
5849 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
);
5853 if (sbuddy
&& xn_working_hours
) {
5854 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
5859 if (do_update_status
) {
5860 if (!status
) { /* no status category in this update, using contact's current status */
5861 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
5862 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
5863 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
5864 status
= purple_status_get_id(pstatus
);
5867 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", status
);
5868 sipe_got_user_status(sip
, uri
, status
);
5871 xmlnode_free(xn_categories
);
5874 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
5876 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
5877 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
5878 payload
->host
= g_strdup(host
);
5879 payload
->buddies
= server
;
5880 sipe_subscribe_presence_batched_routed(sip
, payload
);
5881 sipe_subscribe_presence_batched_routed_free(payload
);
5884 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5887 xmlnode
*xn_resource
;
5888 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
5893 xn_list
= xmlnode_from_str(data
, len
);
5895 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
5897 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
5899 const char *uri
, *state
;
5900 xmlnode
*xn_instance
;
5902 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
5903 if (!xn_instance
) continue;
5905 uri
= xmlnode_get_attrib(xn_resource
, "uri");
5906 state
= xmlnode_get_attrib(xn_instance
, "state");
5907 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
5909 if (strstr(state
, "resubscribe")) {
5910 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
5912 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
5913 gchar
*user
= g_strdup(uri
);
5914 host
= g_strdup(poolFqdn
);
5915 server
= g_hash_table_lookup(servers
, host
);
5916 server
= g_slist_append(server
, user
);
5917 g_hash_table_insert(servers
, host
, server
);
5919 sipe_subscribe_presence_single(sip
, (void *) uri
);
5924 /* Send out any deferred poolFqdn subscriptions */
5925 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
5926 g_hash_table_destroy(servers
);
5928 xmlnode_free(xn_list
);
5931 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5935 gchar
*activity
= NULL
;
5937 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
5938 gboolean isonline
= FALSE
;
5939 xmlnode
*display_name_node
;
5941 pidf
= xmlnode_from_str(data
, len
);
5943 purple_debug_info("sipe", "process_incoming_notify_pidf: no parseable pidf:%s\n",data
);
5947 uri
= sip_uri(xmlnode_get_attrib(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
5949 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
5951 if ((status
= xmlnode_get_child(tuple
, "status"))) {
5952 basicstatus
= xmlnode_get_child(status
, "basic");
5957 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic found\n");
5962 getbasic
= xmlnode_get_data(basicstatus
);
5964 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic data found\n");
5969 purple_debug_info("sipe", "process_incoming_notify_pidf: basic-status(%s)\n", getbasic
);
5970 if (strstr(getbasic
, "open")) {
5975 display_name_node
= xmlnode_get_child(pidf
, "display-name");
5976 if (display_name_node
) {
5977 char * display_name
= xmlnode_get_data(display_name_node
);
5979 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5980 g_free(display_name
);
5983 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
5984 if ((status
= xmlnode_get_child(tuple
, "status"))) {
5985 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
5986 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
5987 activity
= xmlnode_get_data(basicstatus
);
5988 purple_debug_info("sipe", "process_incoming_notify_pidf: activity(%s)\n", activity
);
5995 const gchar
* status_id
= NULL
;
5997 if (!strcmp(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
5998 status_id
= SIPE_STATUS_ID_BUSY
;
5999 } else if (!strcmp(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
6000 status_id
= SIPE_STATUS_ID_AWAY
;
6005 status_id
= SIPE_STATUS_ID_AVAILABLE
;
6008 purple_debug_info("sipe", "process_incoming_notify_pidf: status_id(%s)\n", status_id
);
6009 sipe_got_user_status(sip
, uri
, status_id
);
6011 sipe_got_user_status(sip
, uri
, SIPE_STATUS_ID_OFFLINE
);
6021 sipe_user_info_has_updated(struct sipe_account_data
*sip
,
6022 xmlnode
*xn_userinfo
)
6024 if (sip
->user_info
) {
6025 xmlnode_free(sip
->user_info
);
6027 sip
->user_info
= xmlnode_copy(xn_userinfo
);
6029 /* Publish initial state if not yet.
6030 * Assuming this happens on initial responce to self subscription
6031 * so we've already updated our UserInfo.
6033 if (!sip
->initial_state_published
) {
6034 send_presence_soap(sip
, FALSE
);
6036 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
6040 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6042 char *activity
= NULL
;
6044 const char *status_id
= NULL
;
6047 char *self_uri
= sip_uri_self(sip
);
6050 const char *device_name
= NULL
;
6051 const char *cal_start_time
= NULL
;
6052 const char *cal_granularity
= NULL
;
6053 char *cal_free_busy_base64
= NULL
;
6054 struct sipe_buddy
*sbuddy
;
6056 xmlnode
*xn_presentity
;
6057 xmlnode
*xn_availability
;
6058 xmlnode
*xn_activity
;
6059 xmlnode
*xn_display_name
;
6061 xmlnode
*xn_phone_number
;
6062 xmlnode
*xn_userinfo
;
6066 xmlnode
*xn_contact
;
6068 char *free_activity
;
6070 const char *user_avail_nil
;
6072 time_t user_avail_since
= 0;
6073 time_t activity_since
= 0;
6075 /* fix for Reuters environment on Linux */
6076 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
6078 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
6079 xn_presentity
= xmlnode_from_str(tmp_data
, strlen(tmp_data
));
6082 xn_presentity
= xmlnode_from_str(data
, len
);
6085 xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
6086 xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
6087 xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
6088 xn_email
= xmlnode_get_child(xn_presentity
, "email");
6089 xn_phone_number
= xmlnode_get_child(xn_presentity
, "phoneNumber");
6090 xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
6091 xn_oof
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "oof") : NULL
;
6092 xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
): NULL
;
6093 user_avail
= xn_state
? xmlnode_get_int_attrib(xn_state
, "avail", 0) : 0;
6094 user_avail_since
= xn_state
? sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since")) : 0;
6095 user_avail_nil
= xn_state
? xmlnode_get_attrib(xn_state
, "nil") : NULL
;
6096 xn_contact
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "contact") : NULL
;
6097 xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
6098 note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
6100 if (user_avail_nil
&& !strcmp(user_avail_nil
, "true")) { /* null-ed */
6102 user_avail_since
= 0;
6105 free_activity
= NULL
;
6107 name
= xmlnode_get_attrib(xn_presentity
, "uri"); /* without 'sip:' prefix */
6108 uri
= sip_uri_from_name(name
);
6109 avl
= xmlnode_get_int_attrib(xn_availability
, "aggregate", 0);
6110 epid
= xmlnode_get_attrib(xn_availability
, "epid");
6111 act
= xmlnode_get_int_attrib(xn_activity
, "aggregate", 0);
6113 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
6114 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
6115 if (user_avail
> res_avail
) {
6116 res_avail
= user_avail
;
6117 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
6120 if (xn_display_name
) {
6121 char *display_name
= g_strdup(xmlnode_get_attrib(xn_display_name
, "displayName"));
6122 char *email
= xn_email
? g_strdup(xmlnode_get_attrib(xn_email
, "email")) : NULL
;
6123 char *phone_label
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "label")) : NULL
;
6124 char *phone_number
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "number")) : NULL
;
6125 char *tel_uri
= sip_to_tel_uri(phone_number
);
6127 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6128 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
6129 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
6130 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, !is_empty(phone_label
) ? phone_label
: phone_number
);
6133 g_free(phone_label
);
6134 g_free(phone_number
);
6136 g_free(display_name
);
6141 for (node
= xmlnode_get_child(xn_contact
, "tel"); node
; node
= xmlnode_get_next_twin(node
))
6143 /* Ex.: <tel type="work">tel:+3222220000</tel> */
6144 const char *phone_type
= xmlnode_get_attrib(node
, "type");
6145 char* phone
= xmlnode_get_data(node
);
6147 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, NULL
);
6153 /* devicePresence */
6154 for (node
= xmlnode_get_descendant(xn_presentity
, "devices", "devicePresence", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
6155 xmlnode
*xn_device_name
;
6156 xmlnode
*xn_calendar_info
;
6161 if (!strcmp(xmlnode_get_attrib(node
, "epid"), epid
)) {
6162 xn_device_name
= xmlnode_get_child(node
, "deviceName");
6163 device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
6167 xn_calendar_info
= xmlnode_get_child(node
, "calendarInfo");
6168 if (xn_calendar_info
) {
6169 const char *cal_start_time_tmp
= xmlnode_get_attrib(xn_calendar_info
, "startTime");
6171 if (cal_start_time
) {
6172 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
6173 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
6175 if (cal_start_time_t_tmp
> cal_start_time_t
) {
6176 cal_start_time
= cal_start_time_tmp
;
6177 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6178 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6180 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
);
6183 cal_start_time
= cal_start_time_tmp
;
6184 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6185 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6187 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
);
6192 xn_state
= xmlnode_get_descendant(node
, "states", "state", NULL
);
6194 int dev_avail
= xmlnode_get_int_attrib(xn_state
, "avail", 0);
6195 time_t dev_avail_since
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since"));
6197 state
= xmlnode_get_data(xn_state
);
6198 if (dev_avail_since
> user_avail_since
&&
6199 dev_avail
>= res_avail
)
6201 res_avail
= dev_avail
;
6202 if (!is_empty(state
))
6204 if (!strcmp(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
6205 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
6206 } else if (!strcmp(state
, "presenting")) {
6207 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
6211 activity_since
= dev_avail_since
;
6213 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
6220 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
6221 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
6225 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
6228 g_free(sbuddy
->activity
);
6229 sbuddy
->activity
= activity
;
6231 sbuddy
->activity_since
= activity_since
;
6233 sbuddy
->user_avail
= user_avail
;
6234 sbuddy
->user_avail_since
= user_avail_since
;
6236 g_free(sbuddy
->note
);
6237 sbuddy
->note
= NULL
;
6238 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
6240 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
6242 g_free(sbuddy
->device_name
);
6243 sbuddy
->device_name
= NULL
;
6244 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
6246 if (!is_empty(cal_free_busy_base64
)) {
6247 g_free(sbuddy
->cal_start_time
);
6248 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
6250 sbuddy
->cal_granularity
= !g_ascii_strcasecmp(cal_granularity
, "PT15M") ? 15 : 0;
6252 g_free(sbuddy
->cal_free_busy_base64
);
6253 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
6255 g_free(sbuddy
->cal_free_busy
);
6256 sbuddy
->cal_free_busy
= NULL
;
6259 sbuddy
->last_non_cal_status_id
= status_id
;
6260 g_free(sbuddy
->last_non_cal_activity
);
6261 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
6263 if (!strcmp(sbuddy
->name
, self_uri
)) {
6264 if (!(sbuddy
->note
&& sip
->note
&& !strcmp(sbuddy
->note
, sip
->note
))) /* not same */
6266 sip
->is_oof_note
= sbuddy
->is_oof_note
;
6269 sip
->note
= g_strdup(sbuddy
->note
);
6271 sip
->note_since
= time(NULL
);
6274 g_free(sip
->status
);
6275 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
6279 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", status_id
);
6280 sipe_got_user_status(sip
, uri
, status_id
);
6282 if (!sip
->ocs2007
&& !strcmp(self_uri
, uri
)) {
6283 sipe_user_info_has_updated(sip
, xn_userinfo
);
6287 xmlnode_free(xn_presentity
);
6292 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
6294 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6296 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
6298 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
6299 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
6301 const char *content
= msg
->body
;
6302 unsigned length
= msg
->bodylen
;
6303 PurpleMimeDocument
*mime
= NULL
;
6305 if (strstr(ctype
, "multipart"))
6307 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6308 const char *content_type
;
6310 mime
= purple_mime_document_parse(doc
);
6311 parts
= purple_mime_document_get_parts(mime
);
6313 content
= purple_mime_part_get_data(parts
->data
);
6314 length
= purple_mime_part_get_length(parts
->data
);
6315 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
6316 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
6318 process_incoming_notify_rlmi_resub(sip
, content
, length
);
6320 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
6322 process_incoming_notify_msrtc(sip
, content
, length
);
6326 process_incoming_notify_rlmi(sip
, content
, length
);
6328 parts
= parts
->next
;
6334 purple_mime_document_free(mime
);
6337 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
6339 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
6341 else if(strstr(ctype
, "application/rlmi+xml"))
6343 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
6346 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
6348 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
6352 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
6356 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
6358 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6359 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6361 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
6364 strstr(ctype
, "multipart") &&
6365 (strstr(ctype
, "application/rlmi+xml") ||
6366 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
6367 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6368 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
6369 GList
*parts
= purple_mime_document_get_parts(mime
);
6370 GSList
*buddies
= NULL
;
6371 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6374 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
6375 purple_mime_part_get_length(parts
->data
));
6377 if (strcmp(xml
->name
, "list")) {
6378 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
6380 buddies
= g_slist_append(buddies
, uri
);
6384 parts
= parts
->next
;
6387 if (mime
) purple_mime_document_free(mime
);
6389 payload
->host
= g_strdup(who
);
6390 payload
->buddies
= buddies
;
6391 sipe_schedule_action(action_name
, timeout
,
6392 sipe_subscribe_presence_batched_routed
,
6393 sipe_subscribe_presence_batched_routed_free
,
6395 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
6398 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6399 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
6401 g_free(action_name
);
6405 * Dispatcher for all incoming subscription information
6406 * whether it comes from NOTIFY, BENOTIFY requests or
6407 * piggy-backed to subscription's OK responce.
6409 * @param request whether initiated from BE/NOTIFY request or OK-response message.
6410 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
6412 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
6414 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
6415 gchar
*event
= sipmsg_find_header(msg
, "Event");
6416 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
6420 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n",
6422 tmp
= fix_newlines(msg
->body
));
6424 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n", subscription_state
? subscription_state
: "");
6426 /* implicit subscriptions */
6427 if (content_type
&& purple_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
6428 sipe_process_imdn(sip
, msg
);
6433 const gchar
*expires_header
;
6434 expires_header
= sipmsg_find_header(msg
, "Expires");
6435 timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
6436 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n", timeout
);
6437 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
; // 2 min ahead of expiration
6440 /* for one off subscriptions (send with Expire: 0) */
6441 if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning-v2"))
6443 sipe_process_provisioning_v2(sip
, msg
);
6445 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning"))
6447 sipe_process_provisioning(sip
, msg
);
6450 if (!subscription_state
|| strstr(subscription_state
, "active"))
6452 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
6454 sipe_process_presence(sip
, msg
);
6456 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
6458 sipe_process_roaming_contacts(sip
, msg
);
6460 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self"))
6462 sipe_process_roaming_self(sip
, msg
);
6464 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
6466 sipe_process_roaming_acl(sip
, msg
);
6468 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
6470 sipe_process_presence_wpending(sip
, msg
);
6472 else if (event
&& !g_ascii_strcasecmp(event
, "conference"))
6474 sipe_process_conference(sip
, msg
);
6478 /* The server sends status 'terminated' */
6479 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
6480 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
6481 gchar
*key
= sipe_get_subscription_key(event
, who
);
6483 purple_debug_info("sipe", "process_incoming_notify: server says that subscription to %s was terminated.\n", who
);
6486 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
6487 g_hash_table_remove(sip
->subscriptions
, key
);
6488 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
6494 if (timeout
&& event
) {// For LSC 2005 and OCS 2007
6495 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
6496 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
6498 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
6499 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_contacts, NULL, sip, msg);
6500 g_free(action_name);
6502 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
6503 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
6505 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
6506 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_acl, NULL, sip, msg);
6507 g_free(action_name);
6510 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
6511 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
6513 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
6514 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
6515 g_free(action_name
);
6517 else if (!g_ascii_strcasecmp(event
, "presence") &&
6518 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
6520 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
6521 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6522 if (sip
->batched_support
) {
6523 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
6526 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6527 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
, timeout
);
6529 g_free(action_name
);
6534 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
6536 sipe_process_registration_notify(sip
, msg
);
6539 /* The client responses on received a NOTIFY message */
6540 if (request
&& !benotify
)
6542 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
6547 * Whether user manually changed status or
6548 * it was changed automatically due to user
6549 * became inactive/active again
6552 sipe_is_user_state(struct sipe_account_data
*sip
)
6555 time_t now
= time(NULL
);
6557 purple_debug_info("sipe", "sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
6558 purple_debug_info("sipe", "sipe_is_user_state: now : %s", asctime(localtime(&now
)));
6560 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
6562 purple_debug_info("sipe", "sipe_is_user_state: res = %s\n", res
? "USER" : "MACHINE");
6567 send_presence_soap0(struct sipe_account_data
*sip
,
6568 gboolean do_publish_calendar
,
6569 gboolean do_reset_status
)
6571 struct sipe_ews
* ews
= sip
->ews
;
6572 int availability
= 0;
6577 gchar
*res_note
= NULL
;
6578 gchar
*res_oof
= NULL
;
6579 const gchar
*note_pub
= NULL
;
6580 gchar
*states
= NULL
;
6581 gchar
*calendar_data
= NULL
;
6582 gchar
*epid
= get_epid(sip
);
6583 time_t now
= time(NULL
);
6584 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
6585 const gchar
*oof_note
= ews
? sipe_ews_get_oof_note(ews
) : NULL
;
6586 const char *user_input
;
6587 gboolean pub_oof
= ews
&& oof_note
&& (!sip
->note
|| ews
->updated
> sip
->note_since
);
6589 if (oof_note
&& sip
->note
) {
6590 purple_debug_info("sipe", "ews->oof_start : %s", asctime(localtime(&(ews
->oof_start
))));
6591 purple_debug_info("sipe", "sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
6594 purple_debug_info("sipe", "sip->note : %s", sip
->note
? sip
->note
: "");
6596 if (!sip
->initial_state_published
||
6599 g_free(sip
->status
);
6600 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
6603 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
6607 note_pub
= oof_note
;
6608 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
6609 ews
->published
= TRUE
;
6610 } else if (sip
->note
) {
6611 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in ews already */
6614 sip
->is_oof_note
= FALSE
;
6615 sip
->note_since
= 0;
6617 note_pub
= sip
->note
;
6618 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
6624 /* to protocol internal plain text format */
6625 tmp
= purple_markup_strip_html(note_pub
);
6626 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
6631 if (!do_reset_status
) {
6632 if (sipe_is_user_state(sip
) && !do_publish_calendar
&& sip
->initial_state_published
)
6634 gchar
*activity_token
= NULL
;
6635 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
6637 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
6642 g_free(activity_token
);
6644 else /* preserve existing publication */
6647 if (sip
->user_info
&& (xn_states
= xmlnode_get_child(sip
->user_info
, "states"))) {
6648 states
= xmlnode_to_str(xn_states
, NULL
);
6649 /* this is a hack-around to remove added newline after inner element,
6650 * state in this case, where it shouldn't be.
6651 * After several use of xmlnode_to_str, amount of added newlines
6652 * grows significantly.
6654 purple_str_strip_char(states
, '\n');
6655 //purple_str_strip_char(states, '\r');
6659 /* do nothing - then User state will be erased */
6661 sip
->initial_state_published
= TRUE
;
6664 if (ews
&& (!is_empty(ews
->legacy_dn
) || !is_empty(ews
->email
)) && ews
->fb_start
&& !is_empty(ews
->free_busy
))
6666 char *fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
6667 char *free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
6668 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
6669 !is_empty(ews
->legacy_dn
) ? ews
->legacy_dn
: ews
->email
,
6672 g_free(fb_start_str
);
6673 g_free(free_busy_base64
);
6676 user_input
= !sipe_is_user_state(sip
) && sip
->status
!= SIPE_STATUS_ID_AVAILABLE
? "idle" : "active";
6678 /* forming resulting XML */
6679 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
6683 (tmp
= g_ascii_strup(sipe_get_host_name(), -1)),
6684 res_note
? res_note
: "",
6685 res_oof
? res_oof
: "",
6686 states
? states
: "",
6687 calendar_data
? calendar_data
: "",
6696 g_free(calendar_data
);
6698 send_soap_request(sip
, body
);
6701 g_free(since_time_str
);
6706 send_presence_soap(struct sipe_account_data
*sip
,
6707 gboolean do_publish_calendar
)
6709 return send_presence_soap0(sip
, do_publish_calendar
, FALSE
);
6714 process_send_presence_category_publish_response(struct sipe_account_data
*sip
,
6716 struct transaction
*trans
)
6718 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
6720 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
6726 gboolean has_device_publication
= FALSE
;
6728 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
6730 /* test if version mismatch fault */
6731 fault_code
= xmlnode_get_data(xmlnode_get_child(xml
, "Faultcode"));
6732 if (strcmp(fault_code
, "Client.BadCall.WrongDelta")) {
6733 purple_debug_info("sipe", "process_send_presence_category_publish_response: unsupported fault code:%s returning.\n", fault_code
);
6740 /* accumulating information about faulty versions */
6741 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
6742 for (node
= xmlnode_get_descendant(xml
, "details", "operation", NULL
);
6744 node
= xmlnode_get_next_twin(node
))
6746 const gchar
*index
= xmlnode_get_attrib(node
, "index");
6747 const gchar
*curVersion
= xmlnode_get_attrib(node
, "curVersion");
6749 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
6750 purple_debug_info("sipe", "fault added: index:%s curVersion:%s\n", index
, curVersion
);
6754 /* here we are parsing own request to figure out what publication
6755 * referensed here only by index went wrong
6757 xml
= xmlnode_from_str(trans
->msg
->body
, trans
->msg
->bodylen
);
6760 for (node
= xmlnode_get_descendant(xml
, "publications", "publication", NULL
),
6761 index_our
= 1; /* starts with 1 - our first publication */
6763 node
= xmlnode_get_next_twin(node
), index_our
++)
6765 gchar
*idx
= g_strdup_printf("%d", index_our
);
6766 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
6767 const gchar
*categoryName
= xmlnode_get_attrib(node
, "categoryName");
6770 if (!strcmp("device", categoryName
)) {
6771 has_device_publication
= TRUE
;
6774 if (curVersion
) { /* fault exist on this index */
6775 const gchar
*container
= xmlnode_get_attrib(node
, "container");
6776 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
6777 /* key is <category><instance><container> */
6778 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
6779 struct sipe_publication
*publication
=
6780 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, categoryName
), key
);
6782 purple_debug_info("sipe", "key is %s\n", key
);
6785 purple_debug_info("sipe", "Updating %s with version %s. Was %d before.\n",
6786 key
, curVersion
, publication
->version
);
6787 /* updating publication's version to the correct one */
6788 publication
->version
= atoi(curVersion
);
6794 g_hash_table_destroy(faults
);
6796 /* rebublishing with right versions */
6797 if (has_device_publication
) {
6798 send_publish_category_initial(sip
);
6800 send_presence_status(sip
);
6807 * Returns 'device' XML part for publication.
6808 * Must be g_free'd after use.
6811 sipe_publish_get_category_device(struct sipe_account_data
*sip
)
6815 gchar
*epid
= get_epid(sip
);
6816 gchar
*uuid
= generateUUIDfromEPID(epid
);
6817 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
6818 /* key is <category><instance><container> */
6819 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
6820 struct sipe_publication
*publication
=
6821 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
6826 uri
= sip_uri_self(sip
);
6827 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
6829 publication
? publication
->version
: 0,
6832 "00:00:00+01:00", /* @TODO make timezone real*/
6833 sipe_get_host_name()
6843 * A service method - use
6844 * - send_publish_get_category_state_machine and
6845 * - send_publish_get_category_state_user instead.
6846 * Must be g_free'd after use.
6849 sipe_publish_get_category_state(struct sipe_account_data
*sip
,
6850 gboolean is_user_state
)
6852 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
6853 guint instance
= is_user_state
? sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
) :
6854 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
6855 /* key is <category><instance><container> */
6856 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
6857 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
6858 struct sipe_publication
*publication_2
=
6859 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
6860 struct sipe_publication
*publication_3
=
6861 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
6866 if (publication_2
&& (publication_2
->availability
== availability
))
6868 purple_debug_info("sipe", "sipe_publish_get_category_state: state has NOT changed. Exiting.\n");
6869 return NULL
; /* nothing to update */
6872 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
6874 publication_2
? publication_2
->version
: 0,
6877 publication_3
? publication_3
->version
: 0,
6882 * Only Busy and OOF calendar event are published.
6883 * Different instances are used for that.
6885 * Must be g_free'd after use.
6888 sipe_publish_get_category_state_calendar(struct sipe_account_data
*sip
,
6889 struct sipe_cal_event
*event
,
6893 gchar
*start_time_str
;
6894 int availability
= 0;
6896 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
6897 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
) :
6898 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
6900 /* key is <category><instance><container> */
6901 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
6902 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
6903 struct sipe_publication
*publication_2
=
6904 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
6905 struct sipe_publication
*publication_3
=
6906 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
6911 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
6912 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
6913 "Exiting as no publication and no event for cal_satus:%d\n", cal_satus
);
6919 (publication_3
->availability
== availability
) &&
6920 !strcmp(publication_3
->cal_event_hash
, sipe_cal_event_hash(event
)))
6922 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
6923 "cal state has NOT changed for cal_satus:%d. Exiting.\n", cal_satus
);
6924 return NULL
; /* nothing to update */
6928 (event
->cal_status
== SIPE_CAL_BUSY
||
6929 event
->cal_status
== SIPE_CAL_OOF
))
6931 gchar
*availability_xml_str
= NULL
;
6932 gchar
*activity_xml_str
= NULL
;
6934 if (event
->cal_status
== SIPE_CAL_BUSY
) {
6935 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
6938 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
6939 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
6940 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
6941 "minAvailability=\"6500\"",
6942 "maxAvailability=\"8999\"");
6943 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
6944 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
6945 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
6946 "minAvailability=\"12000\"",
6949 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
6951 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
6953 publication_2
? publication_2
->version
: 0,
6956 availability_xml_str
? availability_xml_str
: "",
6957 activity_xml_str
? activity_xml_str
: "",
6958 event
->subject
? event
->subject
: "",
6959 event
->location
? event
->location
: "",
6962 publication_3
? publication_3
->version
: 0,
6965 availability_xml_str
? availability_xml_str
: "",
6966 activity_xml_str
? activity_xml_str
: "",
6967 event
->subject
? event
->subject
: "",
6968 event
->location
? event
->location
: ""
6970 g_free(start_time_str
);
6971 g_free(availability_xml_str
);
6972 g_free(activity_xml_str
);
6975 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
6977 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
6979 publication_2
? publication_2
->version
: 0,
6982 publication_3
? publication_3
->version
: 0
6990 * Returns 'machineState' XML part for publication.
6991 * Must be g_free'd after use.
6994 sipe_publish_get_category_state_machine(struct sipe_account_data
*sip
)
6996 return sipe_publish_get_category_state(sip
, FALSE
);
7000 * Returns 'userState' XML part for publication.
7001 * Must be g_free'd after use.
7004 sipe_publish_get_category_state_user(struct sipe_account_data
*sip
)
7006 return sipe_publish_get_category_state(sip
, TRUE
);
7010 * Compares two strings even in case both are NULL/empty
7013 sipe_is_equal(const char* n1
, const char* n2
) {
7014 return ((!n1
|| !strlen(n1
)) && (!n2
|| !strlen(n2
))) /* both empty */
7015 || (n1
&& n2
&& !strcmp(n1
, n2
)); /* or not empty and equal */
7019 * Returns 'note' XML part for publication.
7020 * Must be g_free'd after use.
7022 * Protocol format for Note is plain text.
7024 * @param note a note in Sipe internal HTML format
7025 * @param note_type either personal or OOF
7028 sipe_publish_get_category_note(struct sipe_account_data
*sip
,
7029 const char *note
, /* html */
7030 const char *note_type
,
7034 guint instance
= !strcmp("OOF", note_type
) ? sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
) : 0;
7035 /* key is <category><instance><container> */
7036 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
7037 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
7038 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
7040 struct sipe_publication
*publication_note_200
=
7041 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
7042 struct sipe_publication
*publication_note_300
=
7043 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
7044 struct sipe_publication
*publication_note_400
=
7045 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
7047 char *tmp
= note
? purple_markup_strip_html(note
) : NULL
;
7048 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
7049 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
7050 char *res
, *tmp1
, *tmp2
, *tmp3
;
7051 char *start_time_attr
;
7052 char *end_time_attr
;
7055 g_free(key_note_200
);
7056 g_free(key_note_300
);
7057 g_free(key_note_400
);
7059 /* we even need to republish empty note */
7060 if (n1
&& n2
&& !strcmp(n1
, n2
))
7062 purple_debug_info("sipe", "sipe_publish_get_category_note: note has NOT changed. Exiting.\n");
7064 return NULL
; /* nothing to update */
7067 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
7069 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
7073 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7076 publication_note_200
? publication_note_200
->version
: 0,
7078 start_time_attr
? start_time_attr
: "",
7079 end_time_attr
? end_time_attr
: "",
7082 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7085 publication_note_300
? publication_note_300
->version
: 0,
7087 start_time_attr
? start_time_attr
: "",
7088 end_time_attr
? end_time_attr
: "",
7091 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7094 publication_note_400
? publication_note_400
->version
: 0,
7096 start_time_attr
? start_time_attr
: "",
7097 end_time_attr
? end_time_attr
: "",
7100 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7104 publication_note_200
? publication_note_200
->version
: 0,
7106 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7110 publication_note_200
? publication_note_200
->version
: 0,
7112 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7116 publication_note_200
? publication_note_200
->version
: 0,
7119 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
7121 g_free(start_time_attr
);
7122 g_free(end_time_attr
);
7132 * Returns 'calendarData' XML part with WorkingHours for publication.
7133 * Must be g_free'd after use.
7136 sipe_publish_get_category_cal_working_hours(struct sipe_account_data
*sip
)
7138 struct sipe_ews
* ews
= sip
->ews
;
7140 /* key is <category><instance><container> */
7141 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
7142 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
7143 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
7144 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
7145 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
7146 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
7148 struct sipe_publication
*publication_cal_1
=
7149 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7150 struct sipe_publication
*publication_cal_100
=
7151 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7152 struct sipe_publication
*publication_cal_200
=
7153 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7154 struct sipe_publication
*publication_cal_300
=
7155 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7156 struct sipe_publication
*publication_cal_400
=
7157 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7158 struct sipe_publication
*publication_cal_32000
=
7159 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7161 const char *n1
= ews
? ews
->working_hours_xml_str
: NULL
;
7162 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
7165 g_free(key_cal_100
);
7166 g_free(key_cal_200
);
7167 g_free(key_cal_300
);
7168 g_free(key_cal_400
);
7169 g_free(key_cal_32000
);
7171 if (!ews
|| is_empty(ews
->email
) || is_empty(ews
->working_hours_xml_str
)) {
7172 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: no data to publish, exiting\n");
7176 if (sipe_is_equal(n1
, n2
))
7178 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.\n");
7179 return NULL
; /* nothing to update */
7182 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
7184 publication_cal_1
? publication_cal_1
->version
: 0,
7186 ews
->working_hours_xml_str
,
7188 publication_cal_100
? publication_cal_100
->version
: 0,
7190 publication_cal_200
? publication_cal_200
->version
: 0,
7192 ews
->working_hours_xml_str
,
7194 publication_cal_300
? publication_cal_300
->version
: 0,
7196 ews
->working_hours_xml_str
,
7197 /* 400 - Personal */
7198 publication_cal_400
? publication_cal_400
->version
: 0,
7200 ews
->working_hours_xml_str
,
7201 /* 32000 - Blocked */
7202 publication_cal_32000
? publication_cal_32000
->version
: 0
7207 * Returns 'calendarData' XML part with FreeBusy for publication.
7208 * Must be g_free'd after use.
7211 sipe_publish_get_category_cal_free_busy(struct sipe_account_data
*sip
)
7213 struct sipe_ews
* ews
= sip
->ews
;
7214 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
7216 char *free_busy_base64
;
7221 /* key is <category><instance><container> */
7222 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
7223 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
7224 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
7225 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
7226 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
7227 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
7229 struct sipe_publication
*publication_cal_1
=
7230 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7231 struct sipe_publication
*publication_cal_100
=
7232 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7233 struct sipe_publication
*publication_cal_200
=
7234 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7235 struct sipe_publication
*publication_cal_300
=
7236 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7237 struct sipe_publication
*publication_cal_400
=
7238 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7239 struct sipe_publication
*publication_cal_32000
=
7240 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7243 g_free(key_cal_100
);
7244 g_free(key_cal_200
);
7245 g_free(key_cal_300
);
7246 g_free(key_cal_400
);
7247 g_free(key_cal_32000
);
7249 if (!ews
|| is_empty(ews
->email
) || !ews
->fb_start
|| is_empty(ews
->free_busy
)) {
7250 purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: no data to publish, exiting\n");
7254 fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
7255 free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
7257 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
7258 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
7260 /* we will rebuplish the same data to refresh publication time,
7261 * so if data from multiple sources, most recent will be choosen
7263 //if (sipe_is_equal(st, fb_start_str) && sipe_is_equal(fb, free_busy_base64))
7265 // purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.\n");
7266 // g_free(fb_start_str);
7267 // g_free(free_busy_base64);
7268 // return NULL; /* nothing to update */
7271 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
7274 publication_cal_1
? publication_cal_1
->version
: 0,
7277 publication_cal_100
? publication_cal_100
->version
: 0,
7280 publication_cal_200
? publication_cal_200
->version
: 0,
7286 publication_cal_300
? publication_cal_300
->version
: 0,
7290 /* 400 - Personal */
7292 publication_cal_400
? publication_cal_400
->version
: 0,
7296 /* 32000 - Blocked */
7298 publication_cal_32000
? publication_cal_32000
->version
: 0
7301 g_free(fb_start_str
);
7302 g_free(free_busy_base64
);
7306 static void send_presence_publish(struct sipe_account_data
*sip
, const char *publications
)
7313 uri
= sip_uri_self(sip
);
7314 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
7318 tmp
= get_contact(sip
);
7319 hdr
= g_strdup_printf("Contact: %s\r\n"
7320 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
7322 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
7331 send_publish_category_initial(struct sipe_account_data
*sip
)
7333 gchar
*pub_device
= sipe_publish_get_category_device(sip
);
7335 gchar
*publications
;
7337 g_free(sip
->status
);
7338 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
7340 pub_machine
= sipe_publish_get_category_state_machine(sip
);
7341 publications
= g_strdup_printf("%s%s",
7343 pub_machine
? pub_machine
: "");
7345 g_free(pub_machine
);
7347 send_presence_publish(sip
, publications
);
7348 g_free(publications
);
7352 send_presence_category_publish(struct sipe_account_data
*sip
)
7354 gchar
*pub_state
= sipe_is_user_state(sip
) ?
7355 sipe_publish_get_category_state_user(sip
) :
7356 sipe_publish_get_category_state_machine(sip
);
7357 gchar
*pub_note
= sipe_publish_get_category_note(sip
,
7359 sip
->is_oof_note
? "OOF" : "personal",
7362 gchar
*publications
;
7364 if (!pub_state
&& !pub_note
) {
7365 purple_debug_info("sipe", "send_presence_category_publish: nothing has changed. Exiting.\n");
7369 publications
= g_strdup_printf("%s%s",
7370 pub_state
? pub_state
: "",
7371 pub_note
? pub_note
: "");
7376 send_presence_publish(sip
, publications
);
7377 g_free(publications
);
7381 * Publishes self status
7382 * based on own calendar information.
7387 publish_calendar_status_self(struct sipe_account_data
*sip
)
7389 struct sipe_cal_event
* event
= NULL
;
7390 gchar
*pub_cal_working_hours
= NULL
;
7391 gchar
*pub_cal_free_busy
= NULL
;
7392 gchar
*pub_calendar
= NULL
;
7393 gchar
*pub_calendar2
= NULL
;
7394 gchar
*pub_oof_note
= NULL
;
7395 const gchar
*oof_note
;
7396 time_t oof_start
= 0;
7400 purple_debug_info("sipe", "publish_calendar_status_self() no calendar data.\n");
7404 purple_debug_info("sipe", "publish_calendar_status_self() started.\n");
7405 if (sip
->ews
->cal_events
) {
7406 event
= sipe_cal_get_event(sip
->ews
->cal_events
, time(NULL
));
7410 purple_debug_info("sipe", "publish_calendar_status_self: current event is NULL\n");
7412 char *desc
= sipe_cal_event_describe(event
);
7413 purple_debug_info("sipe", "publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
7419 OOF publish, Busy clean
7421 OOF clean, Busy publish
7423 OOF clean, Busy clean
7425 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
7426 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_OOF
);
7427 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7428 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
7429 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7430 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7432 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7433 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7436 oof_note
= sipe_ews_get_oof_note(sip
->ews
);
7437 if (!strcmp("Scheduled", sip
->ews
->oof_state
)) {
7438 oof_start
= sip
->ews
->oof_start
;
7439 oof_end
= sip
->ews
->oof_end
;
7441 pub_oof_note
= sipe_publish_get_category_note(sip
, oof_note
, "OOF", oof_start
, oof_end
);
7443 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sip
);
7444 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sip
);
7446 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
7447 purple_debug_info("sipe", "publish_calendar_status_self: nothing has changed.\n");
7449 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
7450 pub_cal_working_hours
? pub_cal_working_hours
: "",
7451 pub_cal_free_busy
? pub_cal_free_busy
: "",
7452 pub_calendar
? pub_calendar
: "",
7453 pub_calendar2
? pub_calendar2
: "",
7454 pub_oof_note
? pub_oof_note
: "");
7456 send_presence_publish(sip
, publications
);
7457 g_free(publications
);
7460 g_free(pub_cal_working_hours
);
7461 g_free(pub_cal_free_busy
);
7462 g_free(pub_calendar
);
7463 g_free(pub_calendar2
);
7464 g_free(pub_oof_note
);
7466 /* repeat scheduling */
7467 sipe_sched_calendar_status_self_publish(sip
, time(NULL
));
7470 static void send_presence_status(struct sipe_account_data
*sip
)
7472 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
7474 if (!status
) return;
7476 purple_debug_info("sipe", "send_presence_status: status: %s (%s)\n",
7477 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
7478 sipe_is_user_state(sip
) ? "USER" : "MACHINE");
7481 send_presence_category_publish(sip
);
7483 send_presence_soap(sip
, FALSE
);
7487 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
7489 gboolean found
= FALSE
;
7490 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
7491 if (msg
->response
== 0) { /* request */
7492 if (!strcmp(msg
->method
, "MESSAGE")) {
7493 process_incoming_message(sip
, msg
);
7495 } else if (!strcmp(msg
->method
, "NOTIFY")) {
7496 purple_debug_info("sipe","send->process_incoming_notify\n");
7497 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
7499 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
7500 purple_debug_info("sipe","send->process_incoming_benotify\n");
7501 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
7503 } else if (!strcmp(msg
->method
, "INVITE")) {
7504 process_incoming_invite(sip
, msg
);
7506 } else if (!strcmp(msg
->method
, "REFER")) {
7507 process_incoming_refer(sip
, msg
);
7509 } else if (!strcmp(msg
->method
, "OPTIONS")) {
7510 process_incoming_options(sip
, msg
);
7512 } else if (!strcmp(msg
->method
, "INFO")) {
7513 process_incoming_info(sip
, msg
);
7515 } else if (!strcmp(msg
->method
, "ACK")) {
7516 // ACK's don't need any response
7518 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
7519 // LCS 2005 sends us these - just respond 200 OK
7521 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
7522 } else if (!strcmp(msg
->method
, "BYE")) {
7523 process_incoming_bye(sip
, msg
);
7526 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
7528 } else { /* response */
7529 struct transaction
*trans
= transactions_find(sip
, msg
);
7531 if (msg
->response
== 407) {
7532 gchar
*resend
, *auth
, *ptmp
;
7534 if (sip
->proxy
.retries
> 30) return;
7535 sip
->proxy
.retries
++;
7536 /* do proxy authentication */
7538 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
7540 fill_auth(ptmp
, &sip
->proxy
);
7541 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
7542 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
7543 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
7545 resend
= sipmsg_to_string(trans
->msg
);
7546 /* resend request */
7547 sendout_pkt(sip
->gc
, resend
);
7550 if (msg
->response
< 200) {
7551 /* ignore provisional response */
7552 purple_debug_info("sipe", "got provisional (%d) response, ignoring\n", msg
->response
);
7554 sip
->proxy
.retries
= 0;
7555 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
7556 if (msg
->response
== 401)
7558 sip
->registrar
.retries
++;
7562 sip
->registrar
.retries
= 0;
7564 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\n", sip
->cseq
);
7566 if (msg
->response
== 401) {
7567 gchar
*resend
, *auth
, *ptmp
;
7569 if (sip
->registrar
.retries
> 4) return;
7570 sip
->registrar
.retries
++;
7573 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
7575 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
7578 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
7582 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\n", ptmp
);
7584 fill_auth(ptmp
, &sip
->registrar
);
7585 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
7586 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
7587 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
7589 //sipmsg_remove_header_now(trans->msg, "Authorization");
7590 //sipmsg_add_header(trans->msg, "Authorization", auth);
7592 resend
= sipmsg_to_string(trans
->msg
);
7593 /* resend request */
7594 sendout_pkt(sip
->gc
, resend
);
7599 if (trans
->callback
) {
7600 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\n");
7601 /* call the callback to process response*/
7602 (trans
->callback
)(sip
, msg
, trans
);
7605 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\n", sip
->cseq
);
7606 transactions_remove(sip
, trans
);
7612 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
7616 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
7620 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
7629 /* according to the RFC remove CRLF at the beginning */
7630 while (*cur
== '\r' || *cur
== '\n') {
7633 if (cur
!= conn
->inbuf
) {
7634 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
7635 conn
->inbufused
= strlen(conn
->inbuf
);
7638 /* Received a full Header? */
7639 sip
->processing_input
= TRUE
;
7640 while (sip
->processing_input
&&
7641 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
7642 time_t currtime
= time(NULL
);
7645 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), tmp
= fix_newlines(conn
->inbuf
));
7647 msg
= sipmsg_parse_header(conn
->inbuf
);
7650 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
7651 if (msg
&& restlen
>= msg
->bodylen
) {
7652 dummy
= g_malloc(msg
->bodylen
+ 1);
7653 memcpy(dummy
, cur
, msg
->bodylen
);
7654 dummy
[msg
->bodylen
] = '\0';
7656 cur
+= msg
->bodylen
;
7657 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
7658 conn
->inbufused
= strlen(conn
->inbuf
);
7661 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n", restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
7668 purple_debug_info("sipe", "body:\n%s", msg->body);
7671 // Verify the signature before processing it
7672 if (sip
->registrar
.gssapi_context
) {
7673 struct sipmsg_breakdown msgbd
;
7674 gchar
*signature_input_str
;
7677 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
7678 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
7680 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
7682 if (rspauth
!= NULL
) {
7683 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
7684 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
7685 process_input_message(sip
, msg
);
7687 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
7688 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
7689 sip
->gc
->wants_to_die
= TRUE
;
7691 } else if (msg
->response
== 401) {
7692 purple_connection_error(sip
->gc
, _("Wrong password"));
7693 sip
->gc
->wants_to_die
= TRUE
;
7695 g_free(signature_input_str
);
7698 sipmsg_breakdown_free(&msgbd
);
7700 process_input_message(sip
, msg
);
7707 static void sipe_udp_process(gpointer data
, gint source
,
7708 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
7710 PurpleConnection
*gc
= data
;
7711 struct sipe_account_data
*sip
= gc
->proto_data
;
7714 static char buffer
[65536];
7715 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
7716 time_t currtime
= time(NULL
);
7719 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), buffer
);
7720 msg
= sipmsg_parse_msg(buffer
);
7721 if (msg
) process_input_message(sip
, msg
);
7725 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
7727 struct sipe_account_data
*sip
= gc
->proto_data
;
7728 PurpleSslConnection
*gsc
= sip
->gsc
;
7730 purple_debug_error("sipe", "%s",debug
);
7731 purple_connection_error(gc
, msg
);
7733 /* Invalidate this connection. Next send will open a new one */
7735 connection_remove(sip
, gsc
->fd
);
7736 purple_ssl_close(gsc
);
7742 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
7743 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
7745 PurpleConnection
*gc
= data
;
7746 struct sipe_account_data
*sip
;
7747 struct sip_connection
*conn
;
7749 gboolean firstread
= TRUE
;
7751 /* NOTE: This check *IS* necessary */
7752 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
7753 purple_ssl_close(gsc
);
7757 sip
= gc
->proto_data
;
7758 conn
= connection_find(sip
, gsc
->fd
);
7760 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
7761 gc
->wants_to_die
= TRUE
;
7762 purple_connection_error(gc
, _("Connection not found. Please try to connect again"));
7766 /* Read all available data from the SSL connection */
7768 /* Increase input buffer size as needed */
7769 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
7770 conn
->inbuflen
+= SIMPLE_BUF_INC
;
7771 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
7772 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
7775 /* Try to read as much as there is space left in the buffer */
7776 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
7777 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
7779 if (len
< 0 && errno
== EAGAIN
) {
7780 /* Try again later */
7782 } else if (len
< 0) {
7783 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
7785 } else if (firstread
&& (len
== 0)) {
7786 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
7790 conn
->inbufused
+= len
;
7793 /* Equivalence indicates that there is possibly more data to read */
7794 } while (len
== readlen
);
7796 conn
->inbuf
[conn
->inbufused
] = '\0';
7797 process_input(sip
, conn
);
7801 static void sipe_input_cb(gpointer data
, gint source
,
7802 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
7804 PurpleConnection
*gc
= data
;
7805 struct sipe_account_data
*sip
= gc
->proto_data
;
7807 struct sip_connection
*conn
= connection_find(sip
, source
);
7809 purple_debug_error("sipe", "Connection not found!\n");
7813 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
7814 conn
->inbuflen
+= SIMPLE_BUF_INC
;
7815 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
7818 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
7820 if (len
< 0 && errno
== EAGAIN
)
7822 else if (len
<= 0) {
7823 purple_debug_info("sipe", "sipe_input_cb: read error\n");
7824 connection_remove(sip
, source
);
7825 if (sip
->fd
== source
) sip
->fd
= -1;
7829 conn
->inbufused
+= len
;
7830 conn
->inbuf
[conn
->inbufused
] = '\0';
7832 process_input(sip
, conn
);
7835 /* Callback for new connections on incoming TCP port */
7836 static void sipe_newconn_cb(gpointer data
, gint source
,
7837 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
7839 PurpleConnection
*gc
= data
;
7840 struct sipe_account_data
*sip
= gc
->proto_data
;
7841 struct sip_connection
*conn
;
7843 int newfd
= accept(source
, NULL
, NULL
);
7845 conn
= connection_create(sip
, newfd
);
7847 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
7850 static void login_cb(gpointer data
, gint source
,
7851 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
7853 PurpleConnection
*gc
= data
;
7854 struct sipe_account_data
*sip
;
7855 struct sip_connection
*conn
;
7857 if (!PURPLE_CONNECTION_IS_VALID(gc
))
7865 purple_connection_error(gc
, _("Could not connect"));
7869 sip
= gc
->proto_data
;
7871 sip
->last_keepalive
= time(NULL
);
7873 conn
= connection_create(sip
, source
);
7877 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
7880 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
7881 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
7883 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
7884 if (sip
== NULL
) return;
7889 static guint
sipe_ht_hash_nick(const char *nick
)
7891 char *lc
= g_utf8_strdown(nick
, -1);
7892 guint bucket
= g_str_hash(lc
);
7898 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
7900 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
7903 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
7905 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
7907 sip
->listen_data
= NULL
;
7909 if (listenfd
== -1) {
7910 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
7916 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
7917 sip
->listenfd
= sip
->fd
;
7919 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
7921 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
7925 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
7926 SIPE_UNUSED_PARAMETER
const char *error_message
)
7928 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
7930 sip
->query_data
= NULL
;
7932 if (!hosts
|| !hosts
->data
) {
7933 purple_connection_error(sip
->gc
, _("Could not resolve hostname"));
7937 hosts
= g_slist_remove(hosts
, hosts
->data
);
7938 g_free(sip
->serveraddr
);
7939 sip
->serveraddr
= hosts
->data
;
7940 hosts
= g_slist_remove(hosts
, hosts
->data
);
7942 hosts
= g_slist_remove(hosts
, hosts
->data
);
7943 g_free(hosts
->data
);
7944 hosts
= g_slist_remove(hosts
, hosts
->data
);
7947 /* create socket for incoming connections */
7948 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
7949 sipe_udp_host_resolved_listen_cb
, sip
);
7950 if (sip
->listen_data
== NULL
) {
7951 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
7956 static const struct sipe_service_data
*current_service
= NULL
;
7958 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
7959 PurpleSslErrorType error
,
7962 PurpleConnection
*gc
= data
;
7963 struct sipe_account_data
*sip
;
7965 /* If the connection is already disconnected, we don't need to do anything else */
7966 if (!PURPLE_CONNECTION_IS_VALID(gc
))
7969 sip
= gc
->proto_data
;
7970 current_service
= sip
->service_data
;
7971 if (current_service
) {
7972 purple_debug_info("sipe", "current_service: transport '%s' service '%s'\n",
7973 current_service
->transport
? current_service
->transport
: "NULL",
7974 current_service
->service
? current_service
->service
: "NULL");
7981 case PURPLE_SSL_CONNECT_FAILED
:
7982 purple_connection_error(gc
, _("Connection failed"));
7984 case PURPLE_SSL_HANDSHAKE_FAILED
:
7985 purple_connection_error(gc
, _("SSL handshake failed"));
7987 case PURPLE_SSL_CERTIFICATE_INVALID
:
7988 purple_connection_error(gc
, _("SSL certificate invalid"));
7994 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
7996 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
7997 PurpleProxyConnectData
*connect_data
;
7999 sip
->listen_data
= NULL
;
8001 sip
->listenfd
= listenfd
;
8002 if (sip
->listenfd
== -1) {
8003 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8007 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
8008 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
8009 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
8010 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
8011 sipe_newconn_cb
, sip
->gc
);
8012 purple_debug_info("sipe", "connecting to %s port %d\n",
8013 sip
->realhostname
, sip
->realport
);
8014 /* open tcp connection to the server */
8015 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
8016 sip
->realport
, login_cb
, sip
->gc
);
8018 if (connect_data
== NULL
) {
8019 purple_connection_error(sip
->gc
, _("Could not create socket"));
8023 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
8025 PurpleAccount
*account
= sip
->account
;
8026 PurpleConnection
*gc
= sip
->gc
;
8029 port
= (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
8032 sip
->realhostname
= hostname
;
8033 sip
->realport
= port
;
8035 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
8038 /* TODO: is there a good default grow size? */
8039 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
8040 sip
->txbuf
= purple_circ_buffer_new(0);
8042 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
8044 if (!purple_ssl_is_supported()) {
8045 gc
->wants_to_die
= TRUE
;
8046 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor"));
8050 purple_debug_info("sipe", "using SSL\n");
8052 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
8053 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
8054 if (sip
->gsc
== NULL
) {
8055 purple_connection_error(gc
, _("Could not create SSL context"));
8058 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
8060 purple_debug_info("sipe", "using UDP\n");
8062 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
8063 if (sip
->query_data
== NULL
) {
8064 purple_connection_error(gc
, _("Could not resolve hostname"));
8068 purple_debug_info("sipe", "using TCP\n");
8069 /* create socket for incoming connections */
8070 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
8071 sipe_tcp_connect_listen_cb
, sip
);
8072 if (sip
->listen_data
== NULL
) {
8073 purple_connection_error(gc
, _("Could not create listen socket"));
8079 /* Service list for autodection */
8080 static const struct sipe_service_data service_autodetect
[] = {
8081 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8082 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8083 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8084 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8088 /* Service list for SSL/TLS */
8089 static const struct sipe_service_data service_tls
[] = {
8090 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8091 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8095 /* Service list for TCP */
8096 static const struct sipe_service_data service_tcp
[] = {
8097 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8098 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8102 /* Service list for UDP */
8103 static const struct sipe_service_data service_udp
[] = {
8104 { "sip", "udp", SIPE_TRANSPORT_UDP
},
8108 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
8109 static void resolve_next_service(struct sipe_account_data
*sip
,
8110 const struct sipe_service_data
*start
)
8113 sip
->service_data
= start
;
8115 sip
->service_data
++;
8116 if (sip
->service_data
->service
== NULL
) {
8118 /* Try connecting to the SIP hostname directly */
8119 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
8120 if (sip
->auto_transport
) {
8121 // If SSL is supported, default to using it; OCS servers aren't configured
8122 // by default to accept TCP
8123 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
8124 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8125 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
8128 hostname
= g_strdup(sip
->sipdomain
);
8129 create_connection(sip
, hostname
, 0);
8134 /* Try to resolve next service */
8135 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
8136 sip
->service_data
->transport
,
8141 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
8143 struct sipe_account_data
*sip
= data
;
8145 sip
->srv_query_data
= NULL
;
8147 /* find the host to connect to */
8149 gchar
*hostname
= g_strdup(resp
->hostname
);
8150 int port
= resp
->port
;
8151 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
8155 sip
->transport
= sip
->service_data
->type
;
8157 create_connection(sip
, hostname
, port
);
8159 resolve_next_service(sip
, NULL
);
8163 static void sipe_login(PurpleAccount
*account
)
8165 PurpleConnection
*gc
;
8166 struct sipe_account_data
*sip
;
8167 gchar
**signinname_login
, **userserver
;
8168 const char *transport
;
8171 const char *username
= purple_account_get_username(account
);
8172 gc
= purple_account_get_connection(account
);
8174 purple_debug_info("sipe", "sipe_login: username '%s'\n", username
);
8176 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
8177 gc
->wants_to_die
= TRUE
;
8178 purple_connection_error(gc
, _("SIP Exchange user name contains invalid characters"));
8182 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
8183 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
8184 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
8186 sip
->account
= account
;
8187 sip
->reregister_set
= FALSE
;
8188 sip
->reauthenticate_set
= FALSE
;
8189 sip
->subscribed
= FALSE
;
8190 sip
->subscribed_buddies
= FALSE
;
8191 sip
->initial_state_published
= FALSE
;
8193 /* username format: <username>,[<optional login>] */
8194 signinname_login
= g_strsplit(username
, ",", 2);
8195 purple_debug_info("sipe", "sipe_login: signinname[0] '%s'\n", signinname_login
[0]);
8197 /* ensure that username format is name@domain */
8198 if (!strchr(signinname_login
[0], '@') || g_str_has_prefix(signinname_login
[0], "@") || g_str_has_suffix(signinname_login
[0], "@")) {
8199 g_strfreev(signinname_login
);
8200 gc
->wants_to_die
= TRUE
;
8201 purple_connection_error(gc
, _("User name should be a valid SIP URI\nExample: user@company.com"));
8204 sip
->username
= g_strdup(signinname_login
[0]);
8206 /* ensure that email format is name@domain if provided */
8207 email
= purple_account_get_string(sip
->account
, "email", NULL
);
8208 if (!is_empty(email
) &&
8209 (!strchr(email
, '@') || g_str_has_prefix(email
, "@") || g_str_has_suffix(email
, "@")))
8211 gc
->wants_to_die
= TRUE
;
8212 purple_connection_error(gc
, _("Email address should be valid if provided\nExample: user@company.com"));
8215 sip
->email
= !is_empty(email
) ? g_strdup(email
) : g_strdup(sip
->username
);
8217 /* login name specified? */
8218 if (signinname_login
[1] && strlen(signinname_login
[1])) {
8219 gchar
**domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
8220 gboolean has_domain
= domain_user
[1] != NULL
;
8221 purple_debug_info("sipe", "sipe_login: signinname[1] '%s'\n", signinname_login
[1]);
8222 sip
->authdomain
= has_domain
? g_strdup(domain_user
[0]) : NULL
;
8223 sip
->authuser
= g_strdup(domain_user
[has_domain
? 1 : 0]);
8224 purple_debug_info("sipe", "sipe_login: auth domain '%s' user '%s'\n",
8225 sip
->authdomain
? sip
->authdomain
: "", sip
->authuser
);
8226 g_strfreev(domain_user
);
8229 userserver
= g_strsplit(signinname_login
[0], "@", 2);
8230 purple_debug_info("sipe", "sipe_login: user '%s' server '%s'\n", userserver
[0], userserver
[1]);
8231 purple_connection_set_display_name(gc
, userserver
[0]);
8232 sip
->sipdomain
= g_strdup(userserver
[1]);
8233 g_strfreev(userserver
);
8234 g_strfreev(signinname_login
);
8236 if (strchr(sip
->username
, ' ') != NULL
) {
8237 gc
->wants_to_die
= TRUE
;
8238 purple_connection_error(gc
, _("SIP Exchange user name contains whitespace"));
8242 sip
->password
= g_strdup(purple_connection_get_password(gc
));
8244 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
8245 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8246 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
8247 sip
->subscriptions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8248 g_free
, (GDestroyNotify
)sipe_subscription_free
);
8250 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
8252 g_free(sip
->status
);
8253 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
8255 sip
->auto_transport
= FALSE
;
8256 transport
= purple_account_get_string(account
, "transport", "auto");
8257 userserver
= g_strsplit(purple_account_get_string(account
, "server", ""), ":", 2);
8258 if (userserver
[0]) {
8259 /* Use user specified server[:port] */
8263 port
= atoi(userserver
[1]);
8265 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login: user specified SIP server %s:%d\n",
8266 userserver
[0], port
);
8268 if (strcmp(transport
, "auto") == 0) {
8269 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8270 } else if (strcmp(transport
, "tls") == 0) {
8271 sip
->transport
= SIPE_TRANSPORT_TLS
;
8272 } else if (strcmp(transport
, "tcp") == 0) {
8273 sip
->transport
= SIPE_TRANSPORT_TCP
;
8275 sip
->transport
= SIPE_TRANSPORT_UDP
;
8278 create_connection(sip
, g_strdup(userserver
[0]), port
);
8280 /* Server auto-discovery */
8281 if (strcmp(transport
, "auto") == 0) {
8282 sip
->auto_transport
= TRUE
;
8283 if (current_service
&& current_service
->transport
!= NULL
&& current_service
->service
!= NULL
){
8285 resolve_next_service(sip
, current_service
);
8287 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
8289 } else if (strcmp(transport
, "tls") == 0) {
8290 resolve_next_service(sip
, service_tls
);
8291 } else if (strcmp(transport
, "tcp") == 0) {
8292 resolve_next_service(sip
, service_tcp
);
8294 resolve_next_service(sip
, service_udp
);
8297 g_strfreev(userserver
);
8300 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
8302 connection_free_all(sip
);
8307 if (sip
->query_data
!= NULL
)
8308 purple_dnsquery_destroy(sip
->query_data
);
8309 sip
->query_data
= NULL
;
8311 if (sip
->srv_query_data
!= NULL
)
8312 purple_srv_cancel(sip
->srv_query_data
);
8313 sip
->srv_query_data
= NULL
;
8315 if (sip
->listen_data
!= NULL
)
8316 purple_network_listen_cancel(sip
->listen_data
);
8317 sip
->listen_data
= NULL
;
8319 if (sip
->gsc
!= NULL
)
8320 purple_ssl_close(sip
->gsc
);
8323 sipe_auth_free(&sip
->registrar
);
8324 sipe_auth_free(&sip
->proxy
);
8327 purple_circ_buffer_destroy(sip
->txbuf
);
8330 g_free(sip
->realhostname
);
8331 sip
->realhostname
= NULL
;
8333 g_free(sip
->server_version
);
8334 sip
->server_version
= NULL
;
8337 purple_input_remove(sip
->listenpa
);
8339 if (sip
->tx_handler
)
8340 purple_input_remove(sip
->tx_handler
);
8341 sip
->tx_handler
= 0;
8342 if (sip
->resendtimeout
)
8343 purple_timeout_remove(sip
->resendtimeout
);
8344 sip
->resendtimeout
= 0;
8345 if (sip
->timeouts
) {
8346 GSList
*entry
= sip
->timeouts
;
8348 struct scheduled_action
*sched_action
= entry
->data
;
8349 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
8350 purple_timeout_remove(sched_action
->timeout_handler
);
8351 if (sched_action
->destroy
) {
8352 (*sched_action
->destroy
)(sched_action
->payload
);
8354 g_free(sched_action
->name
);
8355 g_free(sched_action
);
8356 entry
= entry
->next
;
8359 g_slist_free(sip
->timeouts
);
8361 if (sip
->allow_events
) {
8362 GSList
*entry
= sip
->allow_events
;
8364 g_free(entry
->data
);
8365 entry
= entry
->next
;
8368 g_slist_free(sip
->allow_events
);
8370 if (sip
->containers
) {
8371 GSList
*entry
= sip
->containers
;
8373 free_container((struct sipe_container
*)entry
->data
);
8374 entry
= entry
->next
;
8377 g_slist_free(sip
->containers
);
8380 g_free(sip
->contact
);
8381 sip
->contact
= NULL
;
8383 g_free(sip
->regcallid
);
8384 sip
->regcallid
= NULL
;
8386 if (sip
->serveraddr
)
8387 g_free(sip
->serveraddr
);
8388 sip
->serveraddr
= NULL
;
8390 if (sip
->focus_factory_uri
)
8391 g_free(sip
->focus_factory_uri
);
8392 sip
->focus_factory_uri
= NULL
;
8395 sip
->processing_input
= FALSE
;
8398 sipe_ews_free(sip
->ews
);
8404 * A callback for g_hash_table_foreach_remove
8406 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
8407 SIPE_UNUSED_PARAMETER gpointer user_data
)
8409 sipe_free_buddy((struct sipe_buddy
*) buddy
);
8411 /* We must return TRUE as the key/value have already been deleted */
8415 static void sipe_close(PurpleConnection
*gc
)
8417 struct sipe_account_data
*sip
= gc
->proto_data
;
8420 /* leave all conversations */
8421 sipe_session_close_all(sip
);
8422 sipe_session_remove_all(sip
);
8425 sip_csta_close(sip
);
8428 if (PURPLE_CONNECTION_IS_CONNECTED(sip
->gc
)) {
8429 /* unsubscribe all */
8430 g_hash_table_foreach(sip
->subscriptions
, sipe_unsubscribe_cb
, sip
);
8433 do_register_exp(sip
, 0);
8436 sipe_connection_cleanup(sip
);
8437 g_free(sip
->sipdomain
);
8438 g_free(sip
->username
);
8440 g_free(sip
->password
);
8441 g_free(sip
->authdomain
);
8442 g_free(sip
->authuser
);
8443 g_free(sip
->status
);
8446 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
8447 g_hash_table_destroy(sip
->buddies
);
8448 g_hash_table_destroy(sip
->our_publications
);
8449 g_hash_table_destroy(sip
->user_state_publications
);
8450 g_hash_table_destroy(sip
->subscriptions
);
8453 GSList
*entry
= sip
->groups
;
8455 struct sipe_group
*group
= entry
->data
;
8456 g_free(group
->name
);
8458 entry
= entry
->next
;
8461 g_slist_free(sip
->groups
);
8463 if (sip
->our_publication_keys
) {
8464 GSList
*entry
= sip
->our_publication_keys
;
8466 g_free(entry
->data
);
8467 entry
= entry
->next
;
8470 g_slist_free(sip
->our_publication_keys
);
8472 while (sip
->transactions
)
8473 transactions_remove(sip
, sip
->transactions
->data
);
8475 g_free(gc
->proto_data
);
8476 gc
->proto_data
= NULL
;
8479 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
8480 SIPE_UNUSED_PARAMETER
void *user_data
)
8482 PurpleAccount
*acct
= purple_connection_get_account(gc
);
8483 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
8484 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
8486 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
8487 purple_conversation_present(conv
);
8491 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
8492 SIPE_UNUSED_PARAMETER
void *user_data
)
8495 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
8496 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
8499 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
8500 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
8502 PurpleNotifySearchResults
*results
;
8503 PurpleNotifySearchColumn
*column
;
8504 xmlnode
*searchResults
;
8506 int match_count
= 0;
8507 gboolean more
= FALSE
;
8510 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
8512 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
8513 if (!searchResults
) {
8514 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
8518 results
= purple_notify_searchresults_new();
8520 if (results
== NULL
) {
8521 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
8522 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
8524 xmlnode_free(searchResults
);
8528 column
= purple_notify_searchresults_column_new(_("User name"));
8529 purple_notify_searchresults_column_add(results
, column
);
8531 column
= purple_notify_searchresults_column_new(_("Name"));
8532 purple_notify_searchresults_column_add(results
, column
);
8534 column
= purple_notify_searchresults_column_new(_("Company"));
8535 purple_notify_searchresults_column_add(results
, column
);
8537 column
= purple_notify_searchresults_column_new(_("Country"));
8538 purple_notify_searchresults_column_add(results
, column
);
8540 column
= purple_notify_searchresults_column_new(_("Email"));
8541 purple_notify_searchresults_column_add(results
, column
);
8543 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
8546 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
8547 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
8548 g_strfreev(uri_parts
);
8550 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
8551 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
8552 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
8553 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
8555 purple_notify_searchresults_row_add(results
, row
);
8559 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
8560 char *data
= xmlnode_get_data_unescaped(mrow
);
8561 more
= (g_strcasecmp(data
, "true") == 0);
8565 secondary
= g_strdup_printf(
8566 dngettext(GETTEXT_PACKAGE
,
8567 "Found %d contact%s:",
8568 "Found %d contacts%s:", match_count
),
8569 match_count
, more
? _(" (more matched your query)") : "");
8571 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
8572 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
8573 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
8576 xmlnode_free(searchResults
);
8580 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
8582 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
8583 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
8587 PurpleRequestField
*field
= entries
->data
;
8588 const char *id
= purple_request_field_get_id(field
);
8589 const char *value
= purple_request_field_string_get_value(field
);
8591 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
8593 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
8594 } while ((entries
= g_list_next(entries
)) != NULL
);
8598 struct sipe_account_data
*sip
= gc
->proto_data
;
8599 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
8600 gchar
*query
= g_strjoinv(NULL
, attrs
);
8601 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
8602 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
8603 send_soap_request_with_cb(sip
, domain_uri
, body
,
8604 (TransCallback
) process_search_contact_response
, NULL
);
8613 static void sipe_show_find_contact(PurplePluginAction
*action
)
8615 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8616 PurpleRequestFields
*fields
;
8617 PurpleRequestFieldGroup
*group
;
8618 PurpleRequestField
*field
;
8620 fields
= purple_request_fields_new();
8621 group
= purple_request_field_group_new(NULL
);
8622 purple_request_fields_add_group(fields
, group
);
8624 field
= purple_request_field_string_new("givenName", _("First name"), NULL
, FALSE
);
8625 purple_request_field_group_add_field(group
, field
);
8626 field
= purple_request_field_string_new("sn", _("Last name"), NULL
, FALSE
);
8627 purple_request_field_group_add_field(group
, field
);
8628 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
8629 purple_request_field_group_add_field(group
, field
);
8630 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
8631 purple_request_field_group_add_field(group
, field
);
8633 purple_request_fields(gc
,
8635 _("Search for a contact"),
8636 _("Enter the information for the person you wish to find. Empty fields will be ignored."),
8638 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
8640 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
8643 static void sipe_show_about_plugin(PurplePluginAction
*action
)
8645 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8646 char *tmp
= g_strdup_printf(
8648 * Non-translatable parts, like markup, are hard-coded
8649 * into the format string. This requires more translatable
8650 * texts but it makes the translations less error prone.
8652 "<b><font size=\"+1\">SIPE " SIPE_VERSION
" </font></b><br/>"
8655 "<li> - MS Office Communications Server 2007 R2</li><br/>"
8656 "<li> - MS Office Communications Server 2007</li><br/>"
8657 "<li> - MS Live Communications Server 2005</li><br/>"
8658 "<li> - MS Live Communications Server 2003</li><br/>"
8659 "<li> - Reuters Messaging</li><br/>"
8661 /* 2 */ "%s: <a href=\"http://sipe.sourceforge.net\">http://sipe.sourceforge.net</a><br/>"
8662 /* 3,4 */ "%s: <a href=\"http://sourceforge.net/projects/sipe/forums/forum/688534\">%s</a><br/>"
8663 /* 5 */ "%s: <a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a><br/>"
8664 /* 6 */ "%s: GPLv2+<br/>"
8668 " - Reuters Messaging network<br/>"
8669 " - Deutsche Bank<br/>"
8670 " - Merrill Lynch<br/>"
8676 " - Alcatel-Lucent<br/>"
8679 /* 8,9 */ "%s<a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a>%s.<br/>"
8681 /* 10 */ "<b>%s:</b><br/>"
8682 " - Anibal Avelar<br/>"
8683 " - Gabriel Burt<br/>"
8684 " - Stefan Becker<br/>"
8689 /* The next 11 texts make up the SIPE about note text */
8690 /* About note, part 1/11: introduction */
8691 _("A third-party plugin implementing extended version of SIP/SIMPLE used by various products"),
8692 /* About note, part 2/11: home page URL (label) */
8694 /* About note, part 3/11: support forum URL (label) */
8696 /* About note, part 4/11: support forum name (hyperlink text) */
8698 /* About note, part 5/11: translation service URL (label) */
8700 /* About note, part 6/11: license type (label) */
8702 /* About note, part 7/11: known users */
8703 _("We support users in such organizations as"),
8704 /* About note, part 8/11: translation request, text before Transifex.net URL */
8705 /* append a space if text is not empty */
8706 _("Please help us to translate SIPE to your native language here at "),
8707 /* About note, part 9/11: translation request, text after Transifex.net URL */
8708 /* start with a space if text is not empty */
8709 _(" using convenient web interface"),
8710 /* About note, part 10/11: author list (header) */
8712 /* About note, part 11/11: Localization credit */
8713 /* PLEASE NOTE: do *NOT* simply translate the english original */
8714 /* but write something similar to the following sentence: */
8715 /* "Localization for <language name> (<language code>): <name>" */
8716 _("Original texts in English (en): SIPE developers")
8718 purple_notify_formatted(gc
, NULL
, " ", NULL
, tmp
, NULL
, NULL
);
8722 static void sipe_republish_calendar(PurplePluginAction
*action
)
8724 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8725 struct sipe_account_data
*sip
= gc
->proto_data
;
8727 sipe_update_calendar(sip
);
8730 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
8734 struct sipe_publication
*publication
= value
;
8736 g_string_append_printf( str
,
8737 SIPE_PUB_XML_PUBLICATION_CLEAR
,
8738 publication
->category
,
8739 publication
->instance
,
8740 publication
->container
,
8741 publication
->version
,
8745 static void sipe_reset_status(PurplePluginAction
*action
)
8747 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8748 struct sipe_account_data
*sip
= gc
->proto_data
;
8750 if (sip
->ocs2007
) /* 2007+ */
8752 GString
* str
= g_string_new(NULL
);
8753 gchar
*publications
;
8755 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
8756 purple_debug_info("sipe", "sipe_reset_status: no userState publications, exiting.\n");
8760 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
8761 publications
= g_string_free(str
, FALSE
);
8763 send_presence_publish(sip
, publications
);
8764 g_free(publications
);
8768 send_presence_soap0(sip
, FALSE
, TRUE
);
8772 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
8775 PurpleConnection
*gc
= (PurpleConnection
*)context
;
8776 struct sipe_account_data
*sip
= gc
->proto_data
;
8778 PurplePluginAction
*act
;
8779 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
8781 act
= purple_plugin_action_new(_("About SIPE plugin..."), sipe_show_about_plugin
);
8782 menu
= g_list_prepend(menu
, act
);
8784 act
= purple_plugin_action_new(_("Contact search..."), sipe_show_find_contact
);
8785 menu
= g_list_prepend(menu
, act
);
8787 if (!strcmp(calendar
, "EXCH")) {
8788 act
= purple_plugin_action_new(_("Republish Calendar"), sipe_republish_calendar
);
8789 menu
= g_list_prepend(menu
, act
);
8792 act
= purple_plugin_action_new(_("Reset status"), sipe_reset_status
);
8793 menu
= g_list_prepend(menu
, act
);
8795 menu
= g_list_reverse(menu
);
8800 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
8804 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
8810 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
8816 static char *sipe_status_text(PurpleBuddy
*buddy
)
8818 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
8819 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
8820 const char *status_id
= purple_status_get_id(status
);
8821 struct sipe_account_data
*sip
= (struct sipe_account_data
*)buddy
->account
->gc
->proto_data
;
8822 struct sipe_buddy
*sbuddy
;
8825 if (!sip
) return NULL
; /* happens on pidgin exit */
8827 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
8829 const char *activity_str
= sbuddy
->activity
?
8831 !strcmp(status_id
, SIPE_STATUS_ID_BUSY
) || !strcmp(status_id
, SIPE_STATUS_ID_BRB
) ?
8832 purple_status_get_name(status
) : NULL
;
8834 if (activity_str
&& sbuddy
->note
)
8836 text
= g_strdup_printf("%s - <i>%s</i>", activity_str
, sbuddy
->note
);
8838 else if (activity_str
)
8840 text
= g_strdup(activity_str
);
8842 else if (sbuddy
->note
)
8844 text
= g_strdup_printf("<i>%s</i>", sbuddy
->note
);
8851 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
8853 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
8854 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
8855 struct sipe_account_data
*sip
;
8856 struct sipe_buddy
*sbuddy
;
8858 gboolean is_oof_note
= FALSE
;
8859 char *activity
= NULL
;
8860 char *calendar
= NULL
;
8861 char *meeting_subject
= NULL
;
8862 char *meeting_location
= NULL
;
8864 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
8865 if (sip
) //happens on pidgin exit
8867 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
8870 note
= sbuddy
->note
;
8871 is_oof_note
= sbuddy
->is_oof_note
;
8872 activity
= sbuddy
->activity
;
8873 calendar
= sipe_cal_get_description(sbuddy
);
8874 meeting_subject
= sbuddy
->meeting_subject
;
8875 meeting_location
= sbuddy
->meeting_location
;
8880 if (purple_presence_is_online(presence
))
8882 const char *status_str
= activity
? activity
: purple_status_get_name(status
);
8884 purple_notify_user_info_add_pair(user_info
, _("Status"), status_str
);
8886 if (purple_presence_is_online(presence
) &&
8887 !is_empty(calendar
))
8889 purple_notify_user_info_add_pair(user_info
, _("Calendar"), calendar
);
8892 if (!is_empty(meeting_location
))
8894 purple_notify_user_info_add_pair(user_info
, _("Meeting in"), meeting_location
);
8896 if (!is_empty(meeting_subject
))
8898 purple_notify_user_info_add_pair(user_info
, _("Meeting about"), meeting_subject
);
8903 char *tmp
= g_strdup_printf("<i>%s</i>", note
);
8904 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, note
);
8906 purple_notify_user_info_add_pair(user_info
, is_oof_note
? _("Out of office note") : _("Note"), tmp
);
8912 #if PURPLE_VERSION_CHECK(2,5,0)
8914 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
8917 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
8918 g_hash_table_insert(table
, "login_label", (gpointer
)_("user@company.com"));
8923 static PurpleBuddy
*
8924 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
8927 const gchar
*server_alias
, *email
;
8928 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
8930 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
8932 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
8934 server_alias
= purple_buddy_get_server_alias(buddy
);
8936 purple_blist_server_alias_buddy(clone
, server_alias
);
8939 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
8941 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
8944 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
8946 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
8951 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
8953 PurpleBuddy
*buddy
, *b
;
8954 PurpleConnection
*gc
;
8955 PurpleGroup
* group
= purple_find_group(group_name
);
8957 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
8959 buddy
= (PurpleBuddy
*)node
;
8961 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
8962 gc
= purple_account_get_connection(buddy
->account
);
8964 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
8966 purple_blist_add_buddy_clone(group
, buddy
);
8969 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
8973 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
8975 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
8977 purple_debug_info("sipe", "sipe_buddy_menu_chat_new_cb: buddy->name=%s\n", buddy
->name
);
8979 /* 2007+ conference */
8982 sipe_conf_add(sip
, buddy
->name
);
8984 else /* 2005- multiparty chat */
8986 gchar
*self
= sip_uri_self(sip
);
8987 struct sip_session
*session
;
8989 session
= sipe_session_add_chat(sip
);
8990 session
->chat_title
= sipe_chat_get_name(session
->callid
);
8991 session
->roster_manager
= g_strdup(self
);
8993 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, session
->chat_title
);
8994 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
8995 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
8996 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, FALSE
);
9003 sipe_is_election_finished(struct sip_session
*session
)
9005 gboolean res
= TRUE
;
9007 SIPE_DIALOG_FOREACH
{
9008 if (dialog
->election_vote
== 0) {
9012 } SIPE_DIALOG_FOREACH_END
;
9015 session
->is_voting_in_progress
= FALSE
;
9021 sipe_election_start(struct sipe_account_data
*sip
,
9022 struct sip_session
*session
)
9024 int election_timeout
;
9026 if (session
->is_voting_in_progress
) {
9027 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
9030 session
->is_voting_in_progress
= TRUE
;
9032 session
->bid
= rand();
9034 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
9036 SIPE_DIALOG_FOREACH
{
9037 /* reset election_vote for each chat participant */
9038 dialog
->election_vote
= 0;
9040 /* send RequestRM to each chat participant*/
9041 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
9042 } SIPE_DIALOG_FOREACH_END
;
9044 election_timeout
= 15; /* sec */
9045 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
9049 * @param who a URI to whom to invite to chat
9052 sipe_invite_to_chat(struct sipe_account_data
*sip
,
9053 struct sip_session
*session
,
9057 if (session
->focus_uri
)
9059 sipe_invite_conf(sip
, session
, who
);
9061 else /* a multi-party chat */
9063 gchar
*self
= sip_uri_self(sip
);
9064 if (session
->roster_manager
) {
9065 if (!strcmp(session
->roster_manager
, self
)) {
9066 sipe_invite(sip
, session
, who
, NULL
, NULL
, FALSE
);
9068 sipe_refer(sip
, session
, who
);
9071 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite: no RM available\n");
9073 session
->pending_invite_queue
= slist_insert_unique_sorted(
9074 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
9076 sipe_election_start(sip
, session
);
9083 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
9084 struct sip_session
*session
)
9087 GSList
*entry
= session
->pending_invite_queue
;
9090 invitee
= entry
->data
;
9091 sipe_invite_to_chat(sip
, session
, invitee
);
9092 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
9098 sipe_election_result(struct sipe_account_data
*sip
,
9101 struct sip_session
*session
= (struct sip_session
*)sess
;
9103 gboolean has_won
= TRUE
;
9105 if (session
->roster_manager
) {
9106 purple_debug_info("sipe",
9107 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
9111 session
->is_voting_in_progress
= FALSE
;
9113 SIPE_DIALOG_FOREACH
{
9114 if (dialog
->election_vote
< 0) {
9116 rival
= dialog
->with
;
9119 } SIPE_DIALOG_FOREACH_END
;
9122 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
9124 session
->roster_manager
= sip_uri_self(sip
);
9126 SIPE_DIALOG_FOREACH
{
9127 /* send SetRM to each chat participant*/
9128 sipe_send_election_set_rm(sip
, dialog
);
9129 } SIPE_DIALOG_FOREACH_END
;
9131 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
9135 sipe_process_pending_invite_queue(sip
, session
);
9139 * For 2007+ conference only.
9142 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9144 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9145 struct sip_session
*session
;
9147 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
9148 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_title=%s\n", chat_title
);
9150 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9152 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
9156 * For 2007+ conference only.
9159 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9161 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9162 struct sip_session
*session
;
9164 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: buddy->name=%s\n", buddy
->name
);
9165 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: chat_title=%s\n", chat_title
);
9167 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9169 sipe_conf_delete_user(sip
, session
, buddy
->name
);
9173 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, char *chat_title
)
9175 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9176 struct sip_session
*session
;
9178 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: buddy->name=%s\n", buddy
->name
);
9179 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: chat_title=%s\n", chat_title
);
9181 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9183 sipe_invite_to_chat(sip
, session
, buddy
->name
);
9187 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
9189 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9191 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: buddy->name=%s\n", buddy
->name
);
9193 char *tel_uri
= sip_to_tel_uri(phone
);
9195 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: going to call number: %s\n", tel_uri
? tel_uri
: "");
9196 sip_csta_make_call(sip
, tel_uri
);
9203 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
9206 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
9208 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9211 char *mailto
= g_strdup_printf("mailto:%s", email
);
9212 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
9216 char *const parmList
[] = {"xdg-email", mailto
, NULL
};
9217 if ((pid
= fork()) == -1)
9219 purple_debug_info("sipe", "fork() error\n");
9223 execvp(parmList
[0], parmList
);
9224 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
9232 //@TODO resolve env variable %WINDIR% first
9233 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
9236 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
9245 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
9250 * A menu which appear when right-clicking on buddy in contact list.
9253 sipe_buddy_menu(PurpleBuddy
*buddy
)
9255 PurpleBlistNode
*g_node
;
9256 PurpleGroup
*group
, *gr_parent
;
9257 PurpleMenuAction
*act
;
9259 GList
*menu_groups
= NULL
;
9260 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9263 const char *phone_disp_str
;
9264 gchar
*self
= sip_uri_self(sip
);
9266 SIPE_SESSION_FOREACH
{
9267 if (g_ascii_strcasecmp(self
, buddy
->name
) && session
->chat_title
&& session
->conv
)
9269 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
9271 PurpleConvChatBuddyFlags flags
;
9272 PurpleConvChatBuddyFlags flags_us
;
9274 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
9275 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9276 if (session
->focus_uri
9277 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
9278 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9280 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"), session
->chat_title
);
9281 act
= purple_menu_action_new(label
,
9282 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
9283 session
->chat_title
, NULL
);
9285 menu
= g_list_prepend(menu
, act
);
9288 if (session
->focus_uri
9289 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9291 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_title
);
9292 act
= purple_menu_action_new(label
,
9293 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
9294 session
->chat_title
, NULL
);
9296 menu
= g_list_prepend(menu
, act
);
9301 if (!session
->focus_uri
9302 || (session
->focus_uri
&& !session
->locked
))
9304 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_title
);
9305 act
= purple_menu_action_new(label
,
9306 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
9307 session
->chat_title
, NULL
);
9309 menu
= g_list_prepend(menu
, act
);
9313 } SIPE_SESSION_FOREACH_END
;
9315 act
= purple_menu_action_new(_("New chat"),
9316 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
9318 menu
= g_list_prepend(menu
, act
);
9320 if (sip
->csta
&& !sip
->csta
->line_status
) {
9323 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
9324 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
9326 gchar
*label
= g_strdup_printf(_("Work %s"),
9327 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9328 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9332 menu
= g_list_prepend(menu
, act
);
9336 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
9337 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
9339 gchar
*label
= g_strdup_printf(_("Mobile %s"),
9340 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9341 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9345 menu
= g_list_prepend(menu
, act
);
9349 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
9350 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
9352 gchar
*label
= g_strdup_printf(_("Home %s"),
9353 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9354 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9358 menu
= g_list_prepend(menu
, act
);
9362 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
9363 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
9365 gchar
*label
= g_strdup_printf(_("Other %s"),
9366 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9367 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9371 menu
= g_list_prepend(menu
, act
);
9375 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
9376 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
9378 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
9379 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9380 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9384 menu
= g_list_prepend(menu
, act
);
9388 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9390 act
= purple_menu_action_new(_("Send email..."),
9391 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
9393 menu
= g_list_prepend(menu
, act
);
9396 gr_parent
= purple_buddy_get_group(buddy
);
9397 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
9398 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
9401 group
= (PurpleGroup
*)g_node
;
9402 if (group
== gr_parent
)
9405 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
9408 act
= purple_menu_action_new(purple_group_get_name(group
),
9409 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
9411 menu_groups
= g_list_prepend(menu_groups
, act
);
9413 menu_groups
= g_list_reverse(menu_groups
);
9415 act
= purple_menu_action_new(_("Copy to"),
9418 menu
= g_list_prepend(menu
, act
);
9419 menu
= g_list_reverse(menu
);
9426 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
9428 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9429 struct sip_session
*session
;
9431 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9432 sipe_conf_modify_conference_lock(sip
, session
, locked
);
9436 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
9438 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
9439 sipe_conf_modify_lock(chat
, FALSE
);
9443 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
9445 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
9446 sipe_conf_modify_lock(chat
, TRUE
);
9450 sipe_chat_menu(PurpleChat
*chat
)
9452 PurpleMenuAction
*act
;
9453 PurpleConvChatBuddyFlags flags_us
;
9455 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9456 struct sip_session
*session
;
9459 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9460 if (!session
) return NULL
;
9462 self
= sip_uri_self(sip
);
9463 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9465 if (session
->focus_uri
9466 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9468 if (session
->locked
) {
9469 act
= purple_menu_action_new(_("Unlock"),
9470 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
9472 menu
= g_list_prepend(menu
, act
);
9474 act
= purple_menu_action_new(_("Lock"),
9475 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
9477 menu
= g_list_prepend(menu
, act
);
9481 menu
= g_list_reverse(menu
);
9488 sipe_blist_node_menu(PurpleBlistNode
*node
)
9490 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
9491 return sipe_buddy_menu((PurpleBuddy
*) node
);
9492 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
9493 return sipe_chat_menu((PurpleChat
*)node
);
9500 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
9502 char *uri
= trans
->payload
->data
;
9504 PurpleNotifyUserInfo
*info
;
9505 PurpleBuddy
*pbuddy
= NULL
;
9506 struct sipe_buddy
*sbuddy
;
9507 const char *alias
= NULL
;
9508 char *device_name
= NULL
;
9509 char *server_alias
= NULL
;
9510 char *phone_number
= NULL
;
9514 if (!sip
) return FALSE
;
9516 purple_debug_info("sipe", "Fetching %s's user info for %s\n", uri
, sip
->username
);
9518 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
9519 alias
= purple_buddy_get_local_alias(pbuddy
);
9521 //will query buddy UA's capabilities and send answer to log
9522 sipe_options_request(sip
, uri
);
9524 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
9526 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
9529 info
= purple_notify_user_info_new();
9531 if (msg
->response
!= 200) {
9532 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
9534 xmlnode
*searchResults
;
9537 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
9538 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
9539 if (!searchResults
) {
9540 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
9541 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
9543 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
9544 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
9545 phone_number
= g_strdup(xmlnode_get_attrib(mrow
, "phone"));
9547 /* For 2007 system we will take this from ContactCard -
9548 * it has cleaner tel: URIs at least
9550 if (!sip
->ocs2007
) {
9551 char *tel_uri
= sip_to_tel_uri(phone_number
);
9552 /* trims its parameters, so call first */
9553 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, server_alias
);
9554 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
9555 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
9556 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, phone_number
);
9560 if (server_alias
&& strlen(server_alias
) > 0) {
9561 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9563 if ((value
= xmlnode_get_attrib(mrow
, "title")) && strlen(value
) > 0) {
9564 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
9566 if ((value
= xmlnode_get_attrib(mrow
, "office")) && strlen(value
) > 0) {
9567 purple_notify_user_info_add_pair(info
, _("Office"), value
);
9569 if (phone_number
&& strlen(phone_number
) > 0) {
9570 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
9572 if ((value
= xmlnode_get_attrib(mrow
, "company")) && strlen(value
) > 0) {
9573 purple_notify_user_info_add_pair(info
, _("Company"), value
);
9575 if ((value
= xmlnode_get_attrib(mrow
, "city")) && strlen(value
) > 0) {
9576 purple_notify_user_info_add_pair(info
, _("City"), value
);
9578 if ((value
= xmlnode_get_attrib(mrow
, "state")) && strlen(value
) > 0) {
9579 purple_notify_user_info_add_pair(info
, _("State"), value
);
9581 if ((value
= xmlnode_get_attrib(mrow
, "country")) && strlen(value
) > 0) {
9582 purple_notify_user_info_add_pair(info
, _("Country"), value
);
9584 if (email
&& strlen(email
) > 0) {
9585 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9589 xmlnode_free(searchResults
);
9592 purple_notify_user_info_add_section_break(info
);
9594 if (!server_alias
|| !strcmp("", server_alias
)) {
9595 g_free(server_alias
);
9596 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
9598 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9602 /* present alias if it differs from server alias */
9603 if (alias
&& (!server_alias
|| strcmp(alias
, server_alias
)))
9605 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
9608 if (!email
|| !strcmp("", email
)) {
9610 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
9612 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9616 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
9618 purple_notify_user_info_add_pair(info
, _("Site"), site
);
9622 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
9625 /* show a buddy's user info in a nice dialog box */
9626 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
9627 uri
, /* buddy's URI */
9629 NULL
, /* callback called when dialog closed */
9630 NULL
); /* userdata for callback */
9632 g_free(phone_number
);
9633 g_free(server_alias
);
9635 g_free(device_name
);
9641 * AD search first, LDAP based
9643 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
9645 struct sipe_account_data
*sip
= gc
->proto_data
;
9646 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
9647 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
9648 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
9649 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
9651 payload
->destroy
= g_free
;
9652 payload
->data
= g_strdup(username
);
9654 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
9655 send_soap_request_with_cb(sip
, domain_uri
, body
,
9656 (TransCallback
) process_get_info_response
, payload
);
9662 static PurplePlugin
*my_protocol
= NULL
;
9664 static PurplePluginProtocolInfo prpl_info
=
9666 OPT_PROTO_CHAT_TOPIC
,
9667 NULL
, /* user_splits */
9668 NULL
, /* protocol_options */
9669 NO_BUDDY_ICONS
, /* icon_spec */
9670 sipe_list_icon
, /* list_icon */
9671 NULL
, /* list_emblems */
9672 sipe_status_text
, /* status_text */
9673 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
9674 sipe_status_types
, /* away_states */
9675 sipe_blist_node_menu
, /* blist_node_menu */
9676 NULL
, /* chat_info */
9677 NULL
, /* chat_info_defaults */
9678 sipe_login
, /* login */
9679 sipe_close
, /* close */
9680 sipe_im_send
, /* send_im */
9681 NULL
, /* set_info */ // TODO maybe
9682 sipe_send_typing
, /* send_typing */
9683 sipe_get_info
, /* get_info */
9684 sipe_set_status
, /* set_status */
9685 sipe_set_idle
, /* set_idle */
9686 NULL
, /* change_passwd */
9687 sipe_add_buddy
, /* add_buddy */
9688 NULL
, /* add_buddies */
9689 sipe_remove_buddy
, /* remove_buddy */
9690 NULL
, /* remove_buddies */
9691 sipe_add_permit
, /* add_permit */
9692 sipe_add_deny
, /* add_deny */
9693 sipe_add_deny
, /* rem_permit */
9694 sipe_add_permit
, /* rem_deny */
9695 dummy_permit_deny
, /* set_permit_deny */
9696 NULL
, /* join_chat */
9697 NULL
, /* reject_chat */
9698 NULL
, /* get_chat_name */
9699 sipe_chat_invite
, /* chat_invite */
9700 sipe_chat_leave
, /* chat_leave */
9701 NULL
, /* chat_whisper */
9702 sipe_chat_send
, /* chat_send */
9703 sipe_keep_alive
, /* keepalive */
9704 NULL
, /* register_user */
9705 NULL
, /* get_cb_info */ // deprecated
9706 NULL
, /* get_cb_away */ // deprecated
9707 sipe_alias_buddy
, /* alias_buddy */
9708 sipe_group_buddy
, /* group_buddy */
9709 sipe_rename_group
, /* rename_group */
9710 NULL
, /* buddy_free */
9711 sipe_convo_closed
, /* convo_closed */
9712 purple_normalize_nocase
, /* normalize */
9713 NULL
, /* set_buddy_icon */
9714 sipe_remove_group
, /* remove_group */
9715 NULL
, /* get_cb_real_name */ // TODO?
9716 NULL
, /* set_chat_topic */
9717 NULL
, /* find_blist_chat */
9718 NULL
, /* roomlist_get_list */
9719 NULL
, /* roomlist_cancel */
9720 NULL
, /* roomlist_expand_category */
9721 NULL
, /* can_receive_file */
9722 NULL
, /* send_file */
9723 NULL
, /* new_xfer */
9724 NULL
, /* offline_message */
9725 NULL
, /* whiteboard_prpl_ops */
9726 sipe_send_raw
, /* send_raw */
9727 NULL
, /* roomlist_room_serialize */
9728 NULL
, /* unregister_user */
9729 NULL
, /* send_attention */
9730 NULL
, /* get_attention_types */
9731 #if !PURPLE_VERSION_CHECK(2,5,0)
9732 /* Backward compatibility when compiling against 2.4.x API */
9733 (void (*)(void)) /* _purple_reserved4 */
9735 sizeof(PurplePluginProtocolInfo
), /* struct_size */
9736 #if PURPLE_VERSION_CHECK(2,5,0)
9737 sipe_get_account_text_table
, /* get_account_text_table */
9738 #if PURPLE_VERSION_CHECK(2,6,0)
9739 NULL
, /* initiate_media */
9740 NULL
, /* get_media_caps */
9746 static PurplePluginInfo info
= {
9747 PURPLE_PLUGIN_MAGIC
,
9748 PURPLE_MAJOR_VERSION
,
9749 PURPLE_MINOR_VERSION
,
9750 PURPLE_PLUGIN_PROTOCOL
, /**< type */
9751 NULL
, /**< ui_requirement */
9753 NULL
, /**< dependencies */
9754 PURPLE_PRIORITY_DEFAULT
, /**< priority */
9755 "prpl-sipe", /**< id */
9756 "Office Communicator", /**< name */
9757 SIPE_VERSION
, /**< version */
9758 "Microsoft Office Communicator Protocol Plugin", /**< summary */
9759 "A plugin for the extended SIP/SIMPLE protocol used by " /**< description */
9760 "Microsoft Live/Office Communications Server (LCS2005/OCS2007+)", /**< description */
9761 "Anibal Avelar <avelar@gmail.com>, " /**< author */
9762 "Gabriel Burt <gburt@novell.com>, " /**< author */
9763 "Stefan Becker <stefan.becker@nokia.com>, " /**< author */
9764 "pier11 <pier11@operamail.com>", /**< author */
9765 "http://sipe.sourceforge.net/", /**< homepage */
9766 sipe_plugin_load
, /**< load */
9767 sipe_plugin_unload
, /**< unload */
9768 sipe_plugin_destroy
, /**< destroy */
9769 NULL
, /**< ui_info */
9770 &prpl_info
, /**< extra_info */
9779 static void sipe_plugin_destroy(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9783 entry
= prpl_info
.protocol_options
;
9785 purple_account_option_destroy(entry
->data
);
9786 entry
= g_list_delete_link(entry
, entry
);
9788 prpl_info
.protocol_options
= NULL
;
9790 entry
= prpl_info
.user_splits
;
9792 purple_account_user_split_destroy(entry
->data
);
9793 entry
= g_list_delete_link(entry
, entry
);
9795 prpl_info
.user_splits
= NULL
;
9798 static void init_plugin(PurplePlugin
*plugin
)
9800 PurpleAccountUserSplit
*split
;
9801 PurpleAccountOption
*option
;
9806 purple_debug_info(PACKAGE
, "bindtextdomain = %s\n", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
9807 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s\n",
9808 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
9809 textdomain(GETTEXT_PACKAGE
);
9812 purple_plugin_register(plugin
);
9814 split
= purple_account_user_split_new(_("Login\n user or DOMAIN\\user or\n user@company.com"), NULL
, ',');
9815 purple_account_user_split_set_reverse(split
, FALSE
);
9816 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
9818 option
= purple_account_option_string_new(_("Server[:Port]\n(leave empty for auto-discovery)"), "server", "");
9819 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9821 option
= purple_account_option_list_new(_("Connection type"), "transport", NULL
);
9822 purple_account_option_add_list_item(option
, _("Auto"), "auto");
9823 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
9824 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
9825 purple_account_option_add_list_item(option
, _("UDP"), "udp");
9826 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9828 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
9829 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
9831 option
= purple_account_option_string_new(_("User Agent"), "useragent", "");
9832 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9835 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
9836 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9838 /* Suitable for sspi/NTLM, sspi/Kerberos and krb5 security mechanisms
9839 * No login/password is taken into account if this option present,
9840 * instead used default credentials stored in OS.
9842 option
= purple_account_option_bool_new(_("Use Single Sign-On"), "sso", TRUE
);
9843 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9846 option
= purple_account_option_list_new(_("Calendar source"), "calendar", NULL
);
9847 purple_account_option_add_list_item(option
, _("Exchange 2007/2010"), "EXCH");
9848 purple_account_option_add_list_item(option
, _("None"), "NONE");
9849 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9851 /** Example: https://server.company.com/EWS/Exchange.asmx */
9852 option
= purple_account_option_string_new(_("Email services URL\n(leave empty for auto-discovery)"), "email_url", "");
9853 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9855 option
= purple_account_option_string_new(_("Email address\n(if different from Username)"), "email", "");
9856 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9858 /** Example: DOMAIN\user or user@company.com */
9859 option
= purple_account_option_string_new(_("Email login\n(if different from Login)"), "email_login", "");
9860 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9862 option
= purple_account_option_string_new(_("Email password\n(if different from Password)"), "email_password", "");
9863 purple_account_option_set_masked(option
, TRUE
);
9864 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
9866 my_protocol
= plugin
;
9869 PURPLE_INIT_PLUGIN(sipe
, init_plugin
, info
);