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"
87 #include "sipe-sign.h"
91 /* Backward compatibility when compiling against 2.4.x API */
92 #if !PURPLE_VERSION_CHECK(2,5,0)
93 #define PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY 0x0100
96 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
98 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
99 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
101 /* Keep in sync with sipe_transport_type! */
102 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
103 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
105 /* Status identifiers (see also: sipe_status_types()) */
106 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
107 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
108 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
109 /* PURPLE_STATUS_UNAVAILABLE: */
110 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
111 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
112 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
113 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
114 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
115 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
116 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
117 /* PURPLE_STATUS_AWAY: */
118 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
119 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
120 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
121 /** Reuters status (user settable) */
122 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
123 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
124 /* ??? PURPLE_STATUS_MOBILE */
125 /* ??? PURPLE_STATUS_TUNE */
127 /* Status attributes (see also sipe_status_types() */
128 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
130 static struct sipe_activity_map_struct
135 const char *status_id
;
137 } const sipe_activity_map
[] =
139 /* This has nothing to do with Availability numbers, like 3500 (online).
140 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
142 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
143 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
144 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , NULL
},
145 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
146 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("Busy-Idle") , NULL
},
147 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
148 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
149 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
150 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , NULL
},
151 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
152 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , NULL
},
153 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , NULL
},
154 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , NULL
},
155 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
156 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
158 /** @param x is sipe_activity */
159 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
162 /* Action name templates */
163 #define ACTION_NAME_PRESENCE "<presence><%s>"
166 sipe_get_activity_by_token(const char *token
)
170 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
172 if (sipe_strequal(token
, sipe_activity_map
[i
].token
))
173 return sipe_activity_map
[i
].type
;
176 return sipe_activity_map
[0].type
;
180 sipe_get_activity_desc_by_token(const char *token
)
182 if (!token
) return NULL
;
184 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token
));
187 /** Allows to send typed messages from chat window again after account reinstantiation. */
189 sipe_rejoin_chat(PurpleConversation
*conv
)
191 if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
&&
192 PURPLE_CONV_CHAT(conv
)->left
)
194 PURPLE_CONV_CHAT(conv
)->left
= FALSE
;
195 purple_conversation_update(conv
, PURPLE_CONV_UPDATE_CHATLEFT
);
199 static char *genbranch()
201 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
202 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
203 rand() & 0xFFFF, rand() & 0xFFFF);
207 static char *default_ua
= NULL
;
209 sipe_get_useragent(struct sipe_account_data
*sip
)
211 const char *useragent
= purple_account_get_string(sip
->account
, "useragent", "");
212 if (is_empty(useragent
)) {
214 /*@TODO: better approach to define _user_ OS, it's version and host architecture */
216 #if defined(__linux__) || defined(__linux) || defined(__LINUX__)
217 #define SIPE_TARGET_PLATFORM "linux"
218 #elif defined(__NetBSD__) ||defined( __OpenBSD__) || defined(__FreeBSD__)
219 #define SIPE_TARGET_PLATFORM "bsd"
220 # elif defined(__APPLE__) || defined(__MACOS__)
221 #define SIPE_TARGET_PLATFORM "macosx"
222 #elif defined(__solaris__) || defined(__sun)
223 #define SIPE_TARGET_PLATFORM "sun"
224 #elif defined(_WIN32)
225 #define SIPE_TARGET_PLATFORM "win"
227 #define SIPE_TARGET_PLATFORM "generic"
230 #if defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)
231 #define SIPE_TARGET_ARCH "x86_64"
232 #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386)
233 #define SIPE_TARGET_ARCH "i386"
234 #elif defined(__ppc64__)
235 #define SIPE_TARGET_ARCH "ppc64"
236 #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR)
237 #define SIPE_TARGET_ARCH "ppc"
239 #define SIPE_TARGET_ARCH "other"
242 default_ua
= g_strdup_printf("Purple/%s Sipe/%s (%s-%s; %s)",
243 purple_core_get_version(),
245 SIPE_TARGET_PLATFORM
,
247 sip
->server_version
? sip
->server_version
: "");
249 useragent
= default_ua
;
254 static const char *sipe_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount
*a
,
255 SIPE_UNUSED_PARAMETER PurpleBuddy
*b
)
260 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
262 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
);
264 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
265 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
268 static void sipe_close(PurpleConnection
*gc
);
270 static void send_presence_status(struct sipe_account_data
*sip
);
272 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
274 static void sipe_keep_alive(PurpleConnection
*gc
)
276 struct sipe_account_data
*sip
= gc
->proto_data
;
277 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
278 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
279 gchar buf
[2] = {0, 0};
280 purple_debug_info("sipe", "sending keep alive\n");
281 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
283 time_t now
= time(NULL
);
284 if ((sip
->keepalive_timeout
> 0) &&
285 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
286 #if PURPLE_VERSION_CHECK(2,4,0)
287 && ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
290 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
291 sendout_pkt(gc
, "\r\n\r\n");
292 sip
->last_keepalive
= now
;
297 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
299 struct sip_connection
*ret
= NULL
;
300 GSList
*entry
= sip
->openconns
;
303 if (ret
->fd
== fd
) return ret
;
309 static void sipe_auth_free(struct sip_auth
*auth
)
311 g_free(auth
->opaque
);
315 g_free(auth
->target
);
317 auth
->type
= AUTH_TYPE_UNSET
;
320 g_free(auth
->gssapi_data
);
321 auth
->gssapi_data
= NULL
;
322 sip_sec_destroy_context(auth
->gssapi_context
);
323 auth
->gssapi_context
= NULL
;
326 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
328 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
330 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
334 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
336 struct sip_connection
*conn
= connection_find(sip
, fd
);
338 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
339 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
345 static void connection_free_all(struct sipe_account_data
*sip
)
347 struct sip_connection
*ret
= NULL
;
348 GSList
*entry
= sip
->openconns
;
351 connection_remove(sip
, ret
->fd
);
352 entry
= sip
->openconns
;
356 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
359 const char *authuser
= sip
->authuser
;
363 if (!authuser
|| strlen(authuser
) < 1) {
364 authuser
= sip
->username
;
367 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
368 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
370 // If we have a signature for the message, include that
371 if (msg
->signature
) {
372 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
);
375 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
376 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
380 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
383 purple_account_get_bool(sip
->account
, "sso", TRUE
),
384 sip
->authdomain
? sip
->authdomain
: "",
389 if (!gssapi_data
|| !auth
->gssapi_context
) {
390 sip
->gc
->wants_to_die
= TRUE
;
391 purple_connection_error(sip
->gc
, _("Failed to authenticate to server"));
395 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
396 ret
= g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth_protocol
, opaque
, auth
->realm
, auth
->target
, gssapi_data
);
402 return g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth_protocol
, auth
->realm
, auth
->target
);
404 } else { /* Digest */
406 /* Calculate new session key */
408 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
409 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
410 authuser
, auth
->realm
, sip
->password
,
411 auth
->gssapi_data
, NULL
);
414 sprintf(noncecount
, "%08d", auth
->nc
++);
415 response
= purple_cipher_http_digest_calculate_response("md5",
416 msg
->method
, msg
->target
, NULL
, NULL
,
417 auth
->gssapi_data
, noncecount
, NULL
,
419 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
421 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
);
427 static char *parse_attribute(const char *attrname
, const char *source
)
429 const char *tmp
, *tmp2
;
431 int len
= strlen(attrname
);
433 if (g_str_has_prefix(source
, attrname
)) {
435 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
437 retval
= g_strndup(tmp
, tmp2
- tmp
);
439 retval
= g_strdup(tmp
);
445 static void fill_auth(gchar
*hdr
, struct sip_auth
*auth
)
451 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
455 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
456 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
457 auth
->type
= AUTH_TYPE_NTLM
;
460 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
461 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
462 auth
->type
= AUTH_TYPE_KERBEROS
;
466 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
467 auth
->type
= AUTH_TYPE_DIGEST
;
471 parts
= g_strsplit(hdr
, "\", ", 0);
472 for (i
= 0; parts
[i
]; i
++) {
475 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
477 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
478 g_free(auth
->gssapi_data
);
479 auth
->gssapi_data
= tmp
;
481 if (auth
->type
== AUTH_TYPE_NTLM
) {
482 /* NTLM module extracts nonce from gssapi-data */
486 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
487 /* Only used with AUTH_TYPE_DIGEST */
488 g_free(auth
->gssapi_data
);
489 auth
->gssapi_data
= tmp
;
490 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
491 g_free(auth
->opaque
);
493 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
497 if (auth
->type
== AUTH_TYPE_DIGEST
) {
498 /* Throw away old session key */
499 g_free(auth
->opaque
);
504 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
505 g_free(auth
->target
);
514 static void sipe_canwrite_cb(gpointer data
,
515 SIPE_UNUSED_PARAMETER gint source
,
516 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
518 PurpleConnection
*gc
= data
;
519 struct sipe_account_data
*sip
= gc
->proto_data
;
523 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
525 if (max_write
== 0) {
526 if (sip
->tx_handler
!= 0){
527 purple_input_remove(sip
->tx_handler
);
533 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
535 if (written
< 0 && errno
== EAGAIN
)
537 else if (written
<= 0) {
538 /*TODO: do we really want to disconnect on a failure to write?*/
539 purple_connection_error(gc
, _("Could not write"));
543 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
546 static void sipe_canwrite_cb_ssl(gpointer data
,
547 SIPE_UNUSED_PARAMETER gint src
,
548 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
550 PurpleConnection
*gc
= data
;
551 struct sipe_account_data
*sip
= gc
->proto_data
;
555 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
557 if (max_write
== 0) {
558 if (sip
->tx_handler
!= 0) {
559 purple_input_remove(sip
->tx_handler
);
565 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
567 if (written
< 0 && errno
== EAGAIN
)
569 else if (written
<= 0) {
570 /*TODO: do we really want to disconnect on a failure to write?*/
571 purple_connection_error(gc
, _("Could not write"));
575 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
578 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
580 static void send_later_cb(gpointer data
, gint source
,
581 SIPE_UNUSED_PARAMETER
const gchar
*error
)
583 PurpleConnection
*gc
= data
;
584 struct sipe_account_data
*sip
;
585 struct sip_connection
*conn
;
587 if (!PURPLE_CONNECTION_IS_VALID(gc
))
595 purple_connection_error(gc
, _("Could not connect"));
599 sip
= gc
->proto_data
;
601 sip
->connecting
= FALSE
;
602 sip
->last_keepalive
= time(NULL
);
604 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
606 /* If there is more to write now, we need to register a handler */
607 if (sip
->txbuf
->bufused
> 0)
608 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
610 conn
= connection_create(sip
, source
);
611 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
614 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
616 struct sipe_account_data
*sip
;
618 if (!PURPLE_CONNECTION_IS_VALID(gc
))
620 if (gsc
) purple_ssl_close(gsc
);
624 sip
= gc
->proto_data
;
627 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
628 sip
->connecting
= FALSE
;
629 sip
->last_keepalive
= time(NULL
);
631 connection_create(sip
, gsc
->fd
);
633 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
638 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
639 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
641 PurpleConnection
*gc
= data
;
642 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
643 if (sip
== NULL
) return;
645 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
647 /* If there is more to write now */
648 if (sip
->txbuf
->bufused
> 0) {
649 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
654 static void sendlater(PurpleConnection
*gc
, const char *buf
)
656 struct sipe_account_data
*sip
= gc
->proto_data
;
658 if (!sip
->connecting
) {
659 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
660 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
661 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
663 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
664 purple_connection_error(gc
, _("Could not create socket"));
667 sip
->connecting
= TRUE
;
670 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
671 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
673 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
676 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
678 struct sipe_account_data
*sip
= gc
->proto_data
;
679 time_t currtime
= time(NULL
);
680 int writelen
= strlen(buf
);
683 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sending - %s######\n%s######\n", ctime(&currtime
), tmp
= fix_newlines(buf
));
685 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
686 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
687 purple_debug_info("sipe", "could not send packet\n");
696 if (sip
->tx_handler
) {
701 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
703 ret
= write(sip
->fd
, buf
, writelen
);
707 if (ret
< 0 && errno
== EAGAIN
)
709 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
714 if (ret
< writelen
) {
715 if (!sip
->tx_handler
){
717 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
720 sip
->tx_handler
= purple_input_add(sip
->fd
,
721 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
726 /* XXX: is it OK to do this? You might get part of a request sent
727 with part of another. */
728 if (sip
->txbuf
->bufused
> 0)
729 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
731 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
737 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
739 sendout_pkt(gc
, buf
);
743 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
745 GSList
*tmp
= msg
->headers
;
748 GString
*outstr
= g_string_new("");
749 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
751 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
752 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
753 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
754 tmp
= g_slist_next(tmp
);
756 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
757 sendout_pkt(sip
->gc
, outstr
->str
);
758 g_string_free(outstr
, TRUE
);
761 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
765 if (sip
->registrar
.type
== AUTH_TYPE_UNSET
) {
769 if (sip
->registrar
.gssapi_context
) {
770 struct sipmsg_breakdown msgbd
;
771 gchar
*signature_input_str
;
773 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
774 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
775 sip
->registrar
.ntlm_num
++;
776 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
777 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
778 if (signature_input_str
!= NULL
) {
779 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
780 msg
->signature
= signature_hex
;
781 msg
->rand
= g_strdup(msgbd
.rand
);
782 msg
->num
= g_strdup(msgbd
.num
);
783 g_free(signature_input_str
);
785 sipmsg_breakdown_free(&msgbd
);
788 if (sip
->registrar
.type
&& sipe_strequal(method
, "REGISTER")) {
789 buf
= auth_header(sip
, &sip
->registrar
, msg
);
791 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
794 } else if (sipe_strequal(method
,"SUBSCRIBE") || sipe_strequal(method
,"SERVICE") || sipe_strequal(method
,"MESSAGE") || sipe_strequal(method
,"INVITE") || sipe_strequal(method
, "ACK") || sipe_strequal(method
, "NOTIFY") || sipe_strequal(method
, "BYE") || sipe_strequal(method
, "INFO") || sipe_strequal(method
, "OPTIONS") || sipe_strequal(method
, "REFER")) {
795 sip
->registrar
.nc
= 3;
796 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
798 if (purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
799 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
804 buf
= auth_header(sip
, &sip
->registrar
, msg
);
805 sipmsg_add_header_now_pos(msg
, "Proxy-Authorization", buf
, 5);
808 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
812 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
813 const char *text
, const char *body
)
817 GString
*outstr
= g_string_new("");
818 struct sipe_account_data
*sip
= gc
->proto_data
;
821 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
823 /* Can return NULL! */
824 contact
= get_contact(sip
);
826 sipmsg_add_header(msg
, "Contact", contact
);
831 gchar
*len
= g_strdup_printf("%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
832 sipmsg_add_header(msg
, "Content-Length", len
);
835 sipmsg_add_header(msg
, "Content-Length", "0");
838 msg
->response
= code
;
840 sipmsg_strip_headers(msg
, keepers
);
841 sipmsg_merge_new_headers(msg
);
842 sign_outgoing_message(msg
, sip
, msg
->method
);
844 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
847 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
848 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
850 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
851 tmp
= g_slist_next(tmp
);
853 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
854 sendout_pkt(gc
, outstr
->str
);
855 g_string_free(outstr
, TRUE
);
858 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
860 if (sip
->transactions
) {
861 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
862 purple_debug_info("sipe", "sip->transactions count:%d after removal\n", g_slist_length(sip
->transactions
));
864 if (trans
->msg
) sipmsg_free(trans
->msg
);
865 if (trans
->payload
) {
866 (*trans
->payload
->destroy
)(trans
->payload
->data
);
867 g_free(trans
->payload
);
874 static struct transaction
*
875 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
877 gchar
*call_id
= NULL
;
879 struct transaction
*trans
= g_new0(struct transaction
, 1);
881 trans
->time
= time(NULL
);
882 trans
->msg
= (struct sipmsg
*)msg
;
883 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
884 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
885 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
886 trans
->callback
= callback
;
887 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
888 purple_debug_info("sipe", "sip->transactions count:%d after addition\n", g_slist_length(sip
->transactions
));
892 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
894 struct transaction
*trans
;
895 GSList
*transactions
= sip
->transactions
;
896 gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
897 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
900 if (!call_id
|| !cseq
) {
901 purple_debug(PURPLE_DEBUG_ERROR
, "sipe", "transaction_find: no Call-ID or CSeq!\n");
905 key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
906 while (transactions
) {
907 trans
= transactions
->data
;
908 if (!g_strcasecmp(trans
->key
, key
)) {
912 transactions
= transactions
->next
;
920 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
921 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
922 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
924 struct sipe_account_data
*sip
= gc
->proto_data
;
925 const char *addh
= "";
928 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
929 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
930 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
931 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
932 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
933 gchar
*route
= g_strdup("");
934 gchar
*epid
= get_epid(sip
);
935 int cseq
= dialog
? ++dialog
->cseq
: 1 /* as Call-Id is new in this case */;
936 struct transaction
*trans
= NULL
;
938 if (dialog
&& dialog
->routes
)
940 GSList
*iter
= dialog
->routes
;
945 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
947 iter
= g_slist_next(iter
);
951 if (!ourtag
&& !dialog
) {
955 if (sipe_strequal(method
, "REGISTER")) {
956 if (sip
->regcallid
) {
958 callid
= g_strdup(sip
->regcallid
);
960 sip
->regcallid
= g_strdup(callid
);
965 if (addheaders
) addh
= addheaders
;
967 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
968 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
969 "From: <sip:%s>%s%s;epid=%s\r\n"
970 "To: <%s>%s%s%s%s\r\n"
971 "Max-Forwards: 70\r\n"
976 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
978 dialog
&& dialog
->request
? dialog
->request
: url
,
979 TRANSPORT_DESCRIPTOR
,
980 purple_network_get_my_ip(-1),
982 branch
? ";branch=" : "",
983 branch
? branch
: "",
985 ourtag
? ";tag=" : "",
986 ourtag
? ourtag
: "",
989 theirtag
? ";tag=" : "",
990 theirtag
? theirtag
: "",
991 theirepid
? ";epid=" : "",
992 theirepid
? theirepid
: "",
995 sipe_get_useragent(sip
),
999 body
? (gsize
) strlen(body
) : 0,
1003 //printf ("parsing msg buf:\n%s\n\n", buf);
1004 msg
= sipmsg_parse_msg(buf
);
1015 sign_outgoing_message (msg
, sip
, method
);
1017 buf
= sipmsg_to_string (msg
);
1019 /* add to ongoing transactions */
1020 /* ACK isn't supposed to be answered ever. So we do not keep transaction for it. */
1021 if (!sipe_strequal(method
, "ACK")) {
1022 trans
= transactions_add_buf(sip
, msg
, tc
);
1026 sendout_pkt(gc
, buf
);
1033 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
1036 send_soap_request_with_cb(struct sipe_account_data
*sip
,
1039 TransCallback callback
,
1040 struct transaction_payload
*payload
)
1042 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sip
);
1043 gchar
*contact
= get_contact(sip
);
1044 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
1045 "Content-Type: application/SOAP+xml\r\n",contact
);
1047 struct transaction
*trans
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1048 trans
->payload
= payload
;
1055 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1057 send_soap_request_with_cb(sip
, NULL
, body
, NULL
, NULL
);
1060 static char *get_contact_register(struct sipe_account_data
*sip
)
1062 char *epid
= get_epid(sip
);
1063 char *uuid
= generateUUIDfromEPID(epid
);
1064 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
);
1070 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1078 if (!sip
->sipdomain
) return;
1080 uri
= sip_uri_from_name(sip
->sipdomain
);
1081 expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1082 to
= sip_uri_self(sip
);
1083 contact
= get_contact_register(sip
);
1084 hdr
= g_strdup_printf("Contact: %s\r\n"
1085 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1086 "Event: registration\r\n"
1087 "Allow-Events: presence\r\n"
1088 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1089 "%s", contact
, expires
);
1093 sip
->registerstatus
= 1;
1095 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1096 process_register_response
);
1103 static void do_register_cb(struct sipe_account_data
*sip
,
1104 SIPE_UNUSED_PARAMETER
void *unused
)
1106 do_register_exp(sip
, -1);
1107 sip
->reregister_set
= FALSE
;
1110 static void do_register(struct sipe_account_data
*sip
)
1112 do_register_exp(sip
, -1);
1116 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1118 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1119 send_soap_request(sip
, body
);
1124 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1127 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1129 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1132 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1136 void sipe_auth_user_cb(void * data
)
1138 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1141 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1146 void sipe_deny_user_cb(void * data
)
1148 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1151 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1156 sipe_add_permit(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
, TRUE
);
1163 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1165 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1166 sipe_contact_allow_deny(sip
, name
, FALSE
);
1170 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1172 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1173 sipe_contact_set_acl(sip, name, "");
1177 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1181 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1182 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1184 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| sipe_strequal(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1186 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1187 if (!watchers
) return;
1189 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1190 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1191 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1192 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1194 // TODO pull out optional displayName to pass as alias
1196 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1197 job
->who
= remote_user
;
1199 purple_account_request_authorization(
1213 xmlnode_free(watchers
);
1218 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1220 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1221 if (!purple_group
) {
1222 purple_group
= purple_group_new(group
->name
);
1223 purple_blist_add_group(purple_group
, NULL
);
1227 group
->purple_group
= purple_group
;
1228 sip
->groups
= g_slist_append(sip
->groups
, group
);
1229 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1231 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1235 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1237 struct sipe_group
*group
;
1243 entry
= sip
->groups
;
1245 group
= entry
->data
;
1246 if (group
->id
== id
) {
1249 entry
= entry
->next
;
1254 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1256 struct sipe_group
*group
;
1258 if (!sip
|| !name
) {
1262 entry
= sip
->groups
;
1264 group
= entry
->data
;
1265 if (sipe_strequal(group
->name
, name
)) {
1268 entry
= entry
->next
;
1274 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1277 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1278 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1279 send_soap_request(sip
, body
);
1281 g_free(group
->name
);
1282 group
->name
= g_strdup(name
);
1286 * Only appends if no such value already stored.
1289 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1290 GSList
* res
= list
;
1291 if (!g_slist_find_custom(list
, data
, func
)) {
1292 res
= g_slist_insert_sorted(list
, data
, func
);
1298 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1299 return group1
->id
- group2
->id
;
1303 * Returns string like "2 4 7 8" - group ids buddy belong to.
1306 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1309 //creating array from GList, converting int to gchar*
1310 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1311 GSList
*entry
= buddy
->groups
;
1313 if (!ids_arr
) return NULL
;
1316 struct sipe_group
* group
= entry
->data
;
1317 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1318 entry
= entry
->next
;
1322 res
= g_strjoinv(" ", ids_arr
);
1323 g_strfreev(ids_arr
);
1328 * Sends buddy update to server
1331 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1333 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1334 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1336 if (buddy
&& purple_buddy
) {
1337 const char *alias
= purple_buddy_get_alias(purple_buddy
);
1338 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1341 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1343 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1344 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1346 send_soap_request(sip
, body
);
1353 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1355 if (msg
->response
== 200) {
1356 struct sipe_group
*group
;
1357 struct group_user_context
*ctx
= trans
->payload
->data
;
1361 struct sipe_buddy
*buddy
;
1363 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1368 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1374 group_id
= xmlnode_get_data(node
);
1380 group
= g_new0(struct sipe_group
, 1);
1381 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1383 group
->name
= g_strdup(ctx
->group_name
);
1385 sipe_group_add(sip
, group
);
1387 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1389 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1392 sipe_group_set_user(sip
, ctx
->user_name
);
1400 static void sipe_group_context_destroy(gpointer data
)
1402 struct group_user_context
*ctx
= data
;
1403 g_free(ctx
->group_name
);
1404 g_free(ctx
->user_name
);
1408 static void sipe_group_create (struct sipe_account_data
*sip
, const gchar
*name
, const gchar
* who
)
1410 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
1411 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
1413 ctx
->group_name
= g_strdup(name
);
1414 ctx
->user_name
= g_strdup(who
);
1415 payload
->destroy
= sipe_group_context_destroy
;
1416 payload
->data
= ctx
;
1418 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1419 send_soap_request_with_cb(sip
, NULL
, body
, process_add_group_response
, payload
);
1424 * Data structure for scheduled actions
1427 struct scheduled_action
{
1430 * Format is <Event>[<Data>...]
1431 * Example: <presence><sip:user@domain.com> or <registration>
1434 guint timeout_handler
;
1435 gboolean repetitive
;
1437 GDestroyNotify destroy
;
1438 struct sipe_account_data
*sip
;
1444 * Should return FALSE if repetitive action is not needed
1446 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1449 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1450 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1451 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1452 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1453 ret
= sched_action
->repetitive
;
1454 if (sched_action
->destroy
) {
1455 (*sched_action
->destroy
)(sched_action
->payload
);
1457 g_free(sched_action
->name
);
1458 g_free(sched_action
);
1463 * Kills action timer effectively cancelling
1466 * @param name of action
1468 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1472 if (!sip
->timeouts
|| !name
) return;
1474 entry
= sip
->timeouts
;
1476 struct scheduled_action
*sched_action
= entry
->data
;
1477 if(sipe_strequal(sched_action
->name
, name
)) {
1478 GSList
*to_delete
= entry
;
1479 entry
= entry
->next
;
1480 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1481 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1482 purple_timeout_remove(sched_action
->timeout_handler
);
1483 if (sched_action
->destroy
) {
1484 (*sched_action
->destroy
)(sched_action
->payload
);
1486 g_free(sched_action
->name
);
1487 g_free(sched_action
);
1489 entry
= entry
->next
;
1495 sipe_schedule_action0(const gchar
*name
,
1499 GDestroyNotify destroy
,
1500 struct sipe_account_data
*sip
,
1503 struct scheduled_action
*sched_action
;
1505 /* Make sure each action only exists once */
1506 sipe_cancel_scheduled_action(sip
, name
);
1508 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1509 sched_action
= g_new0(struct scheduled_action
, 1);
1510 sched_action
->repetitive
= FALSE
;
1511 sched_action
->name
= g_strdup(name
);
1512 sched_action
->action
= action
;
1513 sched_action
->destroy
= destroy
;
1514 sched_action
->sip
= sip
;
1515 sched_action
->payload
= payload
;
1516 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1517 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1518 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1519 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1523 sipe_schedule_action(const gchar
*name
,
1526 GDestroyNotify destroy
,
1527 struct sipe_account_data
*sip
,
1530 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1534 * Same as sipe_schedule_action() but timeout is in milliseconds.
1537 sipe_schedule_action_msec(const gchar
*name
,
1540 GDestroyNotify destroy
,
1541 struct sipe_account_data
*sip
,
1544 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1548 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1549 time_t calculate_from
);
1552 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
);
1555 sipe_get_status_by_availability(int avail
,
1559 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
1560 const char *status_id
,
1561 const char *message
,
1562 time_t do_not_publish
[]);
1565 sipe_apply_calendar_status(struct sipe_account_data
*sip
,
1566 struct sipe_buddy
*sbuddy
,
1567 const char *status_id
)
1569 time_t cal_avail_since
;
1570 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
1574 if (!sbuddy
) return;
1576 if (cal_status
< SIPE_CAL_NO_DATA
) {
1577 purple_debug_info("sipe", "sipe_apply_calendar_status: cal_status : %d for %s\n", cal_status
, sbuddy
->name
);
1578 purple_debug_info("sipe", "sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
1581 /* scheduled Cal update call */
1583 status_id
= sbuddy
->last_non_cal_status_id
;
1584 g_free(sbuddy
->activity
);
1585 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
1589 purple_debug_info("sipe", "sipe_apply_calendar_status: status_id is NULL for %s, exiting.\n",
1590 sbuddy
->name
? sbuddy
->name
: "" );
1594 /* adjust to calendar status */
1595 if (cal_status
!= SIPE_CAL_NO_DATA
) {
1596 purple_debug_info("sipe", "sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
1598 if (cal_status
== SIPE_CAL_BUSY
1599 && cal_avail_since
> sbuddy
->user_avail_since
1600 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
1602 status_id
= SIPE_STATUS_ID_BUSY
;
1603 g_free(sbuddy
->activity
);
1604 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
));
1606 avail
= sipe_get_availability_by_status(status_id
, NULL
);
1608 purple_debug_info("sipe", "sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
1609 if (cal_avail_since
> sbuddy
->activity_since
) {
1610 if (cal_status
== SIPE_CAL_OOF
1611 && avail
>= 15000) /* 12000 in 2007 */
1613 g_free(sbuddy
->activity
);
1614 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
1619 /* then set status_id actually */
1620 purple_debug_info("sipe", "sipe_apply_calendar_status: to %s for %s\n", status_id
, sbuddy
->name
? sbuddy
->name
: "" );
1621 purple_prpl_got_user_status(sip
->account
, sbuddy
->name
, status_id
, NULL
);
1623 /* set our account state to the one in roaming (including calendar info) */
1624 self_uri
= sip_uri_self(sip
);
1625 if (sip
->initial_state_published
&& sipe_strequal(sbuddy
->name
, self_uri
)) {
1626 if (sipe_strequal(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
1627 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
1630 purple_debug_info("sipe", "sipe_apply_calendar_status: switch to '%s' for the account\n", sip
->status
);
1631 sipe_set_purple_account_status_and_note(sip
->account
, status_id
, sip
->note
, sip
->do_not_publish
);
1637 sipe_got_user_status(struct sipe_account_data
*sip
,
1639 const char *status_id
)
1641 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
1643 if (!sbuddy
) return;
1645 /* Check if on 2005 system contact's calendar,
1646 * then set/preserve it.
1648 if (!sip
->ocs2007
) {
1649 sipe_apply_calendar_status(sip
, sbuddy
, status_id
);
1651 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
1656 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
1657 struct sipe_buddy
*sbuddy
,
1658 struct sipe_account_data
*sip
)
1660 sipe_apply_calendar_status(sip
, sbuddy
, NULL
);
1664 * Updates contact's status
1665 * based on their calendar information.
1667 * Applicability: 2005 systems
1670 update_calendar_status(struct sipe_account_data
*sip
)
1672 purple_debug_info("sipe", "update_calendar_status() started.\n");
1673 g_hash_table_foreach(sip
->buddies
, (GHFunc
)update_calendar_status_cb
, (gpointer
)sip
);
1675 /* repeat scheduling */
1676 sipe_sched_calendar_status_update(sip
, time(NULL
) + 3*60 /* 3 min */);
1680 * Schedules process of contacts' status update
1681 * based on their calendar information.
1682 * Should be scheduled to the beginning of every
1683 * 15 min interval, like:
1684 * 13:00, 13:15, 13:30, 13:45, etc.
1686 * Applicability: 2005 systems
1689 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1690 time_t calculate_from
)
1692 int interval
= 15*60;
1693 /** start of the beginning of closest 15 min interval. */
1694 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1696 purple_debug_info("sipe", "sipe_sched_calendar_status_update: calculate_from time: %s",
1697 asctime(localtime(&calculate_from
)));
1698 purple_debug_info("sipe", "sipe_sched_calendar_status_update: next start time : %s",
1699 asctime(localtime(&next_start
)));
1701 sipe_schedule_action("<+2005-cal-status>",
1702 (int)(next_start
- time(NULL
)),
1703 (Action
)update_calendar_status
,
1710 * Schedules process of self status publish
1711 * based on own calendar information.
1712 * Should be scheduled to the beginning of every
1713 * 15 min interval, like:
1714 * 13:00, 13:15, 13:30, 13:45, etc.
1716 * Applicability: 2007+ systems
1719 sipe_sched_calendar_status_self_publish(struct sipe_account_data
*sip
,
1720 time_t calculate_from
)
1722 int interval
= 5*60;
1723 /** start of the beginning of closest 5 min interval. */
1724 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1726 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: calculate_from time: %s",
1727 asctime(localtime(&calculate_from
)));
1728 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: next start time : %s",
1729 asctime(localtime(&next_start
)));
1731 sipe_schedule_action("<+2007-cal-status>",
1732 (int)(next_start
- time(NULL
)),
1733 (Action
)publish_calendar_status_self
,
1739 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1741 /** Should be g_free()'d
1744 sipe_get_subscription_key(const gchar
*event
,
1749 if (is_empty(event
)) return NULL
;
1751 if (event
&& !g_ascii_strcasecmp(event
, "presence")) {
1752 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1753 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, with
);
1755 /* @TODO drop participated buddies' just_added flag */
1757 /* Subscription is identified by <event> key */
1758 key
= g_strdup_printf("<%s>", event
);
1764 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
1765 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
1767 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
1768 gchar
*event
= sipmsg_find_header(msg
, "Event");
1771 /* The case with 2005 Public IM Connectivity (PIC) - no Event header */
1773 struct sipmsg
*request_msg
= trans
->msg
;
1774 event
= sipmsg_find_header(request_msg
, "Event");
1777 key
= sipe_get_subscription_key(event
, with
);
1779 /* 200 OK; 481 Call Leg Does Not Exist */
1780 if (key
&& (msg
->response
== 200 || msg
->response
== 481)) {
1781 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
1782 g_hash_table_remove(sip
->subscriptions
, key
);
1783 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
1787 /* create/store subscription dialog if not yet */
1788 if (msg
->response
== 200) {
1789 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1790 gchar
*cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
1793 struct sip_subscription
*subscription
= g_new0(struct sip_subscription
, 1);
1794 g_hash_table_insert(sip
->subscriptions
, g_strdup(key
), subscription
);
1796 subscription
->dialog
.callid
= g_strdup(callid
);
1797 subscription
->dialog
.cseq
= atoi(cseq
);
1798 subscription
->dialog
.with
= g_strdup(with
);
1799 subscription
->event
= g_strdup(event
);
1800 sipe_dialog_parse(&subscription
->dialog
, msg
, TRUE
);
1802 purple_debug_info("sipe", "process_subscribe_response: subscription dialog added for: %s\n", key
);
1811 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1813 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1818 static void sipe_subscribe_resource_uri(const char *name
,
1819 SIPE_UNUSED_PARAMETER gpointer value
,
1820 gchar
**resources_uri
)
1822 gchar
*tmp
= *resources_uri
;
1823 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1827 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1829 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1830 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1831 gchar
*tmp
= *resources_uri
;
1833 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
1835 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
1840 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1841 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1842 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1843 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1844 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1847 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1850 gchar
*contact
= get_contact(sip
);
1853 gchar
*require
= "";
1855 gchar
*autoextend
= "";
1856 gchar
*content_type
;
1857 struct sip_dialog
*dialog
;
1860 require
= ", categoryList";
1861 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1862 content_type
= "application/msrtc-adrl-categorylist+xml";
1863 content
= g_strdup_printf(
1864 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1865 "<action name=\"subscribe\" id=\"63792024\">\n"
1866 "<adhocList>\n%s</adhocList>\n"
1867 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1868 "<category name=\"calendarData\"/>\n"
1869 "<category name=\"contactCard\"/>\n"
1870 "<category name=\"note\"/>\n"
1871 "<category name=\"state\"/>\n"
1874 "</batchSub>", sip
->username
, resources_uri
);
1876 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1877 content_type
= "application/adrl+xml";
1878 content
= g_strdup_printf(
1879 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1880 "<create xmlns=\"\">\n%s</create>\n"
1881 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1883 g_free(resources_uri
);
1885 request
= g_strdup_printf(
1886 "Require: adhoclist%s\r\n"
1887 "Supported: eventlist\r\n"
1888 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1889 "Supported: ms-piggyback-first-notify\r\n"
1890 "%sSupported: ms-benotify\r\n"
1891 "Proxy-Require: ms-benotify\r\n"
1892 "Event: presence\r\n"
1893 "Content-Type: %s\r\n"
1894 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1897 /* subscribe to buddy presence */
1898 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1899 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1900 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1901 purple_debug_info("sipe", "sipe_subscribe_presence_batched_to: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
1903 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
1911 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
,
1912 SIPE_UNUSED_PARAMETER
void *unused
)
1914 gchar
*to
= sip_uri_self(sip
);
1915 gchar
*resources_uri
= g_strdup("");
1917 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1919 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1922 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1925 struct presence_batched_routed
{
1930 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1932 struct presence_batched_routed
*data
= payload
;
1933 GSList
*buddies
= data
->buddies
;
1935 g_free(buddies
->data
);
1936 buddies
= buddies
->next
;
1938 g_slist_free(data
->buddies
);
1943 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
1945 struct presence_batched_routed
*data
= payload
;
1946 GSList
*buddies
= data
->buddies
;
1947 gchar
*resources_uri
= g_strdup("");
1949 gchar
*tmp
= resources_uri
;
1950 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
1952 buddies
= buddies
->next
;
1954 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
1955 g_strdup(data
->host
));
1959 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1960 * The user sends a single SUBSCRIBE request to the subscribed contact.
1961 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1965 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
1969 gchar
*to
= sip_uri((char *)buddy_name
);
1970 gchar
*tmp
= get_contact(sip
);
1972 gchar
*content
= NULL
;
1973 gchar
*autoextend
= "";
1974 gchar
*content_type
= "";
1975 struct sip_dialog
*dialog
;
1976 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, to
);
1977 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1979 if (sbuddy
) sbuddy
->just_added
= FALSE
;
1982 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
1984 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1987 request
= g_strdup_printf(
1988 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1989 "Supported: ms-piggyback-first-notify\r\n"
1990 "%s%sSupported: ms-benotify\r\n"
1991 "Proxy-Require: ms-benotify\r\n"
1992 "Event: presence\r\n"
1993 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
1996 content
= g_strdup_printf(
1997 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1998 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1999 "<resource uri=\"%s\"%s\n"
2001 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
2002 "<category name=\"calendarData\"/>\n"
2003 "<category name=\"contactCard\"/>\n"
2004 "<category name=\"note\"/>\n"
2005 "<category name=\"state\"/>\n"
2008 "</batchSub>", sip
->username
, to
, context
);
2013 /* subscribe to buddy presence */
2014 /* Subscription is identified by ACTION_NAME_PRESENCE key */
2015 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
2016 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2017 purple_debug_info("sipe", "sipe_subscribe_presence_single: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2019 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
2027 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
2029 purple_debug_info("sipe", "sipe_set_status: status=%s\n", purple_status_get_id(status
));
2031 if (!purple_status_is_active(status
))
2035 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
2040 time_t now
= time(NULL
);
2041 const char *status_id
= purple_status_get_id(status
);
2042 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
2043 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
2044 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
2046 /* when other point of presence clears note, but we are keeping
2047 * state if OOF note.
2049 if (do_not_publish
&& !note
&& sip
->ews
&& sip
->ews
->oof_note
) {
2050 purple_debug_info("sipe", "sipe_set_status: enabling publication as OOF note keepers.\n");
2051 do_not_publish
= FALSE
;
2054 purple_debug_info("sipe", "sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d\n",
2055 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
2057 sip
->do_not_publish
[activity
] = 0;
2058 purple_debug_info("sipe", "sipe_set_status: set: sip->do_not_publish[%s]=%d [0]\n",
2059 status_id
, (int)sip
->do_not_publish
[activity
]);
2063 purple_debug_info("sipe", "sipe_set_status: publication was switched off, exiting.\n");
2067 g_free(sip
->status
);
2068 sip
->status
= g_strdup(status_id
);
2070 /* hack to escape apostrof before comparison */
2071 tmp
= note
? purple_strreplace(note
, "'", "'") : NULL
;
2073 /* this will preserve OOF flag as well */
2074 if (!sipe_strequal(tmp
, sip
->note
)) {
2075 sip
->is_oof_note
= FALSE
;
2077 sip
->note
= g_strdup(note
);
2078 sip
->note_since
= time(NULL
);
2082 /* schedule 2 sec to capture idle flag */
2083 action_name
= g_strdup_printf("<%s>", "+set-status");
2084 sipe_schedule_action(action_name
, SIPE_IDLE_SET_DELAY
, (Action
)send_presence_status
, NULL
, sip
, NULL
);
2085 g_free(action_name
);
2090 sipe_set_idle(PurpleConnection
* gc
,
2093 purple_debug_info("sipe", "sipe_set_idle: interval=%d\n", interval
);
2096 struct sipe_account_data
*sip
= gc
->proto_data
;
2099 sip
->idle_switch
= time(NULL
);
2100 purple_debug_info("sipe", "sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
2106 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
,
2107 SIPE_UNUSED_PARAMETER
const char *alias
)
2109 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2110 sipe_group_set_user(sip
, name
);
2114 sipe_group_buddy(PurpleConnection
*gc
,
2116 const char *old_group_name
,
2117 const char *new_group_name
)
2119 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2120 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
2121 struct sipe_group
* old_group
= NULL
;
2122 struct sipe_group
* new_group
;
2124 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
2125 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
2127 if(!buddy
) { // buddy not in roaming list
2131 if (old_group_name
) {
2132 old_group
= sipe_group_find_by_name(sip
, old_group_name
);
2134 new_group
= sipe_group_find_by_name(sip
, new_group_name
);
2137 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
2138 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
2142 sipe_group_create(sip
, new_group_name
, who
);
2144 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
2145 sipe_group_set_user(sip
, who
);
2149 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2151 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2153 /* libpurple can call us with undefined buddy or group */
2154 if (buddy
&& group
) {
2155 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2157 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2158 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
2159 purple_blist_rename_buddy(buddy
, buddy_name
);
2162 /* Prepend sip: if needed */
2163 if (!g_str_has_prefix(buddy
->name
, "sip:")) {
2164 gchar
*buf
= sip_uri_from_name(buddy
->name
);
2165 purple_blist_rename_buddy(buddy
, buf
);
2169 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
2170 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
2171 purple_debug_info("sipe", "sipe_add_buddy: adding %s\n", buddy
->name
);
2172 b
->name
= g_strdup(buddy
->name
);
2173 b
->just_added
= TRUE
;
2174 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
2175 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
2176 /* @TODO should go to callback */
2177 sipe_subscribe_presence_single(sip
, b
->name
);
2179 purple_debug_info("sipe", "sipe_add_buddy: buddy %s already in internal list\n", buddy
->name
);
2184 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
2188 * We are calling g_hash_table_foreach_steal(). That means that no
2189 * key/value deallocation functions are called. Therefore the glib
2190 * hash code does not touch the key (buddy->name) or value (buddy)
2191 * of the to-be-deleted hash node at all. It follows that we
2193 * - MUST free the memory for the key ourselves and
2194 * - ARE allowed to do it in this function
2196 * Conclusion: glib must be broken on the Windows platform if sipe
2197 * crashes with SIGTRAP when closing. You'll have to live
2198 * with the memory leak until this is fixed.
2200 g_free(buddy
->name
);
2202 g_free(buddy
->activity
);
2203 g_free(buddy
->meeting_subject
);
2204 g_free(buddy
->meeting_location
);
2205 g_free(buddy
->note
);
2207 g_free(buddy
->cal_start_time
);
2208 g_free(buddy
->cal_free_busy_base64
);
2209 g_free(buddy
->cal_free_busy
);
2210 g_free(buddy
->last_non_cal_activity
);
2212 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
2214 g_free(buddy
->device_name
);
2215 g_slist_free(buddy
->groups
);
2220 * Unassociates buddy from group first.
2221 * Then see if no groups left, removes buddy completely.
2222 * Otherwise updates buddy groups on server.
2224 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2226 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2227 struct sipe_buddy
*b
;
2228 struct sipe_group
*g
= NULL
;
2230 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2233 b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
2237 g
= sipe_group_find_by_name(sip
, group
->name
);
2241 b
->groups
= g_slist_remove(b
->groups
, g
);
2242 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
2245 if (g_slist_length(b
->groups
) < 1) {
2246 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
2247 sipe_cancel_scheduled_action(sip
, action_name
);
2248 g_free(action_name
);
2250 g_hash_table_remove(sip
->buddies
, buddy
->name
);
2253 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
2254 send_soap_request(sip
, body
);
2260 //updates groups on server
2261 sipe_group_set_user(sip
, b
->name
);
2267 sipe_rename_group(PurpleConnection
*gc
,
2268 const char *old_name
,
2270 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
2272 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2273 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, old_name
);
2275 sipe_group_rename(sip
, s_group
, group
->name
);
2277 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
2282 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
2284 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2285 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
2288 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
2289 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
2290 send_soap_request(sip
, body
);
2293 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
2294 g_free(s_group
->name
);
2297 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
2301 /** All statuses need message attribute to pass Note */
2302 static GList
*sipe_status_types(SIPE_UNUSED_PARAMETER PurpleAccount
*acc
)
2304 PurpleStatusType
*type
;
2305 GList
*types
= NULL
;
2307 /* Macros to reduce code repetition.
2308 Translators: noun */
2309 #define SIPE_ADD_STATUS(prim,id,name,user) type = purple_status_type_new_with_attrs( \
2311 TRUE, user, FALSE, \
2312 SIPE_STATUS_ATTR_ID_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), \
2314 types = g_list_append(types, type);
2317 SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE
,
2323 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2324 sipe_activity_map
[SIPE_ACTIVITY_BUSY
].status_id
,
2325 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSY
),
2328 /* Do Not Disturb */
2329 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2330 sipe_activity_map
[SIPE_ACTIVITY_DND
].status_id
,
2335 /* Goes first in the list as
2336 * purple picks the first status with the AWAY type
2339 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2345 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2346 sipe_activity_map
[SIPE_ACTIVITY_BRB
].status_id
,
2347 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BRB
),
2350 /* Appear Offline */
2351 SIPE_ADD_STATUS(PURPLE_STATUS_INVISIBLE
,
2356 /* Offline (not user settable) */
2357 SIPE_ADD_STATUS(PURPLE_STATUS_OFFLINE
,
2366 * A callback for g_hash_table_foreach
2369 sipe_buddy_subscribe_cb(char *buddy_name
,
2370 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
2371 struct sipe_account_data
*sip
)
2373 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy_name
);
2374 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
2375 guint time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
2376 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
2378 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy_name
));
2379 g_free(action_name
);
2383 * Removes entries from purple buddy list
2384 * that does not correspond ones in the roaming contact list.
2386 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
2387 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
2388 GSList
*entry
= buddies
;
2389 struct sipe_buddy
*buddy
;
2393 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
2394 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
2397 g
= purple_buddy_get_group(b
);
2398 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
2400 gboolean in_sipe_groups
= FALSE
;
2401 GSList
*entry2
= buddy
->groups
;
2403 struct sipe_group
*group
= entry2
->data
;
2404 if (sipe_strequal(group
->name
, g
->name
)) {
2405 in_sipe_groups
= TRUE
;
2408 entry2
= entry2
->next
;
2410 if(!in_sipe_groups
) {
2411 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
2412 purple_blist_remove_buddy(b
);
2415 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
2416 purple_blist_remove_buddy(b
);
2418 entry
= entry
->next
;
2420 g_slist_free(buddies
);
2423 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2425 int len
= msg
->bodylen
;
2427 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
2430 const gchar
*contacts_delta
;
2431 xmlnode
*group_node
;
2432 if (!g_str_has_prefix(tmp
, "vnd-microsoft-roaming-contacts")) {
2436 /* Convert the contact from XML to Purple Buddies */
2437 isc
= xmlnode_from_str(msg
->body
, len
);
2442 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
2443 if (contacts_delta
) {
2444 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2447 if (sipe_strequal(isc
->name
, "contactList")) {
2450 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
2451 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2452 const char *name
= xmlnode_get_attrib(group_node
, "name");
2454 if (g_str_has_prefix(name
, "~")) {
2455 name
= _("Other Contacts");
2457 group
->name
= g_strdup(name
);
2458 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
2460 sipe_group_add(sip
, group
);
2463 // Make sure we have at least one group
2464 if (g_slist_length(sip
->groups
) == 0) {
2465 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2466 PurpleGroup
*purple_group
;
2467 group
->name
= g_strdup(_("Other Contacts"));
2469 purple_group
= purple_group_new(group
->name
);
2470 purple_blist_add_group(purple_group
, NULL
);
2471 sip
->groups
= g_slist_append(sip
->groups
, group
);
2474 /* Parse contacts */
2475 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
2476 const gchar
*uri
= xmlnode_get_attrib(item
, "uri");
2477 const gchar
*name
= xmlnode_get_attrib(item
, "name");
2479 struct sipe_buddy
*buddy
= NULL
;
2481 gchar
**item_groups
;
2484 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2485 tmp
= sip_uri_from_name(uri
);
2486 buddy_name
= g_ascii_strdown(tmp
, -1);
2489 /* assign to group Other Contacts if nothing else received */
2490 tmp
= g_strdup(xmlnode_get_attrib(item
, "groups"));
2492 struct sipe_group
*group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
2494 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
2496 item_groups
= g_strsplit(tmp
, " ", 0);
2499 while (item_groups
[i
]) {
2500 struct sipe_group
*group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2502 // If couldn't find the right group for this contact, just put them in the first group we have
2503 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2504 group
= sip
->groups
->data
;
2507 if (group
!= NULL
) {
2508 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2510 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2511 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2513 purple_debug_info("sipe", "Created new buddy %s with alias %s\n", buddy_name
, uri
);
2516 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
2517 if (name
!= NULL
&& strlen(name
) != 0) {
2518 purple_blist_alias_buddy(b
, name
);
2520 purple_debug_info("sipe", "Replaced buddy %s alias with %s\n", buddy_name
, name
);
2525 buddy
= g_new0(struct sipe_buddy
, 1);
2526 buddy
->name
= g_strdup(b
->name
);
2527 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2530 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2532 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2534 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2539 } // while, contact groups
2540 g_strfreev(item_groups
);
2545 sipe_cleanup_local_blist(sip
);
2547 /* Add self-contact if not there yet. 2005 systems. */
2548 /* This will resemble subscription to roaming_self in 2007 systems */
2549 if (!sip
->ocs2007
) {
2550 gchar
*self_uri
= sip_uri_self(sip
);
2551 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, self_uri
);
2554 buddy
= g_new0(struct sipe_buddy
, 1);
2555 buddy
->name
= g_strdup(self_uri
);
2556 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2563 /* subscribe to buddies */
2564 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2565 if (sip
->batched_support
) {
2566 sipe_subscribe_presence_batched(sip
, NULL
);
2568 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2570 sip
->subscribed_buddies
= TRUE
;
2572 /* for 2005 systems schedule contacts' status update
2573 * based on their calendar information
2575 if (!sip
->ocs2007
) {
2576 sipe_sched_calendar_status_update(sip
, time(NULL
));
2583 * Subscribe roaming contacts
2585 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
)
2587 gchar
*to
= sip_uri_self(sip
);
2588 gchar
*tmp
= get_contact(sip
);
2589 gchar
*hdr
= g_strdup_printf(
2590 "Event: vnd-microsoft-roaming-contacts\r\n"
2591 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2592 "Supported: com.microsoft.autoextend\r\n"
2593 "Supported: ms-benotify\r\n"
2594 "Proxy-Require: ms-benotify\r\n"
2595 "Supported: ms-piggyback-first-notify\r\n"
2596 "Contact: %s\r\n", tmp
);
2599 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2604 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
,
2605 SIPE_UNUSED_PARAMETER
void *unused
)
2608 struct sip_dialog
*dialog
;
2609 gchar
*to
= sip_uri_self(sip
);
2610 gchar
*tmp
= get_contact(sip
);
2611 gchar
*hdr
= g_strdup_printf(
2612 "Event: presence.wpending\r\n"
2613 "Accept: text/xml+msrtc.wpending\r\n"
2614 "Supported: com.microsoft.autoextend\r\n"
2615 "Supported: ms-benotify\r\n"
2616 "Proxy-Require: ms-benotify\r\n"
2617 "Supported: ms-piggyback-first-notify\r\n"
2618 "Contact: %s\r\n", tmp
);
2621 /* Subscription is identified by <event> key */
2622 key
= g_strdup_printf("<%s>", "presence.wpending");
2623 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2624 purple_debug_info("sipe", "sipe_subscribe_presence_wpending: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2626 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", dialog
, process_subscribe_response
);
2634 * Fires on deregistration event initiated by server.
2635 * [MS-SIPREGE] SIP extension.
2640 // Content-Type: text/registration-event
2641 // subscription-state: terminated;expires=0
2642 // ms-diagnostics-public: 4141;reason="User disabled"
2644 // deregistered;event=rejected
2646 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2648 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2649 gchar
*event
= NULL
;
2650 gchar
*reason
= NULL
;
2651 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2653 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2654 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2656 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2657 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2658 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2659 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2661 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2665 if (warning
!= NULL
) {
2666 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2667 } else { // for LCS2005
2669 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2670 error_id
= 4140; // [MS-SIPREGE]
2671 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2672 reason
= g_strdup(_("you are already signed in at another location"));
2673 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2675 reason
= g_strdup(_("user disabled")); // [MS-OCER]
2676 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2678 reason
= g_strdup(_("user moved")); // [MS-OCER]
2682 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
2685 sip
->gc
->wants_to_die
= TRUE
;
2686 purple_connection_error(sip
->gc
, warning
);
2691 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2693 xmlnode
*xn_provision_group_list
;
2696 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2698 /* provisionGroup */
2699 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2700 if (sipe_strequal("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2701 g_free(sip
->focus_factory_uri
);
2702 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2703 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2704 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2708 xmlnode_free(xn_provision_group_list
);
2711 /** for 2005 system */
2713 sipe_process_provisioning(struct sipe_account_data
*sip
,
2716 xmlnode
*xn_provision
;
2719 xn_provision
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2720 if ((node
= xmlnode_get_child(xn_provision
, "user"))) {
2721 purple_debug_info("sipe", "sipe_process_provisioning: uri=%s\n", xmlnode_get_attrib(node
, "uri"));
2722 if ((node
= xmlnode_get_child(node
, "line"))) {
2723 const gchar
*line_uri
= xmlnode_get_attrib(node
, "uri");
2724 const gchar
*server
= xmlnode_get_attrib(node
, "server");
2725 purple_debug_info("sipe", "sipe_process_provisioning: line_uri=%s server=%s\n", line_uri
, server
);
2726 sip_csta_open(sip
, line_uri
, server
);
2729 xmlnode_free(xn_provision
);
2732 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2734 const gchar
*contacts_delta
;
2737 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2743 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2746 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2753 free_container(struct sipe_container
*container
)
2757 if (!container
) return;
2759 entry
= container
->members
;
2761 void *data
= entry
->data
;
2762 entry
= g_slist_remove(entry
, data
);
2769 * Finds locally stored MS-PRES container member
2771 static struct sipe_container_member
*
2772 sipe_find_container_member(struct sipe_container
*container
,
2776 struct sipe_container_member
*member
;
2779 if (container
== NULL
|| type
== NULL
) {
2783 entry
= container
->members
;
2785 member
= entry
->data
;
2786 if (!g_strcasecmp(member
->type
, type
)
2787 && ((!member
->value
&& !value
)
2788 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2792 entry
= entry
->next
;
2798 * Finds locally stored MS-PRES container by id
2800 static struct sipe_container
*
2801 sipe_find_container(struct sipe_account_data
*sip
,
2804 struct sipe_container
*container
;
2811 entry
= sip
->containers
;
2813 container
= entry
->data
;
2814 if (id
== container
->id
) {
2817 entry
= entry
->next
;
2831 sipe_find_access_level(struct sipe_account_data
*sip
,
2835 guint containers
[] = {32000, 400, 300, 200, 100};
2838 for (i
= 0; i
< 5; i
++) {
2839 struct sipe_container_member
*member
;
2840 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2841 if (!container
) continue;
2843 member
= sipe_find_container_member(container
, type
, value
);
2845 return containers
[i
];
2853 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2855 guint container_version
,
2856 const gchar
* action
,
2860 gchar
*self
= sip_uri_self(sip
);
2861 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2864 gchar
*body
= g_strdup_printf(
2865 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2866 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2867 "</setContainerMembers>",
2875 contact
= get_contact(sip
);
2876 hdr
= g_strdup_printf("Contact: %s\r\n"
2877 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2880 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2888 free_publication(struct sipe_publication
*publication
)
2890 g_free(publication
->category
);
2891 g_free(publication
->cal_event_hash
);
2892 g_free(publication
->note
);
2894 g_free(publication
->working_hours_xml_str
);
2895 g_free(publication
->fb_start_str
);
2896 g_free(publication
->free_busy_base64
);
2898 g_free(publication
);
2901 /* key is <category><instance><container> */
2903 sipe_is_our_publication(struct sipe_account_data
*sip
,
2908 /* filling keys for our publications if not yet cached */
2909 if (!sip
->our_publication_keys
) {
2910 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
2911 guint machine_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
2912 guint user_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
);
2913 guint calendar_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
2914 guint cal_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
);
2915 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
2916 guint note_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
);
2918 purple_debug_info("sipe", "* Our Publication Instances *\n");
2919 purple_debug_info("sipe", "\tDevice : %u\t0x%08X\n", device_instance
, device_instance
);
2920 purple_debug_info("sipe", "\tMachine State : %u\t0x%08X\n", machine_instance
, machine_instance
);
2921 purple_debug_info("sipe", "\tUser Stare : %u\t0x%08X\n", user_instance
, user_instance
);
2922 purple_debug_info("sipe", "\tCalendar State : %u\t0x%08X\n", calendar_instance
, calendar_instance
);
2923 purple_debug_info("sipe", "\tCalendar OOF State : %u\t0x%08X\n", cal_oof_instance
, cal_oof_instance
);
2924 purple_debug_info("sipe", "\tCalendar FreeBusy : %u\t0x%08X\n", cal_data_instance
, cal_data_instance
);
2925 purple_debug_info("sipe", "\tOOF Note : %u\t0x%08X\n", note_oof_instance
, note_oof_instance
);
2926 purple_debug_info("sipe", "\tNote : %u\n", 0);
2927 purple_debug_info("sipe", "\tCalendar WorkingHours: %u\n", 0);
2930 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2931 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
2933 /* state:machineState */
2934 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2935 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
2936 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2937 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
2939 /* state:userState */
2940 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2941 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
2942 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2943 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
2945 /* state:calendarState */
2946 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2947 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
2948 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2949 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
2951 /* state:calendarState OOF */
2952 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2953 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
2954 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2955 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
2958 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2959 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
2960 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2961 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
2962 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2963 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
2966 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2967 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
2968 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2969 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
2970 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2971 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
2973 /* calendarData:WorkingHours */
2974 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2975 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
2976 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2977 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
2978 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2979 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
2980 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2981 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
2982 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2983 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
2984 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2985 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
2987 /* calendarData:FreeBusy */
2988 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2989 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
2990 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2991 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
2992 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2993 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
2994 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2995 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
2996 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2997 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
2998 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2999 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
3001 //purple_debug_info("sipe", "sipe_is_our_publication: sip->our_publication_keys length=%d\n",
3002 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
3005 //purple_debug_info("sipe", "sipe_is_our_publication: key=%s\n", key);
3007 entry
= sip
->our_publication_keys
;
3009 //purple_debug_info("sipe", " sipe_is_our_publication: entry->data=%s\n", entry->data);
3010 if (sipe_strequal(entry
->data
, key
)) {
3013 entry
= entry
->next
;
3018 /** Property names to store in blist.xml */
3019 #define ALIAS_PROP "alias"
3020 #define EMAIL_PROP "email"
3021 #define PHONE_PROP "phone"
3022 #define PHONE_DISPLAY_PROP "phone-display"
3023 #define PHONE_MOBILE_PROP "phone-mobile"
3024 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
3025 #define PHONE_HOME_PROP "phone-home"
3026 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
3027 #define PHONE_OTHER_PROP "phone-other"
3028 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
3029 #define PHONE_CUSTOM1_PROP "phone-custom1"
3030 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
3031 #define SITE_PROP "site"
3032 #define COMPANY_PROP "company"
3033 #define DEPARTMENT_PROP "department"
3034 #define TITLE_PROP "title"
3035 #define OFFICE_PROP "office"
3036 /** implies work address */
3037 #define ADDRESS_STREET_PROP "address-street"
3038 #define ADDRESS_CITY_PROP "address-city"
3039 #define ADDRESS_STATE_PROP "address-state"
3040 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
3041 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
3044 * Tries to figure out user first and last name
3045 * based on Display Name and email properties.
3047 * Allocates memory - must be g_free()'d
3049 * Examples to parse:
3051 * First Last - Company Name
3054 * Last, First (C)(STP) (Company)
3055 * first.last@company.com (preprocessed as "first last")
3056 * first.last.company.com@reuters.net (preprocessed as "first last company com")
3058 * Unusable examples:
3059 * user@company.com (preprocessed as "user")
3060 * first.m.last@company.com (preprocessed as "first m last")
3061 * user.company.com@reuters.net (preprocessed as "user company com")
3064 sipe_get_first_last_names(struct sipe_account_data
*sip
,
3069 PurpleBuddy
*p_buddy
;
3072 const char *first
, *last
;
3075 gboolean has_comma
= FALSE
;
3077 if (!sip
|| !uri
) return;
3079 p_buddy
= purple_find_buddy(sip
->account
, uri
);
3081 if (!p_buddy
) return;
3083 display_name
= g_strdup(purple_buddy_get_alias(p_buddy
));
3084 email
= purple_blist_node_get_string(&p_buddy
->node
, EMAIL_PROP
);
3086 if (!display_name
&& !email
) return;
3088 /* if no display name, make "first last anything_else" out of email */
3089 if (email
&& !display_name
) {
3090 display_name
= g_strndup(email
, strstr(email
, "@") - email
);
3091 display_name
= purple_strreplace((tmp
= display_name
), ".", " ");
3096 has_comma
= (strstr(display_name
, ",") != NULL
);
3097 display_name
= purple_strreplace((tmp
= display_name
), ", ", " ");
3099 display_name
= purple_strreplace((tmp
= display_name
), ",", " ");
3103 parts
= g_strsplit(display_name
, " ", 0);
3105 if (!parts
[0] || !parts
[1]) {
3106 g_free(display_name
);
3120 *first_name
= g_strstrip(g_strdup(first
));
3124 *last_name
= g_strstrip(g_strdup(last
));
3127 g_free(display_name
);
3132 * Update user information
3134 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3135 * @param property_name
3136 * @param property_value may be modified to strip white space
3139 sipe_update_user_info(struct sipe_account_data
*sip
,
3141 const char *property_name
,
3142 char *property_value
)
3144 GSList
*buddies
, *entry
;
3146 if (!property_name
|| strlen(property_name
) == 0) return;
3149 property_value
= g_strstrip(property_value
);
3151 entry
= buddies
= purple_find_buddies(sip
->account
, uri
); /* all buddies in different groups */
3153 const char *prop_str
;
3154 const char *server_alias
;
3155 PurpleBuddy
*p_buddy
= entry
->data
;
3157 /* for Display Name */
3158 if (sipe_strequal(property_name
, ALIAS_PROP
)) {
3159 if (property_value
&& sipe_is_bad_alias(uri
, purple_buddy_get_alias(p_buddy
))) {
3160 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, property_value
);
3161 purple_blist_alias_buddy(p_buddy
, property_value
);
3164 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3165 if (!is_empty(property_value
) &&
3166 (!sipe_strequal(property_value
, server_alias
) || is_empty(server_alias
)) )
3168 purple_blist_server_alias_buddy(p_buddy
, property_value
);
3171 /* for other properties */
3173 if (!is_empty(property_value
)) {
3174 prop_str
= purple_blist_node_get_string(&p_buddy
->node
, property_name
);
3175 if (!prop_str
|| g_ascii_strcasecmp(prop_str
, property_value
)) {
3176 purple_blist_node_set_string(&p_buddy
->node
, property_name
, property_value
);
3181 entry
= entry
->next
;
3183 g_slist_free(buddies
);
3188 * Suitable for both 2005 and 2007 systems.
3190 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3192 * @param phone may be modified to strip white space
3193 * @param phone_display_string may be modified to strip white space
3196 sipe_update_user_phone(struct sipe_account_data
*sip
,
3198 const gchar
*phone_type
,
3200 gchar
*phone_display_string
)
3202 const char *phone_node
= PHONE_PROP
; /* work phone by default */
3203 const char *phone_display_node
= PHONE_DISPLAY_PROP
; /* work phone by default */
3205 if(!phone
|| strlen(phone
) == 0) return;
3207 if ((sipe_strequal(phone_type
, "mobile") || sipe_strequal(phone_type
, "cell"))) {
3208 phone_node
= PHONE_MOBILE_PROP
;
3209 phone_display_node
= PHONE_MOBILE_DISPLAY_PROP
;
3210 } else if (sipe_strequal(phone_type
, "home")) {
3211 phone_node
= PHONE_HOME_PROP
;
3212 phone_display_node
= PHONE_HOME_DISPLAY_PROP
;
3213 } else if (sipe_strequal(phone_type
, "other")) {
3214 phone_node
= PHONE_OTHER_PROP
;
3215 phone_display_node
= PHONE_OTHER_DISPLAY_PROP
;
3216 } else if (sipe_strequal(phone_type
, "custom1")) {
3217 phone_node
= PHONE_CUSTOM1_PROP
;
3218 phone_display_node
= PHONE_CUSTOM1_DISPLAY_PROP
;
3221 sipe_update_user_info(sip
, uri
, phone_node
, phone
);
3222 if (phone_display_string
) {
3223 sipe_update_user_info(sip
, uri
, phone_display_node
, phone_display_string
);
3228 sipe_update_calendar(struct sipe_account_data
*sip
)
3230 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
3232 purple_debug_info("sipe", "sipe_update_calendar: started.\n");
3234 if (sipe_strequal(calendar
, "EXCH")) {
3235 sipe_ews_update_calendar(sip
);
3238 /* schedule repeat */
3239 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_INTERVAL
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3241 purple_debug_info("sipe", "sipe_update_calendar: finished.\n");
3245 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
3246 * by using standard Purple's means of signals and saved statuses.
3248 * Thus all UI elements get updated: Status Button with Note, docklet.
3249 * This is ablolutely important as both our status and note can come
3250 * inbound (roaming) or be updated programmatically (e.g. based on our
3254 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
3255 const char *status_id
,
3256 const char *message
,
3257 time_t do_not_publish
[])
3259 PurpleStatus
*status
= purple_account_get_active_status(account
);
3260 gboolean changed
= TRUE
;
3262 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
3263 sipe_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
3268 if (purple_savedstatus_is_idleaway()) {
3273 PurpleSavedStatus
*saved_status
;
3274 const PurpleStatusType
*acct_status_type
=
3275 purple_status_type_find_with_id(account
->status_types
, status_id
);
3276 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
3277 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
3279 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
3281 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
3284 /* If this type+message is unique then create a new transient saved status
3285 * Ref: gtkstatusbox.c
3287 if (!saved_status
) {
3289 GList
*active_accts
= purple_accounts_get_all_active();
3291 saved_status
= purple_savedstatus_new(NULL
, primitive
);
3292 purple_savedstatus_set_message(saved_status
, message
);
3294 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
3295 purple_savedstatus_set_substatus(saved_status
,
3296 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
3298 g_list_free(active_accts
);
3301 do_not_publish
[activity
] = time(NULL
);
3302 purple_debug_info("sipe", "sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]\n",
3303 status_id
, (int)do_not_publish
[activity
]);
3305 /* Set the status for each account */
3306 purple_savedstatus_activate(saved_status
);
3310 struct hash_table_delete_payload
{
3311 GHashTable
*hash_table
;
3316 sipe_remove_category_container_publications_cb(const char *name
,
3317 struct sipe_publication
*publication
,
3318 struct hash_table_delete_payload
*payload
)
3320 if (publication
->container
== payload
->container
) {
3321 g_hash_table_remove(payload
->hash_table
, name
);
3325 sipe_remove_category_container_publications(GHashTable
*our_publications
,
3326 const char *category
,
3329 struct hash_table_delete_payload payload
;
3330 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
3332 if (!payload
.hash_table
) return;
3334 payload
.container
= container
;
3335 g_hash_table_foreach(payload
.hash_table
, (GHFunc
)sipe_remove_category_container_publications_cb
, &payload
);
3339 send_publish_category_initial(struct sipe_account_data
*sip
);
3342 * When we receive some self (BE) NOTIFY with a new subscriber
3343 * we sends a setSubscribers request to him [SIP-PRES] 4.8
3346 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3353 char *display_name
= NULL
;
3355 GSList
*category_names
= NULL
;
3356 int aggreg_avail
= 0;
3357 static sipe_activity aggreg_activity
= SIPE_ACTIVITY_UNSET
;
3358 gboolean do_update_status
= FALSE
;
3359 gboolean has_note_cleaned
= FALSE
;
3361 purple_debug_info("sipe", "sipe_process_roaming_self\n");
3363 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3366 contact
= get_contact(sip
);
3367 to
= sip_uri_self(sip
);
3371 /* set list of categories participating in this XML */
3372 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3373 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3374 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
3376 purple_debug_info("sipe", "sipe_process_roaming_self: category_names length=%d\n",
3377 category_names
? (int) g_slist_length(category_names
) : -1);
3378 /* drop category information */
3379 if (category_names
) {
3380 GSList
*entry
= category_names
;
3382 GHashTable
*cat_publications
;
3383 const gchar
*category
= entry
->data
;
3384 entry
= entry
->next
;
3385 purple_debug_info("sipe", "sipe_process_roaming_self: dropping category: %s\n", category
);
3386 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
3387 if (cat_publications
) {
3388 g_hash_table_remove(sip
->our_publications
, category
);
3389 purple_debug_info("sipe", " sipe_process_roaming_self: dropped category: %s\n", category
);
3393 g_slist_free(category_names
);
3394 /* filling our categories reflected in roaming data */
3395 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3397 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3398 guint container
= xmlnode_get_int_attrib(node
, "container", -1);
3399 guint instance
= xmlnode_get_int_attrib(node
, "instance", -1);
3400 guint version
= xmlnode_get_int_attrib(node
, "version", 0);
3401 time_t publish_time
= (tmp
= xmlnode_get_attrib(node
, "publishTime")) ?
3402 sipe_utils_str_to_time(tmp
) : 0;
3404 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
3406 /* Ex. clear note: <category name="note"/> */
3407 if (container
== (guint
)-1) {
3410 do_update_status
= TRUE
;
3414 /* Ex. clear note: <category name="note" container="200"/> */
3415 if (instance
== (guint
)-1) {
3416 if (container
== 200) {
3419 do_update_status
= TRUE
;
3421 purple_debug_info("sipe", "sipe_process_roaming_self: removing publications for: %s/%u\n", name
, container
);
3422 sipe_remove_category_container_publications(
3423 sip
->our_publications
, name
, container
);
3427 /* key is <category><instance><container> */
3428 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
3429 purple_debug_info("sipe", "sipe_process_roaming_self: key=%s version=%d\n", key
, version
);
3431 /* capture all userState publication for later clean up if required */
3432 if (sipe_strequal(name
, "state") && (container
== 2 || container
== 3)) {
3433 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3435 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "userState")) {
3436 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3437 publication
->category
= g_strdup(name
);
3438 publication
->instance
= instance
;
3439 publication
->container
= container
;
3440 publication
->version
= version
;
3442 if (!sip
->user_state_publications
) {
3443 sip
->user_state_publications
= g_hash_table_new_full(
3444 g_str_hash
, g_str_equal
,
3445 g_free
, (GDestroyNotify
)free_publication
);
3447 g_hash_table_insert(sip
->user_state_publications
, g_strdup(key
), publication
);
3448 purple_debug_info("sipe", "sipe_process_roaming_self: added to user_state_publications key=%s version=%d\n",
3453 if (sipe_is_our_publication(sip
, key
)) {
3454 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3456 publication
->category
= g_strdup(name
);
3457 publication
->instance
= instance
;
3458 publication
->container
= container
;
3459 publication
->version
= version
;
3461 /* filling publication->availability */
3462 if (sipe_strequal(name
, "state")) {
3463 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3464 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3467 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3469 publication
->availability
= atoi(avail_str
);
3473 /* for calendarState */
3474 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "calendarState")) {
3475 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3476 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
3478 event
->start_time
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "startTime"));
3480 if (sipe_strequal(xmlnode_get_attrib(xn_activity
, "token"),
3481 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
))
3483 event
->is_meeting
= TRUE
;
3486 event
->subject
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingSubject"));
3487 event
->location
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingLocation"));
3489 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
3490 purple_debug_info("sipe", "sipe_process_roaming_self: hash=%s\n",
3491 publication
->cal_event_hash
);
3492 sipe_cal_event_free(event
);
3495 /* filling publication->note */
3496 if (sipe_strequal(name
, "note")) {
3497 xmlnode
*xn_body
= xmlnode_get_descendant(node
, "note", "body", NULL
);
3499 if (!has_note_cleaned
) {
3500 has_note_cleaned
= TRUE
;
3504 sip
->note_since
= publish_time
;
3506 do_update_status
= TRUE
;
3509 g_free(publication
->note
);
3510 publication
->note
= NULL
;
3514 publication
->note
= g_markup_escape_text((tmp
= xmlnode_get_data(xn_body
)), -1);
3516 if (publish_time
>= sip
->note_since
) {
3518 sip
->note
= g_strdup(publication
->note
);
3519 sip
->note_since
= publish_time
;
3520 sip
->is_oof_note
= sipe_strequal(xmlnode_get_attrib(xn_body
, "type"), "OOF");
3522 do_update_status
= TRUE
;
3527 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
3528 if (sipe_strequal(name
, "calendarData") && (publication
->container
== 300)) {
3529 xmlnode
*xn_free_busy
= xmlnode_get_descendant(node
, "calendarData", "freeBusy", NULL
);
3530 xmlnode
*xn_working_hours
= xmlnode_get_descendant(node
, "calendarData", "WorkingHours", NULL
);
3532 publication
->fb_start_str
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
3533 publication
->free_busy_base64
= xmlnode_get_data(xn_free_busy
);
3535 if (xn_working_hours
) {
3536 publication
->working_hours_xml_str
= xmlnode_to_str(xn_working_hours
, NULL
);
3540 if (!cat_publications
) {
3541 cat_publications
= g_hash_table_new_full(
3542 g_str_hash
, g_str_equal
,
3543 g_free
, (GDestroyNotify
)free_publication
);
3544 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
3545 purple_debug_info("sipe", "sipe_process_roaming_self: added GHashTable cat=%s\n", name
);
3547 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
3548 purple_debug_info("sipe", "sipe_process_roaming_self: added key=%s version=%d\n", key
, version
);
3552 /* aggregateState (not an our publication) from 2-nd container */
3553 if (sipe_strequal(name
, "state") && container
== 2) {
3554 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3556 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "aggregateState")) {
3557 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3558 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3561 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3563 aggreg_avail
= atoi(avail_str
);
3569 const char *activity_token
= xmlnode_get_attrib(xn_activity
, "token");
3571 aggreg_activity
= sipe_get_activity_by_token(activity_token
);
3574 do_update_status
= TRUE
;
3578 /* userProperties published by server from AD */
3579 if (!sip
->csta
&& sipe_strequal(name
, "userProperties")) {
3581 /* line, for Remote Call Control (RCC) */
3582 for (line
= xmlnode_get_descendant(node
, "userProperties", "lines", "line", NULL
); line
; line
= xmlnode_get_next_twin(line
)) {
3583 const gchar
*line_server
= xmlnode_get_attrib(line
, "lineServer");
3584 const gchar
*line_type
= xmlnode_get_attrib(line
, "lineType");
3587 if (!line_server
|| !(sipe_strequal(line_type
, "Rcc") || sipe_strequal(line_type
, "Dual"))) continue;
3589 line_uri
= xmlnode_get_data(line
);
3591 purple_debug_info("sipe", "sipe_process_roaming_self: line_uri=%s server=%s\n", line_uri
, line_server
);
3592 sip_csta_open(sip
, line_uri
, line_server
);
3600 purple_debug_info("sipe", "sipe_process_roaming_self: sip->our_publications size=%d\n",
3601 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
3604 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3605 guint id
= xmlnode_get_int_attrib(node
, "id", 0);
3606 struct sipe_container
*container
= sipe_find_container(sip
, id
);
3609 sip
->containers
= g_slist_remove(sip
->containers
, container
);
3610 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
3611 free_container(container
);
3613 container
= g_new0(struct sipe_container
, 1);
3615 container
->version
= xmlnode_get_int_attrib(node
, "version", 0);
3616 sip
->containers
= g_slist_append(sip
->containers
, container
);
3617 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
3619 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
3620 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
3621 member
->type
= xmlnode_get_attrib(node2
, "type");
3622 member
->value
= xmlnode_get_attrib(node2
, "value");
3623 container
->members
= g_slist_append(container
->members
, member
);
3624 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
3625 member
->type
, member
->value
? member
->value
: "");
3629 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
3630 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
3631 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
3632 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
3633 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
3634 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
3635 /* initial set-up to let counterparties see your status */
3636 if (sameEnterpriseAL
< 0) {
3637 struct sipe_container
*container
= sipe_find_container(sip
, 200);
3638 guint version
= container
? container
->version
: 0;
3639 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
3641 if (federatedAL
< 0) {
3642 struct sipe_container
*container
= sipe_find_container(sip
, 100);
3643 guint version
= container
? container
->version
: 0;
3644 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
3646 sip
->access_level_set
= TRUE
;
3650 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3652 const char *acknowledged
;
3656 user
= xmlnode_get_attrib(node
, "user"); /* without 'sip:' prefix */
3657 if (!user
) continue;
3658 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
3659 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
3660 uri
= sip_uri_from_name(user
);
3662 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
3664 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
3665 if(!g_ascii_strcasecmp(acknowledged
,"false")){
3666 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
3667 if (!purple_find_buddy(sip
->account
, uri
)) {
3668 purple_account_request_add(sip
->account
, uri
, _("you"), display_name
, NULL
);
3671 hdr
= g_strdup_printf(
3673 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
3675 body
= g_strdup_printf(
3676 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
3677 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
3678 "</setSubscribers>", user
);
3680 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
3684 g_free(display_name
);
3691 /* Publish initial state if not yet.
3692 * Assuming this happens on initial responce to subscription to roaming-self
3693 * so we've already updated our roaming data in full.
3696 if (!sip
->initial_state_published
) {
3697 send_publish_category_initial(sip
);
3698 sip
->initial_state_published
= TRUE
;
3700 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3701 do_update_status
= FALSE
;
3702 } else if (aggreg_avail
) {
3704 g_free(sip
->status
);
3705 if (aggreg_avail
&& aggreg_avail
< 18000) { /* not offline */
3706 sip
->status
= g_strdup(sipe_get_status_by_availability(aggreg_avail
, NULL
));
3708 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
3712 if (do_update_status
) {
3713 purple_debug_info("sipe", "sipe_process_roaming_self: switch to '%s' for the account\n", sip
->status
);
3714 sipe_set_purple_account_status_and_note(sip
->account
, sip
->status
, sip
->note
, sip
->do_not_publish
);
3720 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
)
3722 gchar
*to
= sip_uri_self(sip
);
3723 gchar
*tmp
= get_contact(sip
);
3724 gchar
*hdr
= g_strdup_printf(
3725 "Event: vnd-microsoft-roaming-ACL\r\n"
3726 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
3727 "Supported: com.microsoft.autoextend\r\n"
3728 "Supported: ms-benotify\r\n"
3729 "Proxy-Require: ms-benotify\r\n"
3730 "Supported: ms-piggyback-first-notify\r\n"
3731 "Contact: %s\r\n", tmp
);
3734 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
3740 * To request for presence information about the user, access level settings that have already been configured by the user
3741 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
3742 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
3745 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
)
3747 gchar
*to
= sip_uri_self(sip
);
3748 gchar
*tmp
= get_contact(sip
);
3749 gchar
*hdr
= g_strdup_printf(
3750 "Event: vnd-microsoft-roaming-self\r\n"
3751 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
3752 "Supported: ms-benotify\r\n"
3753 "Proxy-Require: ms-benotify\r\n"
3754 "Supported: ms-piggyback-first-notify\r\n"
3756 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
3758 gchar
*body
=g_strdup(
3759 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
3760 "<roaming type=\"categories\"/>"
3761 "<roaming type=\"containers\"/>"
3762 "<roaming type=\"subscribers\"/></roamingList>");
3765 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3774 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
)
3776 gchar
*to
= sip_uri_self(sip
);
3777 gchar
*tmp
= get_contact(sip
);
3778 gchar
*hdr
= g_strdup_printf(
3779 "Event: vnd-microsoft-provisioning\r\n"
3780 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
3781 "Supported: com.microsoft.autoextend\r\n"
3782 "Supported: ms-benotify\r\n"
3783 "Proxy-Require: ms-benotify\r\n"
3784 "Supported: ms-piggyback-first-notify\r\n"
3786 "Contact: %s\r\n", tmp
);
3789 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
3794 /** Subscription for provisioning information to help with initial
3795 * configuration. This subscription is a one-time query (denoted by the Expires header,
3796 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
3797 * configuration, meeting policies, and policy settings that Communicator must enforce.
3798 * TODO: for what we need this information.
3801 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
)
3803 gchar
*to
= sip_uri_self(sip
);
3804 gchar
*tmp
= get_contact(sip
);
3805 gchar
*hdr
= g_strdup_printf(
3806 "Event: vnd-microsoft-provisioning-v2\r\n"
3807 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
3808 "Supported: com.microsoft.autoextend\r\n"
3809 "Supported: ms-benotify\r\n"
3810 "Proxy-Require: ms-benotify\r\n"
3811 "Supported: ms-piggyback-first-notify\r\n"
3814 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
3815 gchar
*body
= g_strdup(
3816 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
3817 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
3818 "<provisioningGroup name=\"ucPolicy\"/>"
3819 "</provisioningGroupList>");
3822 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3829 sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key
,
3830 gpointer value
, gpointer user_data
)
3832 struct sip_subscription
*subscription
= value
;
3833 struct sip_dialog
*dialog
= &subscription
->dialog
;
3834 struct sipe_account_data
*sip
= user_data
;
3835 gchar
*tmp
= get_contact(sip
);
3836 gchar
*hdr
= g_strdup_printf(
3839 "Contact: %s\r\n", subscription
->event
, tmp
);
3842 /* Rate limit to max. 25 requests per seconds */
3843 g_usleep(1000000 / 25);
3845 send_sip_request(sip
->gc
, "SUBSCRIBE", dialog
->with
, dialog
->with
, hdr
, NULL
, dialog
, NULL
);
3849 /* IM Session (INVITE and MESSAGE methods) */
3851 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
3853 get_end_points (struct sipe_account_data
*sip
,
3854 struct sip_session
*session
)
3858 if (session
== NULL
) {
3862 res
= g_strdup_printf("<sip:%s>", sip
->username
);
3864 SIPE_DIALOG_FOREACH
{
3866 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
3869 if (dialog
->theirepid
) {
3871 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
3874 } SIPE_DIALOG_FOREACH_END
;
3880 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_account_data
*sip
,
3882 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3884 gboolean ret
= TRUE
;
3886 if (msg
->response
!= 200) {
3887 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
3891 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
3897 * Asks UA/proxy about its capabilities.
3899 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
3901 gchar
*to
= sip_uri(who
);
3902 gchar
*contact
= get_contact(sip
);
3903 gchar
*request
= g_strdup_printf(
3904 "Accept: application/sdp\r\n"
3905 "Contact: %s\r\n", contact
);
3908 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
3915 sipe_notify_user(struct sipe_account_data
*sip
,
3916 struct sip_session
*session
,
3917 PurpleMessageFlags flags
,
3918 const gchar
*message
)
3920 PurpleConversation
*conv
;
3922 if (!session
->conv
) {
3923 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
3925 conv
= session
->conv
;
3927 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
3931 sipe_present_info(struct sipe_account_data
*sip
,
3932 struct sip_session
*session
,
3933 const gchar
*message
)
3935 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
3939 sipe_present_err(struct sipe_account_data
*sip
,
3940 struct sip_session
*session
,
3941 const gchar
*message
)
3943 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_ERROR
, message
);
3947 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
3948 struct sip_session
*session
,
3951 const gchar
*message
)
3953 char *msg
, *msg_tmp
, *msg_tmp2
;
3956 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
3957 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
3959 /* Service unavailable; Server Internal Error; Server Time-out */
3960 if (sip_error
== 503 || sip_error
== 500 || sip_error
== 504) {
3961 label
= _("This message was not delivered to %s because the service is not available");
3962 } else if (sip_error
== 486) { /* Busy Here */
3963 label
= _("This message was not delivered to %s because one or more recipients do not want to be disturbed");
3965 label
= _("This message was not delivered to %s because one or more recipients are offline");
3968 msg_tmp
= g_strdup_printf( "%s:\n%s" ,
3969 msg_tmp2
= g_strdup_printf(label
, who
? who
: ""), msg
? msg
: "");
3970 sipe_present_err(sip
, session
, msg_tmp
);
3978 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
3979 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3981 gboolean ret
= TRUE
;
3982 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3983 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
3984 struct sip_dialog
*dialog
;
3990 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
3995 dialog
= sipe_dialog_find(session
, with
);
3997 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
4002 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4003 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
4005 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4007 if (msg
->response
>= 400) {
4008 PurpleBuddy
*pbuddy
;
4009 const char *alias
= with
;
4011 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
4013 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4014 alias
= purple_buddy_get_alias(pbuddy
);
4017 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, alias
, message
);
4020 gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
4022 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
));
4023 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
4024 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
4027 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4028 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
4029 key
, g_hash_table_size(session
->unconfirmed_messages
));
4035 if (ret
) sipe_im_process_queue(sip
, session
);
4040 sipe_is_election_finished(struct sip_session
*session
);
4043 sipe_election_result(struct sipe_account_data
*sip
,
4047 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4048 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
4050 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4051 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4052 struct sip_dialog
*dialog
;
4053 struct sip_session
*session
;
4055 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4057 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
4061 if (msg
->response
== 200 && g_str_has_prefix(contenttype
, "application/x-ms-mim")) {
4062 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4063 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
4064 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
4066 if (xn_request_rm_response
) {
4067 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
4068 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
4070 dialog
= sipe_dialog_find(session
, with
);
4072 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
4073 xmlnode_free(xn_action
);
4077 if (allow
&& !g_strcasecmp(allow
, "true")) {
4078 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
4079 dialog
->election_vote
= 1;
4080 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
4081 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
4082 dialog
->election_vote
= -1;
4085 if (sipe_is_election_finished(session
)) {
4086 sipe_election_result(sip
, session
);
4089 } else if (xn_set_rm_response
) {
4092 xmlnode_free(xn_action
);
4099 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
, const char *content_type
)
4108 sipe_parse_html(msg
, &msgformat
, &msgtext
);
4109 purple_debug_info("sipe", "sipe_send_message: msgformat=%s\n", msgformat
);
4111 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4114 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
4117 msgr
= g_strdup("");
4120 tmp
= get_contact(sip
);
4121 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
4122 //hdr = g_strdup("Content-Type: text/rtf\r\n");
4123 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
4124 if (content_type
== NULL
)
4125 content_type
= "text/plain";
4127 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp
, content_type
, msgr
);
4131 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
4138 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
4140 GSList
*entry2
= session
->outgoing_message_queue
;
4142 struct queued_message
*msg
= entry2
->data
;
4144 /* for multiparty chat or conference */
4145 if (session
->is_multiparty
|| session
->focus_uri
) {
4146 gchar
*who
= sip_uri_self(sip
);
4147 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
4148 PURPLE_MESSAGE_SEND
, msg
->body
, time(NULL
));
4152 SIPE_DIALOG_FOREACH
{
4155 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
4157 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
4158 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg
->body
));
4159 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
4160 key
, g_hash_table_size(session
->unconfirmed_messages
));
4163 sipe_send_message(sip
, dialog
, msg
->body
, msg
->content_type
);
4164 } SIPE_DIALOG_FOREACH_END
;
4166 entry2
= sipe_session_dequeue_message(session
);
4171 sipe_refer_notify(struct sipe_account_data
*sip
,
4172 struct sip_session
*session
,
4179 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4181 hdr
= g_strdup_printf(
4183 "Subscription-State: %s\r\n"
4184 "Content-Type: message/sipfrag\r\n",
4185 status
>= 200 ? "terminated" : "active");
4187 body
= g_strdup_printf(
4188 "SIP/2.0 %d %s\r\n",
4191 send_sip_request(sip
->gc
, "NOTIFY", who
, who
, hdr
, body
, dialog
, NULL
);
4198 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
4200 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
4201 struct sip_session
*session
;
4202 struct sip_dialog
*dialog
;
4206 struct sipmsg
*request_msg
= trans
->msg
;
4208 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4211 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4213 session
= sipe_session_find_im(sip
, with
);
4216 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
4221 dialog
= sipe_dialog_find(session
, with
);
4223 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
4228 sipe_dialog_parse(dialog
, msg
, TRUE
);
4230 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4231 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
4233 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4235 if (msg
->response
!= 200) {
4236 PurpleBuddy
*pbuddy
;
4237 const char *alias
= with
;
4239 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
4241 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4242 alias
= purple_buddy_get_alias(pbuddy
);
4246 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, alias
, message
);
4248 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
4249 sipe_present_err(sip
, session
, tmp_msg
);
4253 sipe_dialog_remove(session
, with
);
4261 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4262 dialog
->outgoing_invite
= NULL
;
4263 dialog
->is_established
= TRUE
;
4265 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
4267 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
4268 g_free(referred_by
);
4271 /* add user to chat if it is a multiparty session */
4272 if (session
->is_multiparty
) {
4273 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4275 PURPLE_CBFLAGS_NONE
, TRUE
);
4278 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
4279 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
4280 sipe_session_dequeue_message(session
);
4283 sipe_im_process_queue(sip
, session
);
4285 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4286 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
4287 key
, g_hash_table_size(session
->unconfirmed_messages
));
4296 sipe_invite(struct sipe_account_data
*sip
,
4297 struct sip_session
*session
,
4299 const gchar
*msg_body
,
4300 const gchar
*msg_content_type
,
4301 const gchar
*referred_by
,
4302 const gboolean is_triggered
)
4309 char *ms_text_format
= NULL
;
4310 gchar
*roster_manager
;
4312 gchar
*referred_by_str
;
4313 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4315 if (dialog
&& dialog
->is_established
) {
4316 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
4321 dialog
= sipe_dialog_add(session
);
4322 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
4323 dialog
->with
= g_strdup(who
);
4326 if (!(dialog
->ourtag
)) {
4327 dialog
->ourtag
= gentag();
4340 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
4341 purple_debug_info("sipe", "sipe_invite: msgformat=%s\n", msgformat
);
4343 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4346 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
4349 msgr
= g_strdup("");
4352 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
4353 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
,
4354 msg_content_type
? msg_content_type
: "text/plain",
4360 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
4361 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
4362 purple_debug_info("sipe", "sipe_invite: added message %s to unconfirmed_messages(count=%d)\n",
4363 key
, g_hash_table_size(session
->unconfirmed_messages
));
4367 contact
= get_contact(sip
);
4368 end_points
= get_end_points(sip
, session
);
4369 self
= sip_uri_self(sip
);
4370 roster_manager
= g_strdup_printf(
4371 "Roster-Manager: %s\r\n"
4372 "EndPoints: %s\r\n",
4375 referred_by_str
= referred_by
?
4377 "Referred-By: %s\r\n",
4380 hdr
= g_strdup_printf(
4381 "Supported: ms-sender\r\n"
4387 "Content-Type: application/sdp\r\n",
4388 sipe_strequal(session
->roster_manager
, self
) ? roster_manager
: "",
4390 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
4391 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
4393 ms_text_format
? ms_text_format
: "");
4394 g_free(ms_text_format
);
4397 body
= g_strdup_printf(
4399 "o=- 0 0 IN IP4 %s\r\n"
4403 "m=%s %d sip null\r\n"
4404 "a=accept-types:text/plain text/html image/gif "
4405 "multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
4406 purple_network_get_my_ip(-1),
4407 purple_network_get_my_ip(-1),
4408 sip
->ocs2007
? "message" : "x-ms-message",
4411 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
4412 to
, to
, hdr
, body
, dialog
, process_invite_response
);
4415 g_free(roster_manager
);
4417 g_free(referred_by_str
);
4424 sipe_refer(struct sipe_account_data
*sip
,
4425 struct sip_session
*session
,
4430 gchar
*epid
= get_epid(sip
);
4431 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
4432 session
->roster_manager
);
4433 const char *ourtag
= dialog
&& dialog
->ourtag
? dialog
->ourtag
: NULL
;
4435 contact
= get_contact(sip
);
4436 hdr
= g_strdup_printf(
4438 "Refer-to: <%s>\r\n"
4439 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
4440 "Require: com.microsoft.rtc-multiparty\r\n",
4444 ourtag
? ";tag=" : "",
4445 ourtag
? ourtag
: "",
4449 send_sip_request(sip
->gc
, "REFER",
4450 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
4457 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
4458 struct sip_dialog
*dialog
,
4461 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4463 gchar
*body
= g_strdup_printf(
4464 "<?xml version=\"1.0\"?>\r\n"
4465 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4466 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
4467 sip
->username
, bid
);
4469 send_sip_request(sip
->gc
, "INFO",
4470 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4476 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
4477 struct sip_dialog
*dialog
)
4479 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4481 gchar
*body
= g_strdup_printf(
4482 "<?xml version=\"1.0\"?>\r\n"
4483 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4484 "<SetRM uri=\"sip:%s\"/></action>\r\n",
4487 send_sip_request(sip
->gc
, "INFO",
4488 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4494 sipe_session_close(struct sipe_account_data
*sip
,
4495 struct sip_session
* session
)
4497 if (session
&& session
->focus_uri
) {
4498 sipe_conf_immcu_closed(sip
, session
);
4499 conf_session_close(sip
, session
);
4503 SIPE_DIALOG_FOREACH
{
4504 /* @TODO slow down BYE message sending rate */
4505 /* @see single subscription code */
4506 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4507 } SIPE_DIALOG_FOREACH_END
;
4509 sipe_session_remove(sip
, session
);
4514 sipe_session_close_all(struct sipe_account_data
*sip
)
4517 while ((entry
= sip
->sessions
) != NULL
) {
4518 sipe_session_close(sip
, entry
->data
);
4523 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
4525 struct sipe_account_data
*sip
= gc
->proto_data
;
4527 purple_debug_info("sipe", "conversation with %s closed\n", who
);
4528 sipe_session_close(sip
, sipe_session_find_im(sip
, who
));
4532 sipe_chat_leave (PurpleConnection
*gc
, int id
)
4534 struct sipe_account_data
*sip
= gc
->proto_data
;
4535 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
4537 sipe_session_close(sip
, session
);
4540 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
4541 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4543 struct sipe_account_data
*sip
= gc
->proto_data
;
4544 struct sip_session
*session
;
4545 struct sip_dialog
*dialog
;
4546 gchar
*uri
= sip_uri(who
);
4548 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
4550 session
= sipe_session_find_or_add_im(sip
, uri
);
4551 dialog
= sipe_dialog_find(session
, uri
);
4553 // Queue the message
4554 sipe_session_enqueue_message(session
, what
, NULL
);
4556 if (dialog
&& !dialog
->outgoing_invite
) {
4557 sipe_im_process_queue(sip
, session
);
4558 } else if (!dialog
|| !dialog
->outgoing_invite
) {
4559 // Need to send the INVITE to get the outgoing dialog setup
4560 sipe_invite(sip
, session
, uri
, what
, NULL
, NULL
, FALSE
);
4567 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
4568 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4570 struct sipe_account_data
*sip
= gc
->proto_data
;
4571 struct sip_session
*session
;
4573 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
4575 session
= sipe_session_find_chat_by_id(sip
, id
);
4577 // Queue the message
4578 if (session
&& session
->dialogs
) {
4579 sipe_session_enqueue_message(session
,what
,NULL
);
4580 sipe_im_process_queue(sip
, session
);
4582 gchar
*chat_name
= purple_find_chat(sip
->gc
, id
)->name
;
4583 const gchar
*proto_chat_id
= sipe_chat_find_name(chat_name
);
4585 purple_debug_info("sipe", "sipe_chat_send: chat_name='%s'\n", chat_name
? chat_name
: "NULL");
4586 purple_debug_info("sipe", "sipe_chat_send: proto_chat_id='%s'\n", proto_chat_id
? proto_chat_id
: "NULL");
4589 struct sip_session
*session
= sipe_session_add_chat(sip
);
4591 session
->is_multiparty
= FALSE
;
4592 session
->focus_uri
= g_strdup(proto_chat_id
);
4593 sipe_session_enqueue_message(session
, what
, NULL
);
4594 sipe_invite_conf_focus(sip
, session
);
4601 /* End IM Session (INVITE and MESSAGE methods) */
4603 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4605 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4606 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4608 struct sip_session
*session
;
4610 purple_debug_info("sipe", "process_incoming_info: \n%s\n", msg
->body
? msg
->body
: "");
4612 /* Call Control protocol */
4613 if (g_str_has_prefix(contenttype
, "application/csta+xml"))
4615 process_incoming_info_csta(sip
, msg
);
4619 from
= parse_from(sipmsg_find_header(msg
, "From"));
4620 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4622 session
= sipe_session_find_im(sip
, from
);
4629 if (g_str_has_prefix(contenttype
, "application/x-ms-mim"))
4631 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4632 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
4633 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
4635 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
4637 if (xn_request_rm
) {
4638 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
4639 int bid
= xmlnode_get_int_attrib(xn_request_rm
, "bid", 0);
4640 gchar
*body
= g_strdup_printf(
4641 "<?xml version=\"1.0\"?>\r\n"
4642 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4643 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
4645 session
->bid
< bid
? "true" : "false");
4646 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4648 } else if (xn_set_rm
) {
4650 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
4651 g_free(session
->roster_manager
);
4652 session
->roster_manager
= g_strdup(rm
);
4654 body
= g_strdup_printf(
4655 "<?xml version=\"1.0\"?>\r\n"
4656 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4657 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
4659 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4662 xmlnode_free(xn_action
);
4667 /* looks like purple lacks typing notification for chat */
4668 if (!session
->is_multiparty
&& !session
->focus_uri
) {
4669 xmlnode
*xn_keyboard_activity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4670 const char *status
= xmlnode_get_attrib(xmlnode_get_child(xn_keyboard_activity
, "status"),
4672 if (sipe_strequal(status
, "type")) {
4673 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
4674 } else if (sipe_strequal(status
, "idle")) {
4675 serv_got_typing_stopped(sip
->gc
, from
);
4677 xmlnode_free(xn_keyboard_activity
);
4680 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4685 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4687 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4688 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4689 struct sip_session
*session
;
4690 struct sip_dialog
*dialog
;
4692 /* collect dialog identification
4693 * we need callid, ourtag and theirtag to unambiguously identify dialog
4695 /* take data before 'msg' will be modified by send_sip_response */
4696 dialog
= g_new0(struct sip_dialog
, 1);
4697 dialog
->callid
= g_strdup(callid
);
4698 dialog
->cseq
= parse_cseq(sipmsg_find_header(msg
, "CSeq"));
4699 dialog
->with
= g_strdup(from
);
4700 sipe_dialog_parse(dialog
, msg
, FALSE
);
4702 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4704 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4706 session
= sipe_session_find_im(sip
, from
);
4709 sipe_dialog_free(dialog
);
4714 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
4715 g_free(session
->roster_manager
);
4716 session
->roster_manager
= NULL
;
4719 /* This what BYE is essentially for - terminating dialog */
4720 sipe_dialog_remove_3(session
, dialog
);
4721 sipe_dialog_free(dialog
);
4722 if (session
->focus_uri
&& !g_strcasecmp(from
, session
->im_mcu_uri
)) {
4723 sipe_conf_immcu_closed(sip
, session
);
4724 } else if (session
->is_multiparty
) {
4725 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
4731 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4733 gchar
*self
= sip_uri_self(sip
);
4734 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4735 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4736 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
4737 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
4738 struct sip_session
*session
;
4739 struct sip_dialog
*dialog
;
4741 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4742 dialog
= sipe_dialog_find(session
, from
);
4744 if (!session
|| !dialog
|| !session
->roster_manager
|| !sipe_strequal(session
->roster_manager
, self
)) {
4745 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
4747 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
4749 sipe_invite(sip
, session
, refer_to
, NULL
, NULL
, referred_by
, FALSE
);
4755 g_free(referred_by
);
4759 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
4761 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
4762 struct sip_session
*session
;
4763 struct sip_dialog
*dialog
;
4765 if (state
== PURPLE_NOT_TYPING
)
4768 session
= sipe_session_find_im(sip
, who
);
4769 dialog
= sipe_dialog_find(session
, who
);
4771 if (session
&& dialog
&& dialog
->is_established
) {
4772 send_sip_request(gc
, "INFO", who
, who
,
4773 "Content-Type: application/xml\r\n",
4774 SIPE_SEND_TYPING
, dialog
, NULL
);
4776 return SIPE_TYPING_SEND_TIMEOUT
;
4779 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
4781 GSList
*tmp
= sip
->transactions
;
4782 time_t currtime
= time(NULL
);
4784 struct transaction
*trans
= tmp
->data
;
4786 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
4787 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
4790 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
4792 sendout_sipmsg(sip
, trans
->msg
);
4799 static void do_reauthenticate_cb(struct sipe_account_data
*sip
,
4800 SIPE_UNUSED_PARAMETER
void *unused
)
4802 /* register again when security token expires */
4803 /* we have to start a new authentication as the security token
4804 * is almost expired by sending a not signed REGISTER message */
4805 purple_debug_info("sipe", "do a full reauthentication\n");
4806 sipe_auth_free(&sip
->registrar
);
4807 sipe_auth_free(&sip
->proxy
);
4808 sip
->registerstatus
= 0;
4810 sip
->reauthenticate_set
= FALSE
;
4814 sipe_process_incoming_x_msmsgsinvite(struct sipe_account_data
*sip
,
4818 gboolean found
= FALSE
;
4820 if (result
== TRUE
) {
4821 gchar
*invitation_command
= sipmsg_find_header(msg
, "Invitation-Command");
4823 if (sipe_strequal(invitation_command
, "INVITE")) {
4824 sipe_ft_incoming_transfer(sip
->gc
->account
,msg
);
4826 } else if (sipe_strequal(invitation_command
, "CANCEL")) {
4827 sipe_ft_incoming_cancel(sip
->gc
->account
, msg
);
4829 } else if (sipe_strequal(invitation_command
, "ACCEPT")) {
4830 sipe_ft_incoming_accept(sip
->gc
->account
, msg
);
4837 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4841 gboolean found
= FALSE
;
4843 from
= parse_from(sipmsg_find_header(msg
, "From"));
4847 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
4849 contenttype
= sipmsg_find_header(msg
, "Content-Type");
4850 if (g_str_has_prefix(contenttype
, "text/plain")
4851 || g_str_has_prefix(contenttype
, "text/html")
4852 || g_str_has_prefix(contenttype
, "multipart/related")
4853 || g_str_has_prefix(contenttype
, "multipart/alternative"))
4855 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4856 gchar
*html
= get_html_message(contenttype
, msg
->body
);
4858 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
4860 session
= sipe_session_find_im(sip
, from
);
4863 if (session
&& session
->focus_uri
) { /* a conference */
4864 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
4865 gchar
*sender
= parse_from(tmp
);
4867 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
4868 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4870 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
4871 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
4872 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4874 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
4877 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4880 } else if (g_str_has_prefix(contenttype
, "application/im-iscomposing+xml")) {
4881 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4886 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
4891 state
= xmlnode_get_child(isc
, "state");
4894 purple_debug_info("sipe", "process_incoming_message: no state found\n");
4900 statedata
= xmlnode_get_data(state
);
4902 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
4903 else serv_got_typing_stopped(sip
->gc
, from
);
4908 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4910 } else if (g_str_has_prefix(contenttype
, "text/x-msmsgsinvite")) {
4911 gchar
**lines
= g_strsplit(msg
->body
,"\r\n",0);
4912 /* pier11: "I don't like it - to add body to headers." */
4913 int result
= sipmsg_parse_and_append_header(msg
, lines
);
4916 found
= sipe_process_incoming_x_msmsgsinvite(sip
, msg
, result
);
4919 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4920 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
4922 session
= sipe_session_find_im(sip
, from
);
4925 gchar
*errmsg
= g_strdup_printf(_("Received a message with unrecognized contents from %s"),
4927 sipe_present_err(sip
, session
, errmsg
);
4931 purple_debug_info("sipe", "got unknown mime-type '%s'\n", contenttype
);
4932 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
4937 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4943 gboolean is_multiparty
= FALSE
;
4944 gboolean is_triggered
= FALSE
;
4945 gboolean was_multiparty
= TRUE
;
4946 gboolean just_joined
= FALSE
;
4948 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4949 gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
4950 gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
4951 gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
4952 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4953 GSList
*end_points
= NULL
;
4955 struct sip_session
*session
;
4956 const gchar
*ms_text_format
;
4958 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? tmp
= fix_newlines(msg
->body
) : "");
4961 /* Invitation to join conference */
4962 if (g_str_has_prefix(content_type
, "application/ms-conf-invite+xml")) {
4963 process_incoming_invite_conf(sip
, msg
);
4967 /* Only accept text invitations */
4968 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
4969 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
4973 // TODO There *must* be a better way to clean up the To header to add a tag...
4974 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
4975 oldHeader
= sipmsg_find_header(msg
, "To");
4977 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
4978 sipmsg_remove_header_now(msg
, "To");
4979 sipmsg_add_header_now(msg
, "To", newHeader
);
4982 if (end_points_hdr
) {
4983 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
4985 if (g_slist_length(end_points
) > 2) {
4986 is_multiparty
= TRUE
;
4989 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
4990 is_triggered
= TRUE
;
4991 is_multiparty
= TRUE
;
4994 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4995 /* Convert to multiparty */
4996 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
4997 g_free(session
->with
);
4998 session
->with
= NULL
;
4999 was_multiparty
= FALSE
;
5000 session
->is_multiparty
= TRUE
;
5001 session
->chat_id
= rand();
5004 if (!session
&& is_multiparty
) {
5005 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
5008 from
= parse_from(sipmsg_find_header(msg
, "From"));
5010 session
= sipe_session_find_or_add_im(sip
, from
);
5014 g_free(session
->callid
);
5015 session
->callid
= g_strdup(callid
);
5017 session
->is_multiparty
= is_multiparty
;
5018 if (roster_manager
) {
5019 session
->roster_manager
= g_strdup(roster_manager
);
5023 if (is_multiparty
&& end_points
) {
5024 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
5025 GSList
*entry
= end_points
;
5027 struct sip_dialog
*dialog
;
5028 struct sipendpoint
*end_point
= entry
->data
;
5029 entry
= entry
->next
;
5031 if (!g_strcasecmp(from
, end_point
->contact
) ||
5032 !g_strcasecmp(to
, end_point
->contact
))
5035 dialog
= sipe_dialog_find(session
, end_point
->contact
);
5037 g_free(dialog
->theirepid
);
5038 dialog
->theirepid
= end_point
->epid
;
5039 end_point
->epid
= NULL
;
5041 dialog
= sipe_dialog_add(session
);
5043 dialog
->callid
= g_strdup(session
->callid
);
5044 dialog
->with
= end_point
->contact
;
5045 end_point
->contact
= NULL
;
5046 dialog
->theirepid
= end_point
->epid
;
5047 end_point
->epid
= NULL
;
5051 /* send triggered INVITE */
5052 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, NULL
, TRUE
);
5059 GSList
*entry
= end_points
;
5061 struct sipendpoint
*end_point
= entry
->data
;
5062 entry
= entry
->next
;
5063 g_free(end_point
->contact
);
5064 g_free(end_point
->epid
);
5067 g_slist_free(end_points
);
5071 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
5073 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
5075 dialog
= sipe_dialog_add(session
);
5077 dialog
->callid
= g_strdup(session
->callid
);
5078 dialog
->with
= g_strdup(from
);
5079 sipe_dialog_parse(dialog
, msg
, FALSE
);
5081 if (!dialog
->ourtag
) {
5082 dialog
->ourtag
= newTag
;
5089 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
5093 if (is_multiparty
&& !session
->conv
) {
5094 gchar
*chat_title
= sipe_chat_get_name(callid
);
5095 gchar
*self
= sip_uri_self(sip
);
5096 /* create prpl chat */
5097 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_title
);
5098 session
->chat_title
= g_strdup(chat_title
);
5099 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
5101 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5103 PURPLE_CBFLAGS_NONE
, FALSE
);
5108 if (is_multiparty
&& !was_multiparty
) {
5109 /* add current IM counterparty to chat */
5110 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5111 sipe_dialog_first(session
)->with
, NULL
,
5112 PURPLE_CBFLAGS_NONE
, FALSE
);
5115 /* add inviting party to chat */
5116 if (just_joined
&& session
->conv
) {
5117 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5119 PURPLE_CBFLAGS_NONE
, TRUE
);
5122 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
5124 /* This used only in 2005 official client, not 2007 or Reuters.
5125 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
5126 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
5128 /* also enabled for 2005 file transfer. Didn't work otherwise. */
5129 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
5130 if (is_multiparty
||
5131 (ms_text_format
&& g_str_has_prefix(ms_text_format
, "text/x-msmsgsinvite")) )
5133 if (ms_text_format
) {
5134 if (g_str_has_prefix(ms_text_format
, "text/x-msmsgsinvite"))
5136 gchar
*tmp
= sipmsg_find_part_of_header(ms_text_format
, "ms-body=", NULL
, NULL
);
5138 gchar
*body
= purple_base64_decode(tmp
, NULL
);
5139 gchar
**lines
= g_strsplit(body
,"\r\n",0);
5143 /* pier11: "I don't like it - to add body to headers." */
5144 result
= sipmsg_parse_and_append_header(msg
, lines
);
5147 sipe_process_incoming_x_msmsgsinvite(sip
, msg
, result
);
5148 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5152 else if (g_str_has_prefix(ms_text_format
, "text/plain") || g_str_has_prefix(ms_text_format
, "text/html"))
5154 /* please do not optimize logic inside as this code may be re-enabled for other cases */
5155 gchar
*html
= get_html_message(ms_text_format
, NULL
);
5157 if (is_multiparty
) {
5158 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
5159 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5161 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
5164 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5172 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
5173 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5174 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5176 body
= g_strdup_printf(
5178 "o=- 0 0 IN IP4 %s\r\n"
5182 "m=%s %d sip sip:%s\r\n"
5183 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
5184 purple_network_get_my_ip(-1),
5185 purple_network_get_my_ip(-1),
5186 sip
->ocs2007
? "message" : "x-ms-message",
5189 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5193 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5197 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
5198 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5199 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5201 body
= g_strdup_printf(
5203 "o=- 0 0 IN IP4 0.0.0.0\r\n"
5205 "c=IN IP4 0.0.0.0\r\n"
5207 "m=%s %d sip sip:%s\r\n"
5208 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
5209 sip
->ocs2007
? "message" : "x-ms-message",
5212 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5217 sipe_get_auth_scheme_name(struct sipe_account_data
*sip
)
5219 const char *res
= "NTLM";
5221 if (purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5225 (void) sip
; /* make compiler happy */
5230 static void sipe_connection_cleanup(struct sipe_account_data
*);
5231 static void create_connection(struct sipe_account_data
*, gchar
*, int);
5233 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
5234 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5237 const gchar
*expires_header
;
5239 GSList
*hdr
= msg
->headers
;
5240 struct siphdrelement
*elem
;
5242 expires_header
= sipmsg_find_header(msg
, "Expires");
5243 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
5244 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
5246 switch (msg
->response
) {
5249 sip
->registerstatus
= 0;
5251 gchar
*contact_hdr
= NULL
;
5256 gchar
*server_hdr
= sipmsg_find_header(msg
, "Server");
5257 const char *auth_scheme
;
5259 if (!sip
->reregister_set
) {
5260 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
5261 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
5262 g_free(action_name
);
5263 sip
->reregister_set
= TRUE
;
5266 sip
->registerstatus
= 3;
5268 if (server_hdr
&& !sip
->server_version
) {
5269 sip
->server_version
= g_strdup(server_hdr
);
5274 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5275 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5278 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
5279 fill_auth(tmp
, &sip
->registrar
);
5282 if (!sip
->reauthenticate_set
) {
5283 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
5284 guint reauth_timeout
;
5285 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
5286 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
5287 reauth_timeout
= sip
->registrar
.expires
- 300;
5289 /* NTLM: we have to reauthenticate as our security token expires
5290 after eight hours (be five minutes early) */
5291 reauth_timeout
= (8 * 3600) - 300;
5293 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
5294 g_free(action_name
);
5295 sip
->reauthenticate_set
= TRUE
;
5298 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
5300 epid
= get_epid(sip
);
5301 uuid
= generateUUIDfromEPID(epid
);
5304 // There can be multiple Contact headers (one per location where the user is logged in) so
5305 // make sure to only get the one for this uuid
5306 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
5307 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
5308 if (valid_contact
) {
5309 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
5310 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
5311 g_free(valid_contact
);
5314 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
5319 g_free(sip
->contact
);
5321 sip
->contact
= g_strdup_printf("<%s>", gruu
);
5324 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
5325 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
);
5327 sip
->ocs2007
= FALSE
;
5328 sip
->batched_support
= FALSE
;
5333 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
5334 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
5335 /* We interpret this as OCS2007+ indicator */
5336 sip
->ocs2007
= TRUE
;
5337 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s (indicates OCS2007+)\n", elem
->value
);
5339 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
5340 sip
->batched_support
= TRUE
;
5341 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s\n", elem
->value
);
5344 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
5345 gchar
**caps
= g_strsplit(elem
->value
,",",0);
5348 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
5349 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
5354 hdr
= g_slist_next(hdr
);
5357 /* rejoin open chats to be able to use them by continue to send messages */
5358 purple_conversation_foreach(sipe_rejoin_chat
);
5361 if (!sip
->subscribed
) { //do it just once, not every re-register
5363 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
5364 (GCompareFunc
)g_ascii_strcasecmp
)) {
5365 sipe_subscribe_roaming_contacts(sip
);
5368 /* For 2007+ it does not make sence to subscribe to:
5369 * vnd-microsoft-roaming-ACL
5370 * vnd-microsoft-provisioning (not v2)
5372 * These are for backward compatibility.
5376 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
5377 (GCompareFunc
)g_ascii_strcasecmp
)) {
5378 sipe_subscribe_roaming_self(sip
);
5380 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
5381 (GCompareFunc
)g_ascii_strcasecmp
)) {
5382 sipe_subscribe_roaming_provisioning_v2(sip
);
5385 /* For 2005- servers */
5388 //sipe_options_request(sip, sip->sipdomain);
5390 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
5391 (GCompareFunc
)g_ascii_strcasecmp
)) {
5392 sipe_subscribe_roaming_acl(sip
);
5394 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
5395 (GCompareFunc
)g_ascii_strcasecmp
)) {
5396 sipe_subscribe_roaming_provisioning(sip
);
5398 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
5399 (GCompareFunc
)g_ascii_strcasecmp
)) {
5400 sipe_subscribe_presence_wpending(sip
, msg
);
5403 /* For 2007+ we publish our initial statuses and calendar data only after
5404 * received our existing publications in sipe_process_roaming_self()
5405 * Only in this case we know versions of current publications made
5408 /* For 2005- we publish our initial statuses only after
5409 * received our existing UserInfo data in response to
5410 * self subscription.
5411 * Only in this case we won't override existing UserInfo data
5412 * set earlier or by other client on our behalf.
5416 sip
->subscribed
= TRUE
;
5419 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
5420 "timeout=", ";", NULL
);
5421 if (timeout
!= NULL
) {
5422 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
5423 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
5424 sip
->keepalive_timeout
);
5428 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\n", sip
->cseq
);
5433 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
5435 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
5436 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
5440 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
5443 tmp
= g_strsplit(parts
[0], ":", 0);
5444 hostname
= g_strdup(tmp
[0]);
5445 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
5449 tmp
= g_strsplit(parts
[i
], "=", 0);
5451 if (g_strcasecmp("transport", tmp
[0]) == 0) {
5452 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
5453 transport
= SIPE_TRANSPORT_TCP
;
5454 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
5455 transport
= SIPE_TRANSPORT_UDP
;
5464 /* Close old connection */
5465 sipe_connection_cleanup(sip
);
5467 /* Create new connection */
5468 sip
->transport
= transport
;
5469 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
5470 hostname
, port
, TRANSPORT_DESCRIPTOR
);
5471 create_connection(sip
, hostname
, port
);
5477 if (sip
->registerstatus
!= 2) {
5478 const char *auth_scheme
;
5479 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
5480 if (sip
->registrar
.retries
> 3) {
5481 sip
->gc
->wants_to_die
= TRUE
;
5482 purple_connection_error(sip
->gc
, _("Wrong password"));
5486 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5487 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5489 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
? tmp
: "");
5491 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
5492 sip
->gc
->wants_to_die
= TRUE
;
5493 purple_connection_error(sip
->gc
, tmp2
);
5497 fill_auth(tmp
, &sip
->registrar
);
5498 sip
->registerstatus
= 2;
5499 if (sip
->account
->disconnecting
) {
5500 do_register_exp(sip
, 0);
5508 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
5509 gchar
**reason
= NULL
;
5510 if (warning
!= NULL
) {
5512 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
5514 reason
= g_strsplit(warning
, "\"", 0);
5516 warning
= g_strdup_printf(_("You have been rejected by the server: %s"),
5517 (reason
&& reason
[1]) ? reason
[1] : _("no reason given"));
5520 sip
->gc
->wants_to_die
= TRUE
;
5521 purple_connection_error(sip
->gc
, warning
);
5528 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
5529 gchar
*reason
= NULL
;
5530 if (warning
!= NULL
) {
5531 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
5533 warning
= g_strdup_printf(_("Not found: %s. Please contact your Administrator"),
5534 warning
? (reason
? reason
: _("no reason given")) :
5535 _("SIP is either not enabled for the destination URI or it does not exist"));
5538 sip
->gc
->wants_to_die
= TRUE
;
5539 purple_connection_error(sip
->gc
, warning
);
5545 case 504: /* Server time-out */
5547 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
5548 gchar
*reason
= NULL
;
5549 if (warning
!= NULL
) {
5550 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
5552 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
5555 sip
->gc
->wants_to_die
= TRUE
;
5556 purple_connection_error(sip
->gc
, warning
);
5566 * Returns 2005-style activity and Availability.
5568 * @param status Sipe statis id.
5571 sipe_get_act_avail_by_status_2005(const char *status
,
5575 int avail
= 300; /* online */
5576 int act
= 400; /* Available */
5578 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
5580 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
5582 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
5584 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
5586 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
5588 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
5589 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
5591 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
5592 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
5593 avail
= 0; /* offline */
5596 act
= 400; /* Available */
5599 if (activity
) *activity
= act
;
5600 if (availability
) *availability
= avail
;
5606 * @param activity 2005 aggregated activity. Ex.: 600
5607 * @param availablity 2005 aggregated availablity. Ex.: 300
5610 sipe_get_status_by_act_avail_2005(const int activity
,
5611 const int availablity
,
5612 char **activity_desc
)
5614 const char *status_id
= NULL
;
5615 const char *act
= NULL
;
5617 if (activity
< 150) {
5618 status_id
= SIPE_STATUS_ID_AWAY
;
5619 } else if (activity
< 200) {
5620 //status_id = SIPE_STATUS_ID_LUNCH;
5621 status_id
= SIPE_STATUS_ID_AWAY
;
5622 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
5623 } else if (activity
< 300) {
5624 //status_id = SIPE_STATUS_ID_IDLE;
5625 status_id
= SIPE_STATUS_ID_AWAY
;
5626 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5627 } else if (activity
< 400) {
5628 status_id
= SIPE_STATUS_ID_BRB
;
5629 } else if (activity
< 500) {
5630 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5631 } else if (activity
< 600) {
5632 //status_id = SIPE_STATUS_ID_ON_PHONE;
5633 status_id
= SIPE_STATUS_ID_BUSY
;
5634 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
5635 } else if (activity
< 700) {
5636 status_id
= SIPE_STATUS_ID_BUSY
;
5637 } else if (activity
< 800) {
5638 status_id
= SIPE_STATUS_ID_AWAY
;
5640 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5643 if (availablity
< 100)
5644 status_id
= SIPE_STATUS_ID_OFFLINE
;
5646 if (activity_desc
&& act
) {
5647 g_free(*activity_desc
);
5648 *activity_desc
= g_strdup(act
);
5655 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
5658 sipe_get_status_by_availability(int avail
,
5659 char** activity_desc
)
5662 const char *act
= NULL
;
5665 status
= SIPE_STATUS_ID_OFFLINE
;
5666 } else if (avail
< 4500) {
5667 status
= SIPE_STATUS_ID_AVAILABLE
;
5668 } else if (avail
< 6000) {
5669 //status = SIPE_STATUS_ID_IDLE;
5670 status
= SIPE_STATUS_ID_AVAILABLE
;
5671 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5672 } else if (avail
< 7500) {
5673 status
= SIPE_STATUS_ID_BUSY
;
5674 } else if (avail
< 9000) {
5675 //status = SIPE_STATUS_ID_BUSYIDLE;
5676 status
= SIPE_STATUS_ID_BUSY
;
5677 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
5678 } else if (avail
< 12000) {
5679 status
= SIPE_STATUS_ID_DND
;
5680 } else if (avail
< 15000) {
5681 status
= SIPE_STATUS_ID_BRB
;
5682 } else if (avail
< 18000) {
5683 status
= SIPE_STATUS_ID_AWAY
;
5685 status
= SIPE_STATUS_ID_OFFLINE
;
5688 if (activity_desc
&& act
) {
5689 g_free(*activity_desc
);
5690 *activity_desc
= g_strdup(act
);
5697 * Returns 2007-style availability value
5699 * @param sipe_status_id (in)
5700 * @param activity_token (out) Must be g_free()'d after use if consumed.
5703 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
5706 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
5708 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
5709 availability
= 15500;
5710 if (!activity_token
|| !(*activity_token
)) {
5711 activity
= SIPE_ACTIVITY_AWAY
;
5713 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
5714 availability
= 12500;
5715 activity
= SIPE_ACTIVITY_BRB
;
5716 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
5717 availability
= 9500;
5718 activity
= SIPE_ACTIVITY_DND
;
5719 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
5720 availability
= 6500;
5721 if (!activity_token
|| !(*activity_token
)) {
5722 activity
= SIPE_ACTIVITY_BUSY
;
5724 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
5725 availability
= 3500;
5726 activity
= SIPE_ACTIVITY_ONLINE
;
5727 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
5730 // Offline or invisible
5731 availability
= 18500;
5732 activity
= SIPE_ACTIVITY_OFFLINE
;
5735 if (activity_token
) {
5736 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
5738 return availability
;
5741 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5744 xmlnode
*xn_categories
;
5745 xmlnode
*xn_category
;
5747 const char *status
= NULL
;
5748 gboolean do_update_status
= FALSE
;
5749 gboolean has_note_cleaned
= FALSE
;
5750 gboolean has_free_busy_cleaned
= FALSE
;
5752 xn_categories
= xmlnode_from_str(data
, len
);
5753 uri
= xmlnode_get_attrib(xn_categories
, "uri"); /* with 'sip:' prefix */
5755 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
5757 xn_category
= xmlnode_get_next_twin(xn_category
) )
5760 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
5761 time_t publish_time
= (tmp
= xmlnode_get_attrib(xn_category
, "publishTime")) ?
5762 sipe_utils_str_to_time(tmp
) : 0;
5765 if (sipe_strequal(attrVar
, "contactCard"))
5768 /* identity - Display Name and email */
5769 node
= xmlnode_get_descendant(xn_category
, "contactCard", "identity", NULL
);
5771 char* display_name
= xmlnode_get_data(
5772 xmlnode_get_descendant(node
, "name", "displayName", NULL
));
5773 char* email
= xmlnode_get_data(
5774 xmlnode_get_child(node
, "email"));
5776 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5777 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
5779 g_free(display_name
);
5783 node
= xmlnode_get_descendant(xn_category
, "contactCard", "company", NULL
);
5785 char* company
= xmlnode_get_data(node
);
5786 sipe_update_user_info(sip
, uri
, COMPANY_PROP
, company
);
5790 node
= xmlnode_get_descendant(xn_category
, "contactCard", "department", NULL
);
5792 char* department
= xmlnode_get_data(node
);
5793 sipe_update_user_info(sip
, uri
, DEPARTMENT_PROP
, department
);
5797 node
= xmlnode_get_descendant(xn_category
, "contactCard", "title", NULL
);
5799 char* title
= xmlnode_get_data(node
);
5800 sipe_update_user_info(sip
, uri
, TITLE_PROP
, title
);
5804 node
= xmlnode_get_descendant(xn_category
, "contactCard", "office", NULL
);
5806 char* office
= xmlnode_get_data(node
);
5807 sipe_update_user_info(sip
, uri
, OFFICE_PROP
, office
);
5811 node
= xmlnode_get_descendant(xn_category
, "contactCard", "url", NULL
);
5813 char* site
= xmlnode_get_data(node
);
5814 sipe_update_user_info(sip
, uri
, SITE_PROP
, site
);
5818 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "phone", NULL
);
5820 node
= xmlnode_get_next_twin(node
))
5822 const char *phone_type
= xmlnode_get_attrib(node
, "type");
5823 char* phone
= xmlnode_get_data(xmlnode_get_child(node
, "uri"));
5824 char* phone_display_string
= xmlnode_get_data(xmlnode_get_child(node
, "displayString"));
5826 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, phone_display_string
);
5829 g_free(phone_display_string
);
5832 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "address", NULL
);
5834 node
= xmlnode_get_next_twin(node
))
5836 if (sipe_strequal(xmlnode_get_attrib(node
, "type"), "work")) {
5837 char* street
= xmlnode_get_data(xmlnode_get_child(node
, "street"));
5838 char* city
= xmlnode_get_data(xmlnode_get_child(node
, "city"));
5839 char* state
= xmlnode_get_data(xmlnode_get_child(node
, "state"));
5840 char* zipcode
= xmlnode_get_data(xmlnode_get_child(node
, "zipcode"));
5841 char* country_code
= xmlnode_get_data(xmlnode_get_child(node
, "countryCode"));
5843 sipe_update_user_info(sip
, uri
, ADDRESS_STREET_PROP
, street
);
5844 sipe_update_user_info(sip
, uri
, ADDRESS_CITY_PROP
, city
);
5845 sipe_update_user_info(sip
, uri
, ADDRESS_STATE_PROP
, state
);
5846 sipe_update_user_info(sip
, uri
, ADDRESS_ZIPCODE_PROP
, zipcode
);
5847 sipe_update_user_info(sip
, uri
, ADDRESS_COUNTRYCODE_PROP
, country_code
);
5853 g_free(country_code
);
5860 else if (sipe_strequal(attrVar
, "note"))
5863 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
5865 if (!has_note_cleaned
) {
5866 has_note_cleaned
= TRUE
;
5868 g_free(sbuddy
->note
);
5869 sbuddy
->note
= NULL
;
5870 sbuddy
->is_oof_note
= FALSE
;
5871 sbuddy
->note_since
= publish_time
;
5873 do_update_status
= TRUE
;
5875 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
5876 /* clean up in case no 'note' element is supplied
5877 * which indicate note removal in client
5879 g_free(sbuddy
->note
);
5880 sbuddy
->note
= NULL
;
5881 sbuddy
->is_oof_note
= FALSE
;
5882 sbuddy
->note_since
= publish_time
;
5884 xn_node
= xmlnode_get_descendant(xn_category
, "note", "body", NULL
);
5887 sbuddy
->note
= g_markup_escape_text((tmp
= xmlnode_get_data(xn_node
)), -1);
5889 sbuddy
->is_oof_note
= sipe_strequal(xmlnode_get_attrib(xn_node
, "type"), "OOF");
5890 sbuddy
->note_since
= publish_time
;
5892 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s), note(%s)\n",
5893 uri
, sbuddy
->note
? sbuddy
->note
: "");
5895 /* to trigger UI refresh in case no status info is supplied in this update */
5896 do_update_status
= TRUE
;
5901 else if(sipe_strequal(attrVar
, "state"))
5905 xmlnode
*xn_availability
;
5906 xmlnode
*xn_activity
;
5907 xmlnode
*xn_meeting_subject
;
5908 xmlnode
*xn_meeting_location
;
5909 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
5911 xn_node
= xmlnode_get_child(xn_category
, "state");
5912 if (!xn_node
) continue;
5913 xn_availability
= xmlnode_get_child(xn_node
, "availability");
5914 if (!xn_availability
) continue;
5915 xn_activity
= xmlnode_get_child(xn_node
, "activity");
5916 xn_meeting_subject
= xmlnode_get_child(xn_node
, "meetingSubject");
5917 xn_meeting_location
= xmlnode_get_child(xn_node
, "meetingLocation");
5919 tmp
= xmlnode_get_data(xn_availability
);
5920 availability
= atoi(tmp
);
5923 /* activity, meeting_subject, meeting_location */
5928 g_free(sbuddy
->activity
);
5929 sbuddy
->activity
= NULL
;
5931 const char *token
= xmlnode_get_attrib(xn_activity
, "token");
5932 xmlnode
*xn_custom
= xmlnode_get_child(xn_activity
, "custom");
5935 if (!is_empty(token
)) {
5936 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
5938 /* from custom element */
5940 char *custom
= xmlnode_get_data(xn_custom
);
5942 if (!is_empty(custom
)) {
5943 sbuddy
->activity
= custom
;
5949 /* meeting_subject */
5950 g_free(sbuddy
->meeting_subject
);
5951 sbuddy
->meeting_subject
= NULL
;
5952 if (xn_meeting_subject
) {
5953 char *meeting_subject
= xmlnode_get_data(xn_meeting_subject
);
5955 if (!is_empty(meeting_subject
)) {
5956 sbuddy
->meeting_subject
= meeting_subject
;
5957 meeting_subject
= NULL
;
5959 g_free(meeting_subject
);
5961 /* meeting_location */
5962 g_free(sbuddy
->meeting_location
);
5963 sbuddy
->meeting_location
= NULL
;
5964 if (xn_meeting_location
) {
5965 char *meeting_location
= xmlnode_get_data(xn_meeting_location
);
5967 if (!is_empty(meeting_location
)) {
5968 sbuddy
->meeting_location
= meeting_location
;
5969 meeting_location
= NULL
;
5971 g_free(meeting_location
);
5974 status
= sipe_get_status_by_availability(availability
, &tmp
);
5975 if (sbuddy
->activity
&& tmp
) {
5976 char *tmp2
= sbuddy
->activity
;
5978 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
5982 sbuddy
->activity
= tmp
;
5986 do_update_status
= TRUE
;
5989 else if(sipe_strequal(attrVar
, "calendarData"))
5991 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
5992 xmlnode
*xn_free_busy
= xmlnode_get_descendant(xn_category
, "calendarData", "freeBusy", NULL
);
5993 xmlnode
*xn_working_hours
= xmlnode_get_descendant(xn_category
, "calendarData", "WorkingHours", NULL
);
5995 if (sbuddy
&& xn_free_busy
) {
5996 if (!has_free_busy_cleaned
) {
5997 has_free_busy_cleaned
= TRUE
;
5999 g_free(sbuddy
->cal_start_time
);
6000 sbuddy
->cal_start_time
= NULL
;
6002 g_free(sbuddy
->cal_free_busy_base64
);
6003 sbuddy
->cal_free_busy_base64
= NULL
;
6005 g_free(sbuddy
->cal_free_busy
);
6006 sbuddy
->cal_free_busy
= NULL
;
6008 sbuddy
->cal_free_busy_published
= publish_time
;
6011 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
6012 g_free(sbuddy
->cal_start_time
);
6013 sbuddy
->cal_start_time
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
6015 sbuddy
->cal_granularity
= !g_ascii_strcasecmp(xmlnode_get_attrib(xn_free_busy
, "granularity"), "PT15M") ?
6018 g_free(sbuddy
->cal_free_busy_base64
);
6019 sbuddy
->cal_free_busy_base64
= xmlnode_get_data(xn_free_busy
);
6021 g_free(sbuddy
->cal_free_busy
);
6022 sbuddy
->cal_free_busy
= NULL
;
6024 sbuddy
->cal_free_busy_published
= publish_time
;
6026 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
);
6030 if (sbuddy
&& xn_working_hours
) {
6031 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
6036 if (do_update_status
) {
6037 if (!status
) { /* no status category in this update, using contact's current status */
6038 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
6039 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
6040 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
6041 status
= purple_status_get_id(pstatus
);
6044 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", status
);
6045 sipe_got_user_status(sip
, uri
, status
);
6048 xmlnode_free(xn_categories
);
6051 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
6053 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6054 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
6055 payload
->host
= g_strdup(host
);
6056 payload
->buddies
= server
;
6057 sipe_subscribe_presence_batched_routed(sip
, payload
);
6058 sipe_subscribe_presence_batched_routed_free(payload
);
6061 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6064 xmlnode
*xn_resource
;
6065 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
6070 xn_list
= xmlnode_from_str(data
, len
);
6072 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
6074 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
6076 const char *uri
, *state
;
6077 xmlnode
*xn_instance
;
6079 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
6080 if (!xn_instance
) continue;
6082 uri
= xmlnode_get_attrib(xn_resource
, "uri");
6083 state
= xmlnode_get_attrib(xn_instance
, "state");
6084 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
6086 if (strstr(state
, "resubscribe")) {
6087 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
6089 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
6090 gchar
*user
= g_strdup(uri
);
6091 host
= g_strdup(poolFqdn
);
6092 server
= g_hash_table_lookup(servers
, host
);
6093 server
= g_slist_append(server
, user
);
6094 g_hash_table_insert(servers
, host
, server
);
6096 sipe_subscribe_presence_single(sip
, (void *) uri
);
6101 /* Send out any deferred poolFqdn subscriptions */
6102 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
6103 g_hash_table_destroy(servers
);
6105 xmlnode_free(xn_list
);
6108 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6112 gchar
*activity
= NULL
;
6114 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
6115 gboolean isonline
= FALSE
;
6116 xmlnode
*display_name_node
;
6118 pidf
= xmlnode_from_str(data
, len
);
6120 purple_debug_info("sipe", "process_incoming_notify_pidf: no parseable pidf:%s\n",data
);
6124 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
6126 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6127 basicstatus
= xmlnode_get_child(status
, "basic");
6132 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic found\n");
6137 getbasic
= xmlnode_get_data(basicstatus
);
6139 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic data found\n");
6144 purple_debug_info("sipe", "process_incoming_notify_pidf: basic-status(%s)\n", getbasic
);
6145 if (strstr(getbasic
, "open")) {
6150 uri
= sip_uri(xmlnode_get_attrib(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
6152 display_name_node
= xmlnode_get_child(pidf
, "display-name");
6153 if (display_name_node
) {
6154 char * display_name
= xmlnode_get_data(display_name_node
);
6156 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6157 g_free(display_name
);
6160 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
6161 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6162 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
6163 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
6164 activity
= xmlnode_get_data(basicstatus
);
6165 purple_debug_info("sipe", "process_incoming_notify_pidf: activity(%s)\n", activity
);
6172 const gchar
* status_id
= NULL
;
6174 if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
6175 status_id
= SIPE_STATUS_ID_BUSY
;
6176 } else if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
6177 status_id
= SIPE_STATUS_ID_AWAY
;
6182 status_id
= SIPE_STATUS_ID_AVAILABLE
;
6185 purple_debug_info("sipe", "process_incoming_notify_pidf: status_id(%s)\n", status_id
);
6186 sipe_got_user_status(sip
, uri
, status_id
);
6188 sipe_got_user_status(sip
, uri
, SIPE_STATUS_ID_OFFLINE
);
6198 sipe_user_info_has_updated(struct sipe_account_data
*sip
,
6199 xmlnode
*xn_userinfo
)
6201 if (sip
->user_info
) {
6202 xmlnode_free(sip
->user_info
);
6204 sip
->user_info
= xmlnode_copy(xn_userinfo
);
6206 /* Publish initial state if not yet.
6207 * Assuming this happens on initial responce to self subscription
6208 * so we've already updated our UserInfo.
6210 if (!sip
->initial_state_published
) {
6211 send_presence_soap(sip
, FALSE
);
6213 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
6217 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6219 char *activity
= NULL
;
6221 const char *status_id
= NULL
;
6224 char *self_uri
= sip_uri_self(sip
);
6227 const char *device_name
= NULL
;
6228 const char *cal_start_time
= NULL
;
6229 const char *cal_granularity
= NULL
;
6230 char *cal_free_busy_base64
= NULL
;
6231 struct sipe_buddy
*sbuddy
;
6233 xmlnode
*xn_presentity
;
6234 xmlnode
*xn_availability
;
6235 xmlnode
*xn_activity
;
6236 xmlnode
*xn_display_name
;
6238 xmlnode
*xn_phone_number
;
6239 xmlnode
*xn_userinfo
;
6243 xmlnode
*xn_contact
;
6245 char *free_activity
;
6247 const char *user_avail_nil
;
6249 time_t user_avail_since
= 0;
6250 time_t activity_since
= 0;
6252 /* fix for Reuters environment on Linux */
6253 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
6255 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
6256 xn_presentity
= xmlnode_from_str(tmp_data
, strlen(tmp_data
));
6259 xn_presentity
= xmlnode_from_str(data
, len
);
6262 xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
6263 xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
6264 xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
6265 xn_email
= xmlnode_get_child(xn_presentity
, "email");
6266 xn_phone_number
= xmlnode_get_child(xn_presentity
, "phoneNumber");
6267 xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
6268 xn_oof
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "oof") : NULL
;
6269 xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
): NULL
;
6270 user_avail
= xn_state
? xmlnode_get_int_attrib(xn_state
, "avail", 0) : 0;
6271 user_avail_since
= xn_state
? sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since")) : 0;
6272 user_avail_nil
= xn_state
? xmlnode_get_attrib(xn_state
, "nil") : NULL
;
6273 xn_contact
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "contact") : NULL
;
6274 xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
6275 note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
6277 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
6279 user_avail_since
= 0;
6282 free_activity
= NULL
;
6284 name
= xmlnode_get_attrib(xn_presentity
, "uri"); /* without 'sip:' prefix */
6285 uri
= sip_uri_from_name(name
);
6286 avl
= xmlnode_get_int_attrib(xn_availability
, "aggregate", 0);
6287 epid
= xmlnode_get_attrib(xn_availability
, "epid");
6288 act
= xmlnode_get_int_attrib(xn_activity
, "aggregate", 0);
6290 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
6291 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
6292 if (user_avail
> res_avail
) {
6293 res_avail
= user_avail
;
6294 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
6297 if (xn_display_name
) {
6298 char *display_name
= g_strdup(xmlnode_get_attrib(xn_display_name
, "displayName"));
6299 char *email
= xn_email
? g_strdup(xmlnode_get_attrib(xn_email
, "email")) : NULL
;
6300 char *phone_label
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "label")) : NULL
;
6301 char *phone_number
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "number")) : NULL
;
6302 char *tel_uri
= sip_to_tel_uri(phone_number
);
6304 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6305 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
6306 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
6307 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, !is_empty(phone_label
) ? phone_label
: phone_number
);
6310 g_free(phone_label
);
6311 g_free(phone_number
);
6313 g_free(display_name
);
6318 for (node
= xmlnode_get_child(xn_contact
, "tel"); node
; node
= xmlnode_get_next_twin(node
))
6320 /* Ex.: <tel type="work">tel:+3222220000</tel> */
6321 const char *phone_type
= xmlnode_get_attrib(node
, "type");
6322 char* phone
= xmlnode_get_data(node
);
6324 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, NULL
);
6330 /* devicePresence */
6331 for (node
= xmlnode_get_descendant(xn_presentity
, "devices", "devicePresence", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
6332 xmlnode
*xn_device_name
;
6333 xmlnode
*xn_calendar_info
;
6338 if (sipe_strequal(xmlnode_get_attrib(node
, "epid"), epid
)) {
6339 xn_device_name
= xmlnode_get_child(node
, "deviceName");
6340 device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
6344 xn_calendar_info
= xmlnode_get_child(node
, "calendarInfo");
6345 if (xn_calendar_info
) {
6346 const char *cal_start_time_tmp
= xmlnode_get_attrib(xn_calendar_info
, "startTime");
6348 if (cal_start_time
) {
6349 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
6350 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
6352 if (cal_start_time_t_tmp
> cal_start_time_t
) {
6353 cal_start_time
= cal_start_time_tmp
;
6354 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6355 g_free(cal_free_busy_base64
);
6356 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6358 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
);
6361 cal_start_time
= cal_start_time_tmp
;
6362 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6363 g_free(cal_free_busy_base64
);
6364 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6366 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
);
6371 xn_state
= xmlnode_get_descendant(node
, "states", "state", NULL
);
6373 int dev_avail
= xmlnode_get_int_attrib(xn_state
, "avail", 0);
6374 time_t dev_avail_since
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since"));
6376 state
= xmlnode_get_data(xn_state
);
6377 if (dev_avail_since
> user_avail_since
&&
6378 dev_avail
>= res_avail
)
6380 res_avail
= dev_avail
;
6381 if (!is_empty(state
))
6383 if (sipe_strequal(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
6385 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
6386 } else if (sipe_strequal(state
, "presenting")) {
6388 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
6393 activity_since
= dev_avail_since
;
6395 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
6402 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
6404 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
6408 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
6411 g_free(sbuddy
->activity
);
6412 sbuddy
->activity
= activity
;
6415 sbuddy
->activity_since
= activity_since
;
6417 sbuddy
->user_avail
= user_avail
;
6418 sbuddy
->user_avail_since
= user_avail_since
;
6420 g_free(sbuddy
->note
);
6421 sbuddy
->note
= NULL
;
6422 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
6424 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
6426 g_free(sbuddy
->device_name
);
6427 sbuddy
->device_name
= NULL
;
6428 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
6430 if (!is_empty(cal_free_busy_base64
)) {
6431 g_free(sbuddy
->cal_start_time
);
6432 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
6434 sbuddy
->cal_granularity
= !g_ascii_strcasecmp(cal_granularity
, "PT15M") ? 15 : 0;
6436 g_free(sbuddy
->cal_free_busy_base64
);
6437 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
6438 cal_free_busy_base64
= NULL
;
6440 g_free(sbuddy
->cal_free_busy
);
6441 sbuddy
->cal_free_busy
= NULL
;
6444 sbuddy
->last_non_cal_status_id
= status_id
;
6445 g_free(sbuddy
->last_non_cal_activity
);
6446 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
6448 if (sipe_strequal(sbuddy
->name
, self_uri
)) {
6449 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
6451 sip
->is_oof_note
= sbuddy
->is_oof_note
;
6454 sip
->note
= g_strdup(sbuddy
->note
);
6456 sip
->note_since
= time(NULL
);
6459 g_free(sip
->status
);
6460 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
6463 g_free(cal_free_busy_base64
);
6466 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", status_id
);
6467 sipe_got_user_status(sip
, uri
, status_id
);
6469 if (!sip
->ocs2007
&& sipe_strequal(self_uri
, uri
)) {
6470 sipe_user_info_has_updated(sip
, xn_userinfo
);
6474 xmlnode_free(xn_presentity
);
6479 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
6481 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6483 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
6485 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
6486 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
6488 const char *content
= msg
->body
;
6489 unsigned length
= msg
->bodylen
;
6490 PurpleMimeDocument
*mime
= NULL
;
6492 if (strstr(ctype
, "multipart"))
6494 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6495 const char *content_type
;
6497 mime
= purple_mime_document_parse(doc
);
6498 parts
= purple_mime_document_get_parts(mime
);
6500 content
= purple_mime_part_get_data(parts
->data
);
6501 length
= purple_mime_part_get_length(parts
->data
);
6502 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
6503 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
6505 process_incoming_notify_rlmi_resub(sip
, content
, length
);
6507 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
6509 process_incoming_notify_msrtc(sip
, content
, length
);
6513 process_incoming_notify_rlmi(sip
, content
, length
);
6515 parts
= parts
->next
;
6521 purple_mime_document_free(mime
);
6524 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
6526 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
6528 else if(strstr(ctype
, "application/rlmi+xml"))
6530 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
6533 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
6535 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
6539 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
6543 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
6545 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6546 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6548 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
6551 strstr(ctype
, "multipart") &&
6552 (strstr(ctype
, "application/rlmi+xml") ||
6553 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
6554 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6555 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
6556 GList
*parts
= purple_mime_document_get_parts(mime
);
6557 GSList
*buddies
= NULL
;
6558 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6561 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
6562 purple_mime_part_get_length(parts
->data
));
6564 if (xml
&& !sipe_strequal(xml
->name
, "list")) {
6565 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
6567 buddies
= g_slist_append(buddies
, uri
);
6571 parts
= parts
->next
;
6574 if (mime
) purple_mime_document_free(mime
);
6576 payload
->host
= g_strdup(who
);
6577 payload
->buddies
= buddies
;
6578 sipe_schedule_action(action_name
, timeout
,
6579 sipe_subscribe_presence_batched_routed
,
6580 sipe_subscribe_presence_batched_routed_free
,
6582 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
6585 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6586 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
6588 g_free(action_name
);
6592 * Dispatcher for all incoming subscription information
6593 * whether it comes from NOTIFY, BENOTIFY requests or
6594 * piggy-backed to subscription's OK responce.
6596 * @param request whether initiated from BE/NOTIFY request or OK-response message.
6597 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
6599 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
6601 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
6602 const gchar
*event
= sipmsg_find_header(msg
, "Event");
6603 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
6606 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n",
6608 tmp
= fix_newlines(msg
->body
));
6610 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n", subscription_state
? subscription_state
: "");
6612 /* implicit subscriptions */
6613 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
6614 sipe_process_imdn(sip
, msg
);
6618 /* for one off subscriptions (send with Expire: 0) */
6619 if (!g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning-v2"))
6621 sipe_process_provisioning_v2(sip
, msg
);
6623 else if (!g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning"))
6625 sipe_process_provisioning(sip
, msg
);
6627 else if (!g_ascii_strcasecmp(event
, "presence"))
6629 sipe_process_presence(sip
, msg
);
6631 else if (!g_ascii_strcasecmp(event
, "registration-notify"))
6633 sipe_process_registration_notify(sip
, msg
);
6636 if (!subscription_state
|| strstr(subscription_state
, "active"))
6638 if (!g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
6640 sipe_process_roaming_contacts(sip
, msg
);
6642 else if (!g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self"))
6644 sipe_process_roaming_self(sip
, msg
);
6646 else if (!g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
6648 sipe_process_roaming_acl(sip
, msg
);
6650 else if (!g_ascii_strcasecmp(event
, "presence.wpending"))
6652 sipe_process_presence_wpending(sip
, msg
);
6654 else if (!g_ascii_strcasecmp(event
, "conference"))
6656 sipe_process_conference(sip
, msg
);
6661 /* The server sends status 'terminated' */
6662 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
6663 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
6664 gchar
*key
= sipe_get_subscription_key(event
, who
);
6666 purple_debug_info("sipe", "process_incoming_notify: server says that subscription to %s was terminated.\n", who
);
6669 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
6670 g_hash_table_remove(sip
->subscriptions
, key
);
6671 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
6677 if (!request
&& event
) {
6678 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
6679 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
6680 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n", timeout
);
6683 /* 2 min ahead of expiration */
6684 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
6686 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
6687 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
6689 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
6690 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
6691 g_free(action_name
);
6693 else if (!g_ascii_strcasecmp(event
, "presence") &&
6694 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
6696 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
6697 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6699 if (sip
->batched_support
) {
6700 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
6703 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6704 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
, timeout
);
6706 g_free(action_name
);
6712 /* The client responses on received a NOTIFY message */
6713 if (request
&& !benotify
)
6715 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
6720 * Whether user manually changed status or
6721 * it was changed automatically due to user
6722 * became inactive/active again
6725 sipe_is_user_state(struct sipe_account_data
*sip
)
6728 time_t now
= time(NULL
);
6730 purple_debug_info("sipe", "sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
6731 purple_debug_info("sipe", "sipe_is_user_state: now : %s", asctime(localtime(&now
)));
6733 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
6735 purple_debug_info("sipe", "sipe_is_user_state: res = %s\n", res
? "USER" : "MACHINE");
6740 send_presence_soap0(struct sipe_account_data
*sip
,
6741 gboolean do_publish_calendar
,
6742 gboolean do_reset_status
)
6744 struct sipe_ews
* ews
= sip
->ews
;
6745 int availability
= 0;
6750 gchar
*res_note
= NULL
;
6751 gchar
*res_oof
= NULL
;
6752 const gchar
*note_pub
= NULL
;
6753 gchar
*states
= NULL
;
6754 gchar
*calendar_data
= NULL
;
6755 gchar
*epid
= get_epid(sip
);
6756 time_t now
= time(NULL
);
6757 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
6758 const gchar
*oof_note
= ews
? sipe_ews_get_oof_note(ews
) : NULL
;
6759 const char *user_input
;
6760 gboolean pub_oof
= ews
&& oof_note
&& (!sip
->note
|| ews
->updated
> sip
->note_since
);
6762 if (oof_note
&& sip
->note
) {
6763 purple_debug_info("sipe", "ews->oof_start : %s", asctime(localtime(&(ews
->oof_start
))));
6764 purple_debug_info("sipe", "sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
6767 purple_debug_info("sipe", "sip->note : %s", sip
->note
? sip
->note
: "");
6769 if (!sip
->initial_state_published
||
6772 g_free(sip
->status
);
6773 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
6776 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
6780 note_pub
= oof_note
;
6781 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
6782 ews
->published
= TRUE
;
6783 } else if (sip
->note
) {
6784 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in ews already */
6787 sip
->is_oof_note
= FALSE
;
6788 sip
->note_since
= 0;
6790 note_pub
= sip
->note
;
6791 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
6797 /* to protocol internal plain text format */
6798 tmp
= purple_markup_strip_html(note_pub
);
6799 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
6804 if (!do_reset_status
) {
6805 if (sipe_is_user_state(sip
) && !do_publish_calendar
&& sip
->initial_state_published
)
6807 gchar
*activity_token
= NULL
;
6808 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
6810 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
6815 g_free(activity_token
);
6817 else /* preserve existing publication */
6820 if (sip
->user_info
&& (xn_states
= xmlnode_get_child(sip
->user_info
, "states"))) {
6821 states
= xmlnode_to_str(xn_states
, NULL
);
6822 /* this is a hack-around to remove added newline after inner element,
6823 * state in this case, where it shouldn't be.
6824 * After several use of xmlnode_to_str, amount of added newlines
6825 * grows significantly.
6827 purple_str_strip_char(states
, '\n');
6828 //purple_str_strip_char(states, '\r');
6832 /* do nothing - then User state will be erased */
6834 sip
->initial_state_published
= TRUE
;
6837 if (ews
&& (!is_empty(ews
->legacy_dn
) || !is_empty(ews
->email
)) && ews
->fb_start
&& !is_empty(ews
->free_busy
))
6839 char *fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
6840 char *free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
6841 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
6842 !is_empty(ews
->legacy_dn
) ? ews
->legacy_dn
: ews
->email
,
6845 g_free(fb_start_str
);
6846 g_free(free_busy_base64
);
6849 user_input
= !sipe_is_user_state(sip
) && sip
->status
!= SIPE_STATUS_ID_AVAILABLE
? "idle" : "active";
6851 /* forming resulting XML */
6852 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
6856 (tmp
= g_ascii_strup(sipe_get_host_name(), -1)),
6857 res_note
? res_note
: "",
6858 res_oof
? res_oof
: "",
6859 states
? states
: "",
6860 calendar_data
? calendar_data
: "",
6869 g_free(calendar_data
);
6871 send_soap_request(sip
, body
);
6874 g_free(since_time_str
);
6879 send_presence_soap(struct sipe_account_data
*sip
,
6880 gboolean do_publish_calendar
)
6882 return send_presence_soap0(sip
, do_publish_calendar
, FALSE
);
6887 process_send_presence_category_publish_response(struct sipe_account_data
*sip
,
6889 struct transaction
*trans
)
6891 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
6893 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
6899 gboolean has_device_publication
= FALSE
;
6901 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
6903 /* test if version mismatch fault */
6904 fault_code
= xmlnode_get_data(xmlnode_get_child(xml
, "Faultcode"));
6905 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
6906 purple_debug_info("sipe", "process_send_presence_category_publish_response: unsupported fault code:%s returning.\n", fault_code
);
6913 /* accumulating information about faulty versions */
6914 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
6915 for (node
= xmlnode_get_descendant(xml
, "details", "operation", NULL
);
6917 node
= xmlnode_get_next_twin(node
))
6919 const gchar
*index
= xmlnode_get_attrib(node
, "index");
6920 const gchar
*curVersion
= xmlnode_get_attrib(node
, "curVersion");
6922 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
6923 purple_debug_info("sipe", "fault added: index:%s curVersion:%s\n", index
, curVersion
);
6927 /* here we are parsing own request to figure out what publication
6928 * referensed here only by index went wrong
6930 xml
= xmlnode_from_str(trans
->msg
->body
, trans
->msg
->bodylen
);
6933 for (node
= xmlnode_get_descendant(xml
, "publications", "publication", NULL
),
6934 index_our
= 1; /* starts with 1 - our first publication */
6936 node
= xmlnode_get_next_twin(node
), index_our
++)
6938 gchar
*idx
= g_strdup_printf("%d", index_our
);
6939 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
6940 const gchar
*categoryName
= xmlnode_get_attrib(node
, "categoryName");
6943 if (sipe_strequal("device", categoryName
)) {
6944 has_device_publication
= TRUE
;
6947 if (curVersion
) { /* fault exist on this index */
6948 const gchar
*container
= xmlnode_get_attrib(node
, "container");
6949 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
6950 /* key is <category><instance><container> */
6951 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
6952 struct sipe_publication
*publication
=
6953 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, categoryName
), key
);
6955 purple_debug_info("sipe", "key is %s\n", key
);
6958 purple_debug_info("sipe", "Updating %s with version %s. Was %d before.\n",
6959 key
, curVersion
, publication
->version
);
6960 /* updating publication's version to the correct one */
6961 publication
->version
= atoi(curVersion
);
6967 g_hash_table_destroy(faults
);
6969 /* rebublishing with right versions */
6970 if (has_device_publication
) {
6971 send_publish_category_initial(sip
);
6973 send_presence_status(sip
);
6980 * Returns 'device' XML part for publication.
6981 * Must be g_free'd after use.
6984 sipe_publish_get_category_device(struct sipe_account_data
*sip
)
6988 gchar
*epid
= get_epid(sip
);
6989 gchar
*uuid
= generateUUIDfromEPID(epid
);
6990 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
6991 /* key is <category><instance><container> */
6992 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
6993 struct sipe_publication
*publication
=
6994 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
6999 uri
= sip_uri_self(sip
);
7000 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
7002 publication
? publication
->version
: 0,
7005 "00:00:00+01:00", /* @TODO make timezone real*/
7006 sipe_get_host_name()
7016 * A service method - use
7017 * - send_publish_get_category_state_machine and
7018 * - send_publish_get_category_state_user instead.
7019 * Must be g_free'd after use.
7022 sipe_publish_get_category_state(struct sipe_account_data
*sip
,
7023 gboolean is_user_state
)
7025 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
7026 guint instance
= is_user_state
? sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
) :
7027 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
7028 /* key is <category><instance><container> */
7029 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7030 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7031 struct sipe_publication
*publication_2
=
7032 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7033 struct sipe_publication
*publication_3
=
7034 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7039 if (publication_2
&& (publication_2
->availability
== availability
))
7041 purple_debug_info("sipe", "sipe_publish_get_category_state: state has NOT changed. Exiting.\n");
7042 return NULL
; /* nothing to update */
7045 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
7047 publication_2
? publication_2
->version
: 0,
7050 publication_3
? publication_3
->version
: 0,
7055 * Only Busy and OOF calendar event are published.
7056 * Different instances are used for that.
7058 * Must be g_free'd after use.
7061 sipe_publish_get_category_state_calendar(struct sipe_account_data
*sip
,
7062 struct sipe_cal_event
*event
,
7066 gchar
*start_time_str
;
7067 int availability
= 0;
7070 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
7071 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
) :
7072 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
7074 /* key is <category><instance><container> */
7075 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7076 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7077 struct sipe_publication
*publication_2
=
7078 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7079 struct sipe_publication
*publication_3
=
7080 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7085 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
7086 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7087 "Exiting as no publication and no event for cal_satus:%d\n", cal_satus
);
7093 (publication_3
->availability
== availability
) &&
7094 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
7097 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7098 "cal state has NOT changed for cal_satus:%d. Exiting.\n", cal_satus
);
7099 return NULL
; /* nothing to update */
7104 (event
->cal_status
== SIPE_CAL_BUSY
||
7105 event
->cal_status
== SIPE_CAL_OOF
))
7107 gchar
*availability_xml_str
= NULL
;
7108 gchar
*activity_xml_str
= NULL
;
7110 if (event
->cal_status
== SIPE_CAL_BUSY
) {
7111 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
7114 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
7115 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7116 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
7117 "minAvailability=\"6500\"",
7118 "maxAvailability=\"8999\"");
7119 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
7120 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7121 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
7122 "minAvailability=\"12000\"",
7125 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
7127 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
7129 publication_2
? publication_2
->version
: 0,
7132 availability_xml_str
? availability_xml_str
: "",
7133 activity_xml_str
? activity_xml_str
: "",
7134 event
->subject
? event
->subject
: "",
7135 event
->location
? event
->location
: "",
7138 publication_3
? publication_3
->version
: 0,
7141 availability_xml_str
? availability_xml_str
: "",
7142 activity_xml_str
? activity_xml_str
: "",
7143 event
->subject
? event
->subject
: "",
7144 event
->location
? event
->location
: ""
7146 g_free(start_time_str
);
7147 g_free(availability_xml_str
);
7148 g_free(activity_xml_str
);
7151 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
7153 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
7155 publication_2
? publication_2
->version
: 0,
7158 publication_3
? publication_3
->version
: 0
7166 * Returns 'machineState' XML part for publication.
7167 * Must be g_free'd after use.
7170 sipe_publish_get_category_state_machine(struct sipe_account_data
*sip
)
7172 return sipe_publish_get_category_state(sip
, FALSE
);
7176 * Returns 'userState' XML part for publication.
7177 * Must be g_free'd after use.
7180 sipe_publish_get_category_state_user(struct sipe_account_data
*sip
)
7182 return sipe_publish_get_category_state(sip
, TRUE
);
7186 * Returns 'note' XML part for publication.
7187 * Must be g_free'd after use.
7189 * Protocol format for Note is plain text.
7191 * @param note a note in Sipe internal HTML format
7192 * @param note_type either personal or OOF
7195 sipe_publish_get_category_note(struct sipe_account_data
*sip
,
7196 const char *note
, /* html */
7197 const char *note_type
,
7201 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
) : 0;
7202 /* key is <category><instance><container> */
7203 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
7204 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
7205 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
7207 struct sipe_publication
*publication_note_200
=
7208 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
7209 struct sipe_publication
*publication_note_300
=
7210 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
7211 struct sipe_publication
*publication_note_400
=
7212 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
7214 char *tmp
= note
? purple_markup_strip_html(note
) : NULL
;
7215 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
7216 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
7217 char *res
, *tmp1
, *tmp2
, *tmp3
;
7218 char *start_time_attr
;
7219 char *end_time_attr
;
7223 g_free(key_note_200
);
7224 g_free(key_note_300
);
7225 g_free(key_note_400
);
7227 /* we even need to republish empty note */
7228 if (sipe_strequal(n1
, n2
))
7230 purple_debug_info("sipe", "sipe_publish_get_category_note: note has NOT changed. Exiting.\n");
7232 return NULL
; /* nothing to update */
7235 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
7238 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
7242 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7245 publication_note_200
? publication_note_200
->version
: 0,
7247 start_time_attr
? start_time_attr
: "",
7248 end_time_attr
? end_time_attr
: "",
7251 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7254 publication_note_300
? publication_note_300
->version
: 0,
7256 start_time_attr
? start_time_attr
: "",
7257 end_time_attr
? end_time_attr
: "",
7260 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7263 publication_note_400
? publication_note_400
->version
: 0,
7265 start_time_attr
? start_time_attr
: "",
7266 end_time_attr
? end_time_attr
: "",
7269 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7273 publication_note_200
? publication_note_200
->version
: 0,
7275 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7279 publication_note_200
? publication_note_200
->version
: 0,
7281 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7285 publication_note_200
? publication_note_200
->version
: 0,
7288 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
7290 g_free(start_time_attr
);
7291 g_free(end_time_attr
);
7301 * Returns 'calendarData' XML part with WorkingHours for publication.
7302 * Must be g_free'd after use.
7305 sipe_publish_get_category_cal_working_hours(struct sipe_account_data
*sip
)
7307 struct sipe_ews
* ews
= sip
->ews
;
7309 /* key is <category><instance><container> */
7310 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
7311 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
7312 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
7313 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
7314 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
7315 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
7317 struct sipe_publication
*publication_cal_1
=
7318 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7319 struct sipe_publication
*publication_cal_100
=
7320 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7321 struct sipe_publication
*publication_cal_200
=
7322 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7323 struct sipe_publication
*publication_cal_300
=
7324 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7325 struct sipe_publication
*publication_cal_400
=
7326 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7327 struct sipe_publication
*publication_cal_32000
=
7328 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7330 const char *n1
= ews
? ews
->working_hours_xml_str
: NULL
;
7331 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
7334 g_free(key_cal_100
);
7335 g_free(key_cal_200
);
7336 g_free(key_cal_300
);
7337 g_free(key_cal_400
);
7338 g_free(key_cal_32000
);
7340 if (!ews
|| is_empty(ews
->email
) || is_empty(ews
->working_hours_xml_str
)) {
7341 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: no data to publish, exiting\n");
7345 if (sipe_strequal(n1
, n2
))
7347 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.\n");
7348 return NULL
; /* nothing to update */
7351 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
7353 publication_cal_1
? publication_cal_1
->version
: 0,
7355 ews
->working_hours_xml_str
,
7357 publication_cal_100
? publication_cal_100
->version
: 0,
7359 publication_cal_200
? publication_cal_200
->version
: 0,
7361 ews
->working_hours_xml_str
,
7363 publication_cal_300
? publication_cal_300
->version
: 0,
7365 ews
->working_hours_xml_str
,
7366 /* 400 - Personal */
7367 publication_cal_400
? publication_cal_400
->version
: 0,
7369 ews
->working_hours_xml_str
,
7370 /* 32000 - Blocked */
7371 publication_cal_32000
? publication_cal_32000
->version
: 0
7376 * Returns 'calendarData' XML part with FreeBusy for publication.
7377 * Must be g_free'd after use.
7380 sipe_publish_get_category_cal_free_busy(struct sipe_account_data
*sip
)
7382 struct sipe_ews
* ews
= sip
->ews
;
7383 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
7385 char *free_busy_base64
;
7390 /* key is <category><instance><container> */
7391 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
7392 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
7393 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
7394 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
7395 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
7396 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
7398 struct sipe_publication
*publication_cal_1
=
7399 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7400 struct sipe_publication
*publication_cal_100
=
7401 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7402 struct sipe_publication
*publication_cal_200
=
7403 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7404 struct sipe_publication
*publication_cal_300
=
7405 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7406 struct sipe_publication
*publication_cal_400
=
7407 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7408 struct sipe_publication
*publication_cal_32000
=
7409 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7412 g_free(key_cal_100
);
7413 g_free(key_cal_200
);
7414 g_free(key_cal_300
);
7415 g_free(key_cal_400
);
7416 g_free(key_cal_32000
);
7418 if (!ews
|| is_empty(ews
->email
) || !ews
->fb_start
|| is_empty(ews
->free_busy
)) {
7419 purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: no data to publish, exiting\n");
7423 fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
7424 free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
7426 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
7427 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
7429 /* we will rebuplish the same data to refresh publication time,
7430 * so if data from multiple sources, most recent will be choosen
7432 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
7434 // purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.\n");
7435 // g_free(fb_start_str);
7436 // g_free(free_busy_base64);
7437 // return NULL; /* nothing to update */
7440 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
7443 publication_cal_1
? publication_cal_1
->version
: 0,
7446 publication_cal_100
? publication_cal_100
->version
: 0,
7449 publication_cal_200
? publication_cal_200
->version
: 0,
7455 publication_cal_300
? publication_cal_300
->version
: 0,
7459 /* 400 - Personal */
7461 publication_cal_400
? publication_cal_400
->version
: 0,
7465 /* 32000 - Blocked */
7467 publication_cal_32000
? publication_cal_32000
->version
: 0
7470 g_free(fb_start_str
);
7471 g_free(free_busy_base64
);
7475 static void send_presence_publish(struct sipe_account_data
*sip
, const char *publications
)
7482 uri
= sip_uri_self(sip
);
7483 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
7487 tmp
= get_contact(sip
);
7488 hdr
= g_strdup_printf("Contact: %s\r\n"
7489 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
7491 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
7500 send_publish_category_initial(struct sipe_account_data
*sip
)
7502 gchar
*pub_device
= sipe_publish_get_category_device(sip
);
7504 gchar
*publications
;
7506 g_free(sip
->status
);
7507 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
7509 pub_machine
= sipe_publish_get_category_state_machine(sip
);
7510 publications
= g_strdup_printf("%s%s",
7512 pub_machine
? pub_machine
: "");
7514 g_free(pub_machine
);
7516 send_presence_publish(sip
, publications
);
7517 g_free(publications
);
7521 send_presence_category_publish(struct sipe_account_data
*sip
)
7523 gchar
*pub_state
= sipe_is_user_state(sip
) ?
7524 sipe_publish_get_category_state_user(sip
) :
7525 sipe_publish_get_category_state_machine(sip
);
7526 gchar
*pub_note
= sipe_publish_get_category_note(sip
,
7528 sip
->is_oof_note
? "OOF" : "personal",
7531 gchar
*publications
;
7533 if (!pub_state
&& !pub_note
) {
7534 purple_debug_info("sipe", "send_presence_category_publish: nothing has changed. Exiting.\n");
7538 publications
= g_strdup_printf("%s%s",
7539 pub_state
? pub_state
: "",
7540 pub_note
? pub_note
: "");
7545 send_presence_publish(sip
, publications
);
7546 g_free(publications
);
7550 * Publishes self status
7551 * based on own calendar information.
7556 publish_calendar_status_self(struct sipe_account_data
*sip
)
7558 struct sipe_cal_event
* event
= NULL
;
7559 gchar
*pub_cal_working_hours
= NULL
;
7560 gchar
*pub_cal_free_busy
= NULL
;
7561 gchar
*pub_calendar
= NULL
;
7562 gchar
*pub_calendar2
= NULL
;
7563 gchar
*pub_oof_note
= NULL
;
7564 const gchar
*oof_note
;
7565 time_t oof_start
= 0;
7569 purple_debug_info("sipe", "publish_calendar_status_self() no calendar data.\n");
7573 purple_debug_info("sipe", "publish_calendar_status_self() started.\n");
7574 if (sip
->ews
->cal_events
) {
7575 event
= sipe_cal_get_event(sip
->ews
->cal_events
, time(NULL
));
7579 purple_debug_info("sipe", "publish_calendar_status_self: current event is NULL\n");
7581 char *desc
= sipe_cal_event_describe(event
);
7582 purple_debug_info("sipe", "publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
7588 OOF publish, Busy clean
7590 OOF clean, Busy publish
7592 OOF clean, Busy clean
7594 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
7595 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_OOF
);
7596 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7597 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
7598 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7599 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7601 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7602 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7605 oof_note
= sipe_ews_get_oof_note(sip
->ews
);
7606 if (sipe_strequal("Scheduled", sip
->ews
->oof_state
)) {
7607 oof_start
= sip
->ews
->oof_start
;
7608 oof_end
= sip
->ews
->oof_end
;
7610 pub_oof_note
= sipe_publish_get_category_note(sip
, oof_note
, "OOF", oof_start
, oof_end
);
7612 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sip
);
7613 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sip
);
7615 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
7616 purple_debug_info("sipe", "publish_calendar_status_self: nothing has changed.\n");
7618 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
7619 pub_cal_working_hours
? pub_cal_working_hours
: "",
7620 pub_cal_free_busy
? pub_cal_free_busy
: "",
7621 pub_calendar
? pub_calendar
: "",
7622 pub_calendar2
? pub_calendar2
: "",
7623 pub_oof_note
? pub_oof_note
: "");
7625 send_presence_publish(sip
, publications
);
7626 g_free(publications
);
7629 g_free(pub_cal_working_hours
);
7630 g_free(pub_cal_free_busy
);
7631 g_free(pub_calendar
);
7632 g_free(pub_calendar2
);
7633 g_free(pub_oof_note
);
7635 /* repeat scheduling */
7636 sipe_sched_calendar_status_self_publish(sip
, time(NULL
));
7639 static void send_presence_status(struct sipe_account_data
*sip
)
7641 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
7643 if (!status
) return;
7645 purple_debug_info("sipe", "send_presence_status: status: %s (%s)\n",
7646 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
7647 sipe_is_user_state(sip
) ? "USER" : "MACHINE");
7650 send_presence_category_publish(sip
);
7652 send_presence_soap(sip
, FALSE
);
7656 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
7658 gboolean found
= FALSE
;
7659 const char *method
= msg
->method
? msg
->method
: "NOT FOUND";
7660 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,method
);
7661 if (msg
->response
== 0) { /* request */
7662 if (sipe_strequal(method
, "MESSAGE")) {
7663 process_incoming_message(sip
, msg
);
7665 } else if (sipe_strequal(method
, "NOTIFY")) {
7666 purple_debug_info("sipe","send->process_incoming_notify\n");
7667 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
7669 } else if (sipe_strequal(method
, "BENOTIFY")) {
7670 purple_debug_info("sipe","send->process_incoming_benotify\n");
7671 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
7673 } else if (sipe_strequal(method
, "INVITE")) {
7674 process_incoming_invite(sip
, msg
);
7676 } else if (sipe_strequal(method
, "REFER")) {
7677 process_incoming_refer(sip
, msg
);
7679 } else if (sipe_strequal(method
, "OPTIONS")) {
7680 process_incoming_options(sip
, msg
);
7682 } else if (sipe_strequal(method
, "INFO")) {
7683 process_incoming_info(sip
, msg
);
7685 } else if (sipe_strequal(method
, "ACK")) {
7686 // ACK's don't need any response
7688 } else if (sipe_strequal(method
, "SUBSCRIBE")) {
7689 // LCS 2005 sends us these - just respond 200 OK
7691 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
7692 } else if (sipe_strequal(method
, "BYE")) {
7693 process_incoming_bye(sip
, msg
);
7696 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
7698 } else { /* response */
7699 struct transaction
*trans
= transactions_find(sip
, msg
);
7701 if (msg
->response
== 407) {
7702 gchar
*resend
, *auth
, *ptmp
;
7704 if (sip
->proxy
.retries
> 30) return;
7705 sip
->proxy
.retries
++;
7706 /* do proxy authentication */
7708 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
7710 fill_auth(ptmp
, &sip
->proxy
);
7711 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
7712 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
7713 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
7715 resend
= sipmsg_to_string(trans
->msg
);
7716 /* resend request */
7717 sendout_pkt(sip
->gc
, resend
);
7720 if (msg
->response
< 200) {
7721 /* ignore provisional response */
7722 purple_debug_info("sipe", "got provisional (%d) response, ignoring\n", msg
->response
);
7724 sip
->proxy
.retries
= 0;
7725 if (sipe_strequal(trans
->msg
->method
, "REGISTER")) {
7726 if (msg
->response
== 401)
7728 sip
->registrar
.retries
++;
7732 sip
->registrar
.retries
= 0;
7734 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\n", sip
->cseq
);
7736 if (msg
->response
== 401) {
7737 gchar
*resend
, *auth
, *ptmp
;
7738 const char* auth_scheme
;
7740 if (sip
->registrar
.retries
> 4) return;
7741 sip
->registrar
.retries
++;
7743 auth_scheme
= sipe_get_auth_scheme_name(sip
);
7744 ptmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
7746 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\n", ptmp
? ptmp
: "");
7748 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
7749 sip
->gc
->wants_to_die
= TRUE
;
7750 purple_connection_error(sip
->gc
, tmp2
);
7755 fill_auth(ptmp
, &sip
->registrar
);
7756 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
7757 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
7758 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
7760 //sipmsg_remove_header_now(trans->msg, "Authorization");
7761 //sipmsg_add_header(trans->msg, "Authorization", auth);
7763 resend
= sipmsg_to_string(trans
->msg
);
7764 /* resend request */
7765 sendout_pkt(sip
->gc
, resend
);
7770 if (trans
->callback
) {
7771 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\n");
7772 /* call the callback to process response*/
7773 (trans
->callback
)(sip
, msg
, trans
);
7776 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\n", sip
->cseq
);
7777 transactions_remove(sip
, trans
);
7783 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
7787 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", method
, msg
->response
);
7791 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
7800 /* according to the RFC remove CRLF at the beginning */
7801 while (*cur
== '\r' || *cur
== '\n') {
7804 if (cur
!= conn
->inbuf
) {
7805 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
7806 conn
->inbufused
= strlen(conn
->inbuf
);
7809 /* Received a full Header? */
7810 sip
->processing_input
= TRUE
;
7811 while (sip
->processing_input
&&
7812 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
7813 time_t currtime
= time(NULL
);
7816 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), tmp
= fix_newlines(conn
->inbuf
));
7818 msg
= sipmsg_parse_header(conn
->inbuf
);
7821 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
7822 if (msg
&& restlen
>= msg
->bodylen
) {
7823 dummy
= g_malloc(msg
->bodylen
+ 1);
7824 memcpy(dummy
, cur
, msg
->bodylen
);
7825 dummy
[msg
->bodylen
] = '\0';
7827 cur
+= msg
->bodylen
;
7828 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
7829 conn
->inbufused
= strlen(conn
->inbuf
);
7832 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n", restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
7839 purple_debug_info("sipe", "body:\n%s", msg->body);
7842 // Verify the signature before processing it
7843 if (sip
->registrar
.gssapi_context
) {
7844 struct sipmsg_breakdown msgbd
;
7845 gchar
*signature_input_str
;
7848 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
7849 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
7851 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
7853 if (rspauth
!= NULL
) {
7854 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
7855 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
7856 process_input_message(sip
, msg
);
7858 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
7859 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
7860 sip
->gc
->wants_to_die
= TRUE
;
7862 } else if (msg
->response
== 401) {
7863 purple_connection_error(sip
->gc
, _("Wrong password"));
7864 sip
->gc
->wants_to_die
= TRUE
;
7866 g_free(signature_input_str
);
7869 sipmsg_breakdown_free(&msgbd
);
7871 process_input_message(sip
, msg
);
7878 static void sipe_udp_process(gpointer data
, gint source
,
7879 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
7881 PurpleConnection
*gc
= data
;
7882 struct sipe_account_data
*sip
= gc
->proto_data
;
7885 static char buffer
[65536];
7886 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
7887 time_t currtime
= time(NULL
);
7890 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), buffer
);
7891 msg
= sipmsg_parse_msg(buffer
);
7892 if (msg
) process_input_message(sip
, msg
);
7896 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
7898 struct sipe_account_data
*sip
= gc
->proto_data
;
7899 PurpleSslConnection
*gsc
= sip
->gsc
;
7901 purple_debug_error("sipe", "%s",debug
);
7902 purple_connection_error(gc
, msg
);
7904 /* Invalidate this connection. Next send will open a new one */
7906 connection_remove(sip
, gsc
->fd
);
7907 purple_ssl_close(gsc
);
7913 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
7914 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
7916 PurpleConnection
*gc
= data
;
7917 struct sipe_account_data
*sip
;
7918 struct sip_connection
*conn
;
7920 gboolean firstread
= TRUE
;
7922 /* NOTE: This check *IS* necessary */
7923 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
7924 purple_ssl_close(gsc
);
7928 sip
= gc
->proto_data
;
7929 conn
= connection_find(sip
, gsc
->fd
);
7931 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
7932 gc
->wants_to_die
= TRUE
;
7933 purple_connection_error(gc
, _("Connection not found. Please try to connect again"));
7937 /* Read all available data from the SSL connection */
7939 /* Increase input buffer size as needed */
7940 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
7941 conn
->inbuflen
+= SIMPLE_BUF_INC
;
7942 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
7943 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
7946 /* Try to read as much as there is space left in the buffer */
7947 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
7948 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
7950 if (len
< 0 && errno
== EAGAIN
) {
7951 /* Try again later */
7953 } else if (len
< 0) {
7954 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
7956 } else if (firstread
&& (len
== 0)) {
7957 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
7961 conn
->inbufused
+= len
;
7964 /* Equivalence indicates that there is possibly more data to read */
7965 } while (len
== readlen
);
7967 conn
->inbuf
[conn
->inbufused
] = '\0';
7968 process_input(sip
, conn
);
7972 static void sipe_input_cb(gpointer data
, gint source
,
7973 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
7975 PurpleConnection
*gc
= data
;
7976 struct sipe_account_data
*sip
= gc
->proto_data
;
7978 struct sip_connection
*conn
= connection_find(sip
, source
);
7980 purple_debug_error("sipe", "Connection not found!\n");
7984 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
7985 conn
->inbuflen
+= SIMPLE_BUF_INC
;
7986 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
7989 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
7991 if (len
< 0 && errno
== EAGAIN
)
7993 else if (len
<= 0) {
7994 purple_debug_info("sipe", "sipe_input_cb: read error\n");
7995 connection_remove(sip
, source
);
7996 if (sip
->fd
== source
) sip
->fd
= -1;
8000 conn
->inbufused
+= len
;
8001 conn
->inbuf
[conn
->inbufused
] = '\0';
8003 process_input(sip
, conn
);
8006 /* Callback for new connections on incoming TCP port */
8007 static void sipe_newconn_cb(gpointer data
, gint source
,
8008 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8010 PurpleConnection
*gc
= data
;
8011 struct sipe_account_data
*sip
= gc
->proto_data
;
8012 struct sip_connection
*conn
;
8014 int newfd
= accept(source
, NULL
, NULL
);
8016 conn
= connection_create(sip
, newfd
);
8018 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8021 static void login_cb(gpointer data
, gint source
,
8022 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
8024 PurpleConnection
*gc
= data
;
8025 struct sipe_account_data
*sip
;
8026 struct sip_connection
*conn
;
8028 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8036 purple_connection_error(gc
, _("Could not connect"));
8040 sip
= gc
->proto_data
;
8042 sip
->last_keepalive
= time(NULL
);
8044 conn
= connection_create(sip
, source
);
8048 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8051 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
8052 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8054 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
8055 if (sip
== NULL
) return;
8060 static guint
sipe_ht_hash_nick(const char *nick
)
8062 char *lc
= g_utf8_strdown(nick
, -1);
8063 guint bucket
= g_str_hash(lc
);
8069 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
8071 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
8074 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
8076 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8078 sip
->listen_data
= NULL
;
8080 if (listenfd
== -1) {
8081 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8087 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
8088 sip
->listenfd
= sip
->fd
;
8090 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
8092 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
8096 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
8097 SIPE_UNUSED_PARAMETER
const char *error_message
)
8099 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8101 sip
->query_data
= NULL
;
8103 if (!hosts
|| !hosts
->data
) {
8104 purple_connection_error(sip
->gc
, _("Could not resolve hostname"));
8108 hosts
= g_slist_remove(hosts
, hosts
->data
);
8109 g_free(sip
->serveraddr
);
8110 sip
->serveraddr
= hosts
->data
;
8111 hosts
= g_slist_remove(hosts
, hosts
->data
);
8113 void *tmp
= hosts
->data
;
8114 hosts
= g_slist_remove(hosts
, tmp
);
8115 hosts
= g_slist_remove(hosts
, tmp
);
8119 /* create socket for incoming connections */
8120 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
8121 sipe_udp_host_resolved_listen_cb
, sip
);
8122 if (sip
->listen_data
== NULL
) {
8123 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8128 static const struct sipe_service_data
*current_service
= NULL
;
8130 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
8131 PurpleSslErrorType error
,
8134 PurpleConnection
*gc
= data
;
8135 struct sipe_account_data
*sip
;
8137 /* If the connection is already disconnected, we don't need to do anything else */
8138 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8141 sip
= gc
->proto_data
;
8142 current_service
= sip
->service_data
;
8143 if (current_service
) {
8144 purple_debug_info("sipe", "current_service: transport '%s' service '%s'\n",
8145 current_service
->transport
? current_service
->transport
: "NULL",
8146 current_service
->service
? current_service
->service
: "NULL");
8153 case PURPLE_SSL_CONNECT_FAILED
:
8154 purple_connection_error(gc
, _("Connection failed"));
8156 case PURPLE_SSL_HANDSHAKE_FAILED
:
8157 purple_connection_error(gc
, _("SSL handshake failed"));
8159 case PURPLE_SSL_CERTIFICATE_INVALID
:
8160 purple_connection_error(gc
, _("SSL certificate invalid"));
8166 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
8168 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8169 PurpleProxyConnectData
*connect_data
;
8171 sip
->listen_data
= NULL
;
8173 sip
->listenfd
= listenfd
;
8174 if (sip
->listenfd
== -1) {
8175 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8179 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
8180 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
8181 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
8182 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
8183 sipe_newconn_cb
, sip
->gc
);
8184 purple_debug_info("sipe", "connecting to %s port %d\n",
8185 sip
->realhostname
, sip
->realport
);
8186 /* open tcp connection to the server */
8187 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
8188 sip
->realport
, login_cb
, sip
->gc
);
8190 if (connect_data
== NULL
) {
8191 purple_connection_error(sip
->gc
, _("Could not create socket"));
8195 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
8197 PurpleAccount
*account
= sip
->account
;
8198 PurpleConnection
*gc
= sip
->gc
;
8201 port
= (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
8204 sip
->realhostname
= hostname
;
8205 sip
->realport
= port
;
8207 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
8210 /* TODO: is there a good default grow size? */
8211 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
8212 sip
->txbuf
= purple_circ_buffer_new(0);
8214 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
8216 if (!purple_ssl_is_supported()) {
8217 gc
->wants_to_die
= TRUE
;
8218 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor"));
8222 purple_debug_info("sipe", "using SSL\n");
8224 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
8225 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
8226 if (sip
->gsc
== NULL
) {
8227 purple_connection_error(gc
, _("Could not create SSL context"));
8230 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
8232 purple_debug_info("sipe", "using UDP\n");
8234 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
8235 if (sip
->query_data
== NULL
) {
8236 purple_connection_error(gc
, _("Could not resolve hostname"));
8240 purple_debug_info("sipe", "using TCP\n");
8241 /* create socket for incoming connections */
8242 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
8243 sipe_tcp_connect_listen_cb
, sip
);
8244 if (sip
->listen_data
== NULL
) {
8245 purple_connection_error(gc
, _("Could not create listen socket"));
8251 /* Service list for autodection */
8252 static const struct sipe_service_data service_autodetect
[] = {
8253 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8254 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8255 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8256 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8260 /* Service list for SSL/TLS */
8261 static const struct sipe_service_data service_tls
[] = {
8262 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8263 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8267 /* Service list for TCP */
8268 static const struct sipe_service_data service_tcp
[] = {
8269 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8270 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8274 /* Service list for UDP */
8275 static const struct sipe_service_data service_udp
[] = {
8276 { "sip", "udp", SIPE_TRANSPORT_UDP
},
8280 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
8281 static void resolve_next_service(struct sipe_account_data
*sip
,
8282 const struct sipe_service_data
*start
)
8285 sip
->service_data
= start
;
8287 sip
->service_data
++;
8288 if (sip
->service_data
->service
== NULL
) {
8290 /* Try connecting to the SIP hostname directly */
8291 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
8292 if (sip
->auto_transport
) {
8293 // If SSL is supported, default to using it; OCS servers aren't configured
8294 // by default to accept TCP
8295 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
8296 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8297 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
8300 hostname
= g_strdup(sip
->sipdomain
);
8301 create_connection(sip
, hostname
, 0);
8306 /* Try to resolve next service */
8307 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
8308 sip
->service_data
->transport
,
8313 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
8315 struct sipe_account_data
*sip
= data
;
8317 sip
->srv_query_data
= NULL
;
8319 /* find the host to connect to */
8321 gchar
*hostname
= g_strdup(resp
->hostname
);
8322 int port
= resp
->port
;
8323 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
8327 sip
->transport
= sip
->service_data
->type
;
8329 create_connection(sip
, hostname
, port
);
8331 resolve_next_service(sip
, NULL
);
8335 static void sipe_login(PurpleAccount
*account
)
8337 PurpleConnection
*gc
;
8338 struct sipe_account_data
*sip
;
8339 gchar
**signinname_login
, **userserver
;
8340 const char *transport
;
8343 const char *username
= purple_account_get_username(account
);
8344 gc
= purple_account_get_connection(account
);
8346 purple_debug_info("sipe", "sipe_login: username '%s'\n", username
);
8348 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
8349 gc
->wants_to_die
= TRUE
;
8350 purple_connection_error(gc
, _("SIP Exchange user name contains invalid characters"));
8354 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
8355 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
8356 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
8358 sip
->account
= account
;
8359 sip
->reregister_set
= FALSE
;
8360 sip
->reauthenticate_set
= FALSE
;
8361 sip
->subscribed
= FALSE
;
8362 sip
->subscribed_buddies
= FALSE
;
8363 sip
->initial_state_published
= FALSE
;
8365 /* username format: <username>,[<optional login>] */
8366 signinname_login
= g_strsplit(username
, ",", 2);
8367 purple_debug_info("sipe", "sipe_login: signinname[0] '%s'\n", signinname_login
[0]);
8369 /* ensure that username format is name@domain */
8370 if (!strchr(signinname_login
[0], '@') || g_str_has_prefix(signinname_login
[0], "@") || g_str_has_suffix(signinname_login
[0], "@")) {
8371 g_strfreev(signinname_login
);
8372 gc
->wants_to_die
= TRUE
;
8373 purple_connection_error(gc
, _("User name should be a valid SIP URI\nExample: user@company.com"));
8376 sip
->username
= g_strdup(signinname_login
[0]);
8378 /* ensure that email format is name@domain if provided */
8379 email
= purple_account_get_string(sip
->account
, "email", NULL
);
8380 if (!is_empty(email
) &&
8381 (!strchr(email
, '@') || g_str_has_prefix(email
, "@") || g_str_has_suffix(email
, "@")))
8383 gc
->wants_to_die
= TRUE
;
8384 purple_connection_error(gc
, _("Email address should be valid if provided\nExample: user@company.com"));
8387 sip
->email
= !is_empty(email
) ? g_strdup(email
) : g_strdup(sip
->username
);
8389 /* login name specified? */
8390 if (signinname_login
[1] && strlen(signinname_login
[1])) {
8391 gchar
**domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
8392 gboolean has_domain
= domain_user
[1] != NULL
;
8393 purple_debug_info("sipe", "sipe_login: signinname[1] '%s'\n", signinname_login
[1]);
8394 sip
->authdomain
= has_domain
? g_strdup(domain_user
[0]) : NULL
;
8395 sip
->authuser
= g_strdup(domain_user
[has_domain
? 1 : 0]);
8396 purple_debug_info("sipe", "sipe_login: auth domain '%s' user '%s'\n",
8397 sip
->authdomain
? sip
->authdomain
: "", sip
->authuser
);
8398 g_strfreev(domain_user
);
8401 userserver
= g_strsplit(signinname_login
[0], "@", 2);
8402 purple_debug_info("sipe", "sipe_login: user '%s' server '%s'\n", userserver
[0], userserver
[1]);
8403 purple_connection_set_display_name(gc
, userserver
[0]);
8404 sip
->sipdomain
= g_strdup(userserver
[1]);
8405 g_strfreev(userserver
);
8406 g_strfreev(signinname_login
);
8408 if (strchr(sip
->username
, ' ') != NULL
) {
8409 gc
->wants_to_die
= TRUE
;
8410 purple_connection_error(gc
, _("SIP Exchange user name contains whitespace"));
8414 sip
->password
= g_strdup(purple_connection_get_password(gc
));
8416 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
8417 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8418 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
8419 sip
->subscriptions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8420 g_free
, (GDestroyNotify
)sipe_subscription_free
);
8422 sip
->filetransfers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,g_free
,NULL
);
8424 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
8426 g_free(sip
->status
);
8427 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
8429 sip
->auto_transport
= FALSE
;
8430 transport
= purple_account_get_string(account
, "transport", "auto");
8431 userserver
= g_strsplit(purple_account_get_string(account
, "server", ""), ":", 2);
8432 if (userserver
[0]) {
8433 /* Use user specified server[:port] */
8437 port
= atoi(userserver
[1]);
8439 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login: user specified SIP server %s:%d\n",
8440 userserver
[0], port
);
8442 if (sipe_strequal(transport
, "auto")) {
8443 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8444 } else if (sipe_strequal(transport
, "tls")) {
8445 sip
->transport
= SIPE_TRANSPORT_TLS
;
8446 } else if (sipe_strequal(transport
, "tcp")) {
8447 sip
->transport
= SIPE_TRANSPORT_TCP
;
8449 sip
->transport
= SIPE_TRANSPORT_UDP
;
8452 create_connection(sip
, g_strdup(userserver
[0]), port
);
8454 /* Server auto-discovery */
8455 if (sipe_strequal(transport
, "auto")) {
8456 sip
->auto_transport
= TRUE
;
8457 if (current_service
&& current_service
->transport
!= NULL
&& current_service
->service
!= NULL
){
8459 resolve_next_service(sip
, current_service
);
8461 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
8463 } else if (sipe_strequal(transport
, "tls")) {
8464 resolve_next_service(sip
, service_tls
);
8465 } else if (sipe_strequal(transport
, "tcp")) {
8466 resolve_next_service(sip
, service_tcp
);
8468 resolve_next_service(sip
, service_udp
);
8471 g_strfreev(userserver
);
8474 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
8476 connection_free_all(sip
);
8481 if (sip
->query_data
!= NULL
)
8482 purple_dnsquery_destroy(sip
->query_data
);
8483 sip
->query_data
= NULL
;
8485 if (sip
->srv_query_data
!= NULL
)
8486 purple_srv_cancel(sip
->srv_query_data
);
8487 sip
->srv_query_data
= NULL
;
8489 if (sip
->listen_data
!= NULL
)
8490 purple_network_listen_cancel(sip
->listen_data
);
8491 sip
->listen_data
= NULL
;
8493 if (sip
->gsc
!= NULL
)
8494 purple_ssl_close(sip
->gsc
);
8497 sipe_auth_free(&sip
->registrar
);
8498 sipe_auth_free(&sip
->proxy
);
8501 purple_circ_buffer_destroy(sip
->txbuf
);
8504 g_free(sip
->realhostname
);
8505 sip
->realhostname
= NULL
;
8507 g_free(sip
->server_version
);
8508 sip
->server_version
= NULL
;
8511 purple_input_remove(sip
->listenpa
);
8513 if (sip
->tx_handler
)
8514 purple_input_remove(sip
->tx_handler
);
8515 sip
->tx_handler
= 0;
8516 if (sip
->resendtimeout
)
8517 purple_timeout_remove(sip
->resendtimeout
);
8518 sip
->resendtimeout
= 0;
8519 if (sip
->timeouts
) {
8520 GSList
*entry
= sip
->timeouts
;
8522 struct scheduled_action
*sched_action
= entry
->data
;
8523 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
8524 purple_timeout_remove(sched_action
->timeout_handler
);
8525 if (sched_action
->destroy
) {
8526 (*sched_action
->destroy
)(sched_action
->payload
);
8528 g_free(sched_action
->name
);
8529 g_free(sched_action
);
8530 entry
= entry
->next
;
8533 g_slist_free(sip
->timeouts
);
8535 if (sip
->allow_events
) {
8536 GSList
*entry
= sip
->allow_events
;
8538 g_free(entry
->data
);
8539 entry
= entry
->next
;
8542 g_slist_free(sip
->allow_events
);
8544 if (sip
->containers
) {
8545 GSList
*entry
= sip
->containers
;
8547 free_container((struct sipe_container
*)entry
->data
);
8548 entry
= entry
->next
;
8551 g_slist_free(sip
->containers
);
8554 g_free(sip
->contact
);
8555 sip
->contact
= NULL
;
8557 g_free(sip
->regcallid
);
8558 sip
->regcallid
= NULL
;
8560 if (sip
->serveraddr
)
8561 g_free(sip
->serveraddr
);
8562 sip
->serveraddr
= NULL
;
8564 if (sip
->focus_factory_uri
)
8565 g_free(sip
->focus_factory_uri
);
8566 sip
->focus_factory_uri
= NULL
;
8569 sip
->processing_input
= FALSE
;
8572 sipe_ews_free(sip
->ews
);
8578 * A callback for g_hash_table_foreach_remove
8580 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
8581 SIPE_UNUSED_PARAMETER gpointer user_data
)
8583 sipe_free_buddy((struct sipe_buddy
*) buddy
);
8585 /* We must return TRUE as the key/value have already been deleted */
8589 static void sipe_close(PurpleConnection
*gc
)
8591 struct sipe_account_data
*sip
= gc
->proto_data
;
8594 /* leave all conversations */
8595 sipe_session_close_all(sip
);
8596 sipe_session_remove_all(sip
);
8599 sip_csta_close(sip
);
8602 if (PURPLE_CONNECTION_IS_CONNECTED(sip
->gc
)) {
8603 /* unsubscribe all */
8604 g_hash_table_foreach(sip
->subscriptions
, sipe_unsubscribe_cb
, sip
);
8607 do_register_exp(sip
, 0);
8610 sipe_connection_cleanup(sip
);
8611 g_free(sip
->sipdomain
);
8612 g_free(sip
->username
);
8614 g_free(sip
->password
);
8615 g_free(sip
->authdomain
);
8616 g_free(sip
->authuser
);
8617 g_free(sip
->status
);
8620 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
8621 g_hash_table_destroy(sip
->buddies
);
8622 g_hash_table_destroy(sip
->our_publications
);
8623 g_hash_table_destroy(sip
->user_state_publications
);
8624 g_hash_table_destroy(sip
->subscriptions
);
8625 g_hash_table_destroy(sip
->filetransfers
);
8628 GSList
*entry
= sip
->groups
;
8630 struct sipe_group
*group
= entry
->data
;
8631 g_free(group
->name
);
8633 entry
= entry
->next
;
8636 g_slist_free(sip
->groups
);
8638 if (sip
->our_publication_keys
) {
8639 GSList
*entry
= sip
->our_publication_keys
;
8641 g_free(entry
->data
);
8642 entry
= entry
->next
;
8645 g_slist_free(sip
->our_publication_keys
);
8647 while (sip
->transactions
)
8648 transactions_remove(sip
, sip
->transactions
->data
);
8650 g_free(gc
->proto_data
);
8651 gc
->proto_data
= NULL
;
8654 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
8655 SIPE_UNUSED_PARAMETER
void *user_data
)
8657 PurpleAccount
*acct
= purple_connection_get_account(gc
);
8658 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
8659 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
8661 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
8662 purple_conversation_present(conv
);
8666 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
8667 SIPE_UNUSED_PARAMETER
void *user_data
)
8670 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
8671 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
8674 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
8675 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
8677 PurpleNotifySearchResults
*results
;
8678 PurpleNotifySearchColumn
*column
;
8679 xmlnode
*searchResults
;
8681 int match_count
= 0;
8682 gboolean more
= FALSE
;
8685 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
8687 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
8688 if (!searchResults
) {
8689 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
8693 results
= purple_notify_searchresults_new();
8695 if (results
== NULL
) {
8696 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
8697 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
8699 xmlnode_free(searchResults
);
8703 column
= purple_notify_searchresults_column_new(_("User name"));
8704 purple_notify_searchresults_column_add(results
, column
);
8706 column
= purple_notify_searchresults_column_new(_("Name"));
8707 purple_notify_searchresults_column_add(results
, column
);
8709 column
= purple_notify_searchresults_column_new(_("Company"));
8710 purple_notify_searchresults_column_add(results
, column
);
8712 column
= purple_notify_searchresults_column_new(_("Country"));
8713 purple_notify_searchresults_column_add(results
, column
);
8715 column
= purple_notify_searchresults_column_new(_("Email"));
8716 purple_notify_searchresults_column_add(results
, column
);
8718 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
8721 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
8722 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
8723 g_strfreev(uri_parts
);
8725 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
8726 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
8727 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
8728 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
8730 purple_notify_searchresults_row_add(results
, row
);
8734 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
8735 char *data
= xmlnode_get_data_unescaped(mrow
);
8736 more
= (g_strcasecmp(data
, "true") == 0);
8740 secondary
= g_strdup_printf(
8741 dngettext(GETTEXT_PACKAGE
,
8742 "Found %d contact%s:",
8743 "Found %d contacts%s:", match_count
),
8744 match_count
, more
? _(" (more matched your query)") : "");
8746 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
8747 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
8748 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
8751 xmlnode_free(searchResults
);
8755 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
8757 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
8758 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
8764 PurpleRequestField
*field
= entries
->data
;
8765 const char *id
= purple_request_field_get_id(field
);
8766 const char *value
= purple_request_field_string_get_value(field
);
8768 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
8770 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
8771 } while ((entries
= g_list_next(entries
)) != NULL
);
8775 struct sipe_account_data
*sip
= gc
->proto_data
;
8776 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
8777 gchar
*query
= g_strjoinv(NULL
, attrs
);
8778 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
8779 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
8780 send_soap_request_with_cb(sip
, domain_uri
, body
,
8781 (TransCallback
) process_search_contact_response
, NULL
);
8790 static void sipe_show_find_contact(PurplePluginAction
*action
)
8792 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8793 PurpleRequestFields
*fields
;
8794 PurpleRequestFieldGroup
*group
;
8795 PurpleRequestField
*field
;
8797 fields
= purple_request_fields_new();
8798 group
= purple_request_field_group_new(NULL
);
8799 purple_request_fields_add_group(fields
, group
);
8801 field
= purple_request_field_string_new("givenName", _("First name"), NULL
, FALSE
);
8802 purple_request_field_group_add_field(group
, field
);
8803 field
= purple_request_field_string_new("sn", _("Last name"), NULL
, FALSE
);
8804 purple_request_field_group_add_field(group
, field
);
8805 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
8806 purple_request_field_group_add_field(group
, field
);
8807 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
8808 purple_request_field_group_add_field(group
, field
);
8810 purple_request_fields(gc
,
8812 _("Search for a contact"),
8813 _("Enter the information for the person you wish to find. Empty fields will be ignored."),
8815 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
8817 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
8820 static void sipe_show_about_plugin(PurplePluginAction
*action
)
8822 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8823 char *tmp
= g_strdup_printf(
8825 * Non-translatable parts, like markup, are hard-coded
8826 * into the format string. This requires more translatable
8827 * texts but it makes the translations less error prone.
8829 "<b><font size=\"+1\">SIPE " SIPE_VERSION
" </font></b><br/>"
8832 "<li> - MS Office Communications Server 2007 R2</li><br/>"
8833 "<li> - MS Office Communications Server 2007</li><br/>"
8834 "<li> - MS Live Communications Server 2005</li><br/>"
8835 "<li> - MS Live Communications Server 2003</li><br/>"
8836 "<li> - Reuters Messaging</li><br/>"
8838 /* 2 */ "%s: <a href=\"http://sipe.sourceforge.net\">http://sipe.sourceforge.net</a><br/>"
8839 /* 3,4 */ "%s: <a href=\"http://sourceforge.net/projects/sipe/forums/forum/688534\">%s</a><br/>"
8840 /* 5 */ "%s: <a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a><br/>"
8841 /* 6 */ "%s: GPLv2+<br/>"
8845 " - Reuters Messaging network<br/>"
8846 " - Deutsche Bank<br/>"
8847 " - Merrill Lynch<br/>"
8855 " - Alcatel-Lucent<br/>"
8858 /* 8,9 */ "%s<a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a>%s.<br/>"
8860 /* 10 */ "<b>%s:</b><br/>"
8861 " - Anibal Avelar<br/>"
8862 " - Gabriel Burt<br/>"
8863 " - Stefan Becker<br/>"
8865 " - Jakub Adam<br/>"
8866 " - Tomáš Hrabčík<br/>"
8870 /* The next 11 texts make up the SIPE about note text */
8871 /* About note, part 1/11: introduction */
8872 _("A third-party plugin implementing extended version of SIP/SIMPLE used by various products"),
8873 /* About note, part 2/11: home page URL (label) */
8875 /* About note, part 3/11: support forum URL (label) */
8877 /* About note, part 4/11: support forum name (hyperlink text) */
8879 /* About note, part 5/11: translation service URL (label) */
8881 /* About note, part 6/11: license type (label) */
8883 /* About note, part 7/11: known users */
8884 _("We support users in such organizations as"),
8885 /* About note, part 8/11: translation request, text before Transifex.net URL */
8886 /* append a space if text is not empty */
8887 _("Please help us to translate SIPE to your native language here at "),
8888 /* About note, part 9/11: translation request, text after Transifex.net URL */
8889 /* start with a space if text is not empty */
8890 _(" using convenient web interface"),
8891 /* About note, part 10/11: author list (header) */
8893 /* About note, part 11/11: Localization credit */
8894 /* PLEASE NOTE: do *NOT* simply translate the english original */
8895 /* but write something similar to the following sentence: */
8896 /* "Localization for <language name> (<language code>): <name>" */
8897 _("Original texts in English (en): SIPE developers")
8899 purple_notify_formatted(gc
, NULL
, " ", NULL
, tmp
, NULL
, NULL
);
8903 static void sipe_republish_calendar(PurplePluginAction
*action
)
8905 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8906 struct sipe_account_data
*sip
= gc
->proto_data
;
8908 sipe_update_calendar(sip
);
8911 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
8915 struct sipe_publication
*publication
= value
;
8917 g_string_append_printf( str
,
8918 SIPE_PUB_XML_PUBLICATION_CLEAR
,
8919 publication
->category
,
8920 publication
->instance
,
8921 publication
->container
,
8922 publication
->version
,
8926 static void sipe_reset_status(PurplePluginAction
*action
)
8928 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8929 struct sipe_account_data
*sip
= gc
->proto_data
;
8931 if (sip
->ocs2007
) /* 2007+ */
8933 GString
* str
= g_string_new(NULL
);
8934 gchar
*publications
;
8936 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
8937 purple_debug_info("sipe", "sipe_reset_status: no userState publications, exiting.\n");
8941 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
8942 publications
= g_string_free(str
, FALSE
);
8944 send_presence_publish(sip
, publications
);
8945 g_free(publications
);
8949 send_presence_soap0(sip
, FALSE
, TRUE
);
8953 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
8956 PurpleConnection
*gc
= (PurpleConnection
*)context
;
8957 struct sipe_account_data
*sip
= gc
->proto_data
;
8959 PurplePluginAction
*act
;
8960 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
8962 act
= purple_plugin_action_new(_("About SIPE plugin..."), sipe_show_about_plugin
);
8963 menu
= g_list_prepend(menu
, act
);
8965 act
= purple_plugin_action_new(_("Contact search..."), sipe_show_find_contact
);
8966 menu
= g_list_prepend(menu
, act
);
8968 if (sipe_strequal(calendar
, "EXCH")) {
8969 act
= purple_plugin_action_new(_("Republish Calendar"), sipe_republish_calendar
);
8970 menu
= g_list_prepend(menu
, act
);
8973 act
= purple_plugin_action_new(_("Reset status"), sipe_reset_status
);
8974 menu
= g_list_prepend(menu
, act
);
8976 menu
= g_list_reverse(menu
);
8981 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
8985 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
8991 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
8997 static char *sipe_status_text(PurpleBuddy
*buddy
)
8999 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9000 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9001 const char *status_id
= purple_status_get_id(status
);
9002 struct sipe_account_data
*sip
= (struct sipe_account_data
*)buddy
->account
->gc
->proto_data
;
9003 struct sipe_buddy
*sbuddy
;
9006 if (!sip
) return NULL
; /* happens on pidgin exit */
9008 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9010 const char *activity_str
= sbuddy
->activity
?
9012 sipe_strequal(status_id
, SIPE_STATUS_ID_BUSY
) || sipe_strequal(status_id
, SIPE_STATUS_ID_BRB
) ?
9013 purple_status_get_name(status
) : NULL
;
9015 if (activity_str
&& sbuddy
->note
)
9017 text
= g_strdup_printf("%s - <i>%s</i>", activity_str
, sbuddy
->note
);
9019 else if (activity_str
)
9021 text
= g_strdup(activity_str
);
9023 else if (sbuddy
->note
)
9025 text
= g_strdup_printf("<i>%s</i>", sbuddy
->note
);
9032 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
9034 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9035 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9036 struct sipe_account_data
*sip
;
9037 struct sipe_buddy
*sbuddy
;
9039 gboolean is_oof_note
= FALSE
;
9040 char *activity
= NULL
;
9041 char *calendar
= NULL
;
9042 char *meeting_subject
= NULL
;
9043 char *meeting_location
= NULL
;
9045 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
9046 if (sip
) //happens on pidgin exit
9048 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9051 note
= sbuddy
->note
;
9052 is_oof_note
= sbuddy
->is_oof_note
;
9053 activity
= sbuddy
->activity
;
9054 calendar
= sipe_cal_get_description(sbuddy
);
9055 meeting_subject
= sbuddy
->meeting_subject
;
9056 meeting_location
= sbuddy
->meeting_location
;
9061 if (purple_presence_is_online(presence
))
9063 const char *status_str
= activity
? activity
: purple_status_get_name(status
);
9065 purple_notify_user_info_add_pair(user_info
, _("Status"), status_str
);
9067 if (purple_presence_is_online(presence
) &&
9068 !is_empty(calendar
))
9070 purple_notify_user_info_add_pair(user_info
, _("Calendar"), calendar
);
9073 if (!is_empty(meeting_location
))
9075 purple_notify_user_info_add_pair(user_info
, _("Meeting in"), meeting_location
);
9077 if (!is_empty(meeting_subject
))
9079 purple_notify_user_info_add_pair(user_info
, _("Meeting about"), meeting_subject
);
9084 char *tmp
= g_strdup_printf("<i>%s</i>", note
);
9085 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, note
);
9087 purple_notify_user_info_add_pair(user_info
, is_oof_note
? _("Out of office note") : _("Note"), tmp
);
9093 #if PURPLE_VERSION_CHECK(2,5,0)
9095 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
9098 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
9099 g_hash_table_insert(table
, "login_label", (gpointer
)_("user@company.com"));
9104 static PurpleBuddy
*
9105 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
9108 const gchar
*server_alias
, *email
;
9109 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
9111 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
9113 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
9115 server_alias
= purple_buddy_get_server_alias(buddy
);
9117 purple_blist_server_alias_buddy(clone
, server_alias
);
9120 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9122 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
9125 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
9127 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
9132 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
9134 PurpleBuddy
*buddy
, *b
;
9135 PurpleConnection
*gc
;
9136 PurpleGroup
* group
= purple_find_group(group_name
);
9138 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
9140 buddy
= (PurpleBuddy
*)node
;
9142 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
9143 gc
= purple_account_get_connection(buddy
->account
);
9145 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
9147 purple_blist_add_buddy_clone(group
, buddy
);
9150 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
9154 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
9156 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9158 purple_debug_info("sipe", "sipe_buddy_menu_chat_new_cb: buddy->name=%s\n", buddy
->name
);
9160 /* 2007+ conference */
9163 sipe_conf_add(sip
, buddy
->name
);
9165 else /* 2005- multiparty chat */
9167 gchar
*self
= sip_uri_self(sip
);
9168 struct sip_session
*session
;
9170 session
= sipe_session_add_chat(sip
);
9171 session
->chat_title
= sipe_chat_get_name(session
->callid
);
9172 session
->roster_manager
= g_strdup(self
);
9174 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, session
->chat_title
);
9175 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
9176 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
9177 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
9184 sipe_is_election_finished(struct sip_session
*session
)
9186 gboolean res
= TRUE
;
9188 SIPE_DIALOG_FOREACH
{
9189 if (dialog
->election_vote
== 0) {
9193 } SIPE_DIALOG_FOREACH_END
;
9196 session
->is_voting_in_progress
= FALSE
;
9202 sipe_election_start(struct sipe_account_data
*sip
,
9203 struct sip_session
*session
)
9205 int election_timeout
;
9207 if (session
->is_voting_in_progress
) {
9208 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
9211 session
->is_voting_in_progress
= TRUE
;
9213 session
->bid
= rand();
9215 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
9217 SIPE_DIALOG_FOREACH
{
9218 /* reset election_vote for each chat participant */
9219 dialog
->election_vote
= 0;
9221 /* send RequestRM to each chat participant*/
9222 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
9223 } SIPE_DIALOG_FOREACH_END
;
9225 election_timeout
= 15; /* sec */
9226 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
9230 * @param who a URI to whom to invite to chat
9233 sipe_invite_to_chat(struct sipe_account_data
*sip
,
9234 struct sip_session
*session
,
9238 if (session
->focus_uri
)
9240 sipe_invite_conf(sip
, session
, who
);
9242 else /* a multi-party chat */
9244 gchar
*self
= sip_uri_self(sip
);
9245 if (session
->roster_manager
) {
9246 if (sipe_strequal(session
->roster_manager
, self
)) {
9247 sipe_invite(sip
, session
, who
, NULL
, NULL
, NULL
, FALSE
);
9249 sipe_refer(sip
, session
, who
);
9252 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite: no RM available\n");
9254 session
->pending_invite_queue
= slist_insert_unique_sorted(
9255 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
9257 sipe_election_start(sip
, session
);
9264 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
9265 struct sip_session
*session
)
9268 GSList
*entry
= session
->pending_invite_queue
;
9271 invitee
= entry
->data
;
9272 sipe_invite_to_chat(sip
, session
, invitee
);
9273 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
9279 sipe_election_result(struct sipe_account_data
*sip
,
9282 struct sip_session
*session
= (struct sip_session
*)sess
;
9284 gboolean has_won
= TRUE
;
9286 if (session
->roster_manager
) {
9287 purple_debug_info("sipe",
9288 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
9292 session
->is_voting_in_progress
= FALSE
;
9294 SIPE_DIALOG_FOREACH
{
9295 if (dialog
->election_vote
< 0) {
9297 rival
= dialog
->with
;
9300 } SIPE_DIALOG_FOREACH_END
;
9303 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
9305 session
->roster_manager
= sip_uri_self(sip
);
9307 SIPE_DIALOG_FOREACH
{
9308 /* send SetRM to each chat participant*/
9309 sipe_send_election_set_rm(sip
, dialog
);
9310 } SIPE_DIALOG_FOREACH_END
;
9312 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
9316 sipe_process_pending_invite_queue(sip
, session
);
9320 * For 2007+ conference only.
9323 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9325 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9326 struct sip_session
*session
;
9328 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
9329 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_title=%s\n", chat_title
);
9331 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9333 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
9337 * For 2007+ conference only.
9340 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9342 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9343 struct sip_session
*session
;
9345 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: buddy->name=%s\n", buddy
->name
);
9346 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: chat_title=%s\n", chat_title
);
9348 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9350 sipe_conf_delete_user(sip
, session
, buddy
->name
);
9354 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, char *chat_title
)
9356 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9357 struct sip_session
*session
;
9359 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: buddy->name=%s\n", buddy
->name
);
9360 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: chat_title=%s\n", chat_title
);
9362 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9364 sipe_invite_to_chat(sip
, session
, buddy
->name
);
9368 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
9370 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9372 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: buddy->name=%s\n", buddy
->name
);
9374 char *tel_uri
= sip_to_tel_uri(phone
);
9376 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: going to call number: %s\n", tel_uri
? tel_uri
: "");
9377 sip_csta_make_call(sip
, tel_uri
);
9384 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
9387 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
9389 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9392 char *mailto
= g_strdup_printf("mailto:%s", email
);
9393 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
9397 char *const parmList
[] = {"xdg-email", mailto
, NULL
};
9398 if ((pid
= fork()) == -1)
9400 purple_debug_info("sipe", "fork() error\n");
9404 execvp(parmList
[0], parmList
);
9405 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
9413 //@TODO resolve env variable %WINDIR% first
9414 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
9417 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
9426 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
9431 * A menu which appear when right-clicking on buddy in contact list.
9434 sipe_buddy_menu(PurpleBuddy
*buddy
)
9436 PurpleBlistNode
*g_node
;
9437 PurpleGroup
*group
, *gr_parent
;
9438 PurpleMenuAction
*act
;
9440 GList
*menu_groups
= NULL
;
9441 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9444 const char *phone_disp_str
;
9445 gchar
*self
= sip_uri_self(sip
);
9447 SIPE_SESSION_FOREACH
{
9448 if (g_ascii_strcasecmp(self
, buddy
->name
) && session
->chat_title
&& session
->conv
)
9450 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
9452 PurpleConvChatBuddyFlags flags
;
9453 PurpleConvChatBuddyFlags flags_us
;
9455 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
9456 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9457 if (session
->focus_uri
9458 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
9459 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9461 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"), session
->chat_title
);
9462 act
= purple_menu_action_new(label
,
9463 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
9464 session
->chat_title
, NULL
);
9466 menu
= g_list_prepend(menu
, act
);
9469 if (session
->focus_uri
9470 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9472 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_title
);
9473 act
= purple_menu_action_new(label
,
9474 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
9475 session
->chat_title
, NULL
);
9477 menu
= g_list_prepend(menu
, act
);
9482 if (!session
->focus_uri
9483 || (session
->focus_uri
&& !session
->locked
))
9485 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_title
);
9486 act
= purple_menu_action_new(label
,
9487 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
9488 session
->chat_title
, NULL
);
9490 menu
= g_list_prepend(menu
, act
);
9494 } SIPE_SESSION_FOREACH_END
;
9496 act
= purple_menu_action_new(_("New chat"),
9497 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
9499 menu
= g_list_prepend(menu
, act
);
9501 if (sip
->csta
&& !sip
->csta
->line_status
) {
9504 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
9505 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
9507 gchar
*label
= g_strdup_printf(_("Work %s"),
9508 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9509 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9513 menu
= g_list_prepend(menu
, act
);
9517 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
9518 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
9520 gchar
*label
= g_strdup_printf(_("Mobile %s"),
9521 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9522 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9526 menu
= g_list_prepend(menu
, act
);
9530 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
9531 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
9533 gchar
*label
= g_strdup_printf(_("Home %s"),
9534 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9535 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9539 menu
= g_list_prepend(menu
, act
);
9543 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
9544 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
9546 gchar
*label
= g_strdup_printf(_("Other %s"),
9547 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9548 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9552 menu
= g_list_prepend(menu
, act
);
9556 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
9557 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
9559 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
9560 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9561 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9565 menu
= g_list_prepend(menu
, act
);
9569 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9571 act
= purple_menu_action_new(_("Send email..."),
9572 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
9574 menu
= g_list_prepend(menu
, act
);
9577 gr_parent
= purple_buddy_get_group(buddy
);
9578 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
9579 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
9582 group
= (PurpleGroup
*)g_node
;
9583 if (group
== gr_parent
)
9586 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
9589 act
= purple_menu_action_new(purple_group_get_name(group
),
9590 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
9592 menu_groups
= g_list_prepend(menu_groups
, act
);
9594 menu_groups
= g_list_reverse(menu_groups
);
9596 act
= purple_menu_action_new(_("Copy to"),
9599 menu
= g_list_prepend(menu
, act
);
9600 menu
= g_list_reverse(menu
);
9607 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
9609 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9610 struct sip_session
*session
;
9612 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9613 sipe_conf_modify_conference_lock(sip
, session
, locked
);
9617 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
9619 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
9620 sipe_conf_modify_lock(chat
, FALSE
);
9624 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
9626 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
9627 sipe_conf_modify_lock(chat
, TRUE
);
9631 sipe_chat_menu(PurpleChat
*chat
)
9633 PurpleMenuAction
*act
;
9634 PurpleConvChatBuddyFlags flags_us
;
9636 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9637 struct sip_session
*session
;
9640 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9641 if (!session
) return NULL
;
9643 self
= sip_uri_self(sip
);
9644 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9646 if (session
->focus_uri
9647 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9649 if (session
->locked
) {
9650 act
= purple_menu_action_new(_("Unlock"),
9651 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
9653 menu
= g_list_prepend(menu
, act
);
9655 act
= purple_menu_action_new(_("Lock"),
9656 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
9658 menu
= g_list_prepend(menu
, act
);
9662 menu
= g_list_reverse(menu
);
9669 sipe_blist_node_menu(PurpleBlistNode
*node
)
9671 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
9672 return sipe_buddy_menu((PurpleBuddy
*) node
);
9673 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
9674 return sipe_chat_menu((PurpleChat
*)node
);
9681 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
9683 char *uri
= trans
->payload
->data
;
9685 PurpleNotifyUserInfo
*info
;
9686 PurpleBuddy
*pbuddy
= NULL
;
9687 struct sipe_buddy
*sbuddy
;
9688 const char *alias
= NULL
;
9689 char *device_name
= NULL
;
9690 char *server_alias
= NULL
;
9691 char *phone_number
= NULL
;
9694 char *first_name
= NULL
;
9695 char *last_name
= NULL
;
9697 if (!sip
) return FALSE
;
9699 purple_debug_info("sipe", "Fetching %s's user info for %s\n", uri
, sip
->username
);
9701 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
9702 alias
= purple_buddy_get_local_alias(pbuddy
);
9704 //will query buddy UA's capabilities and send answer to log
9705 sipe_options_request(sip
, uri
);
9707 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
9709 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
9712 info
= purple_notify_user_info_new();
9714 if (msg
->response
!= 200) {
9715 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
9717 xmlnode
*searchResults
;
9720 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
9721 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
9722 if (!searchResults
) {
9723 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
9724 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
9726 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
9727 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
9728 phone_number
= g_strdup(xmlnode_get_attrib(mrow
, "phone"));
9730 /* For 2007 system we will take this from ContactCard -
9731 * it has cleaner tel: URIs at least
9733 if (!sip
->ocs2007
) {
9734 char *tel_uri
= sip_to_tel_uri(phone_number
);
9735 /* trims its parameters, so call first */
9736 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, server_alias
);
9737 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
9738 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
9739 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, phone_number
);
9743 if (server_alias
&& strlen(server_alias
) > 0) {
9744 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9746 if ((value
= xmlnode_get_attrib(mrow
, "title")) && strlen(value
) > 0) {
9747 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
9749 if ((value
= xmlnode_get_attrib(mrow
, "office")) && strlen(value
) > 0) {
9750 purple_notify_user_info_add_pair(info
, _("Office"), value
);
9752 if (phone_number
&& strlen(phone_number
) > 0) {
9753 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
9755 if ((value
= xmlnode_get_attrib(mrow
, "company")) && strlen(value
) > 0) {
9756 purple_notify_user_info_add_pair(info
, _("Company"), value
);
9758 if ((value
= xmlnode_get_attrib(mrow
, "city")) && strlen(value
) > 0) {
9759 purple_notify_user_info_add_pair(info
, _("City"), value
);
9761 if ((value
= xmlnode_get_attrib(mrow
, "state")) && strlen(value
) > 0) {
9762 purple_notify_user_info_add_pair(info
, _("State"), value
);
9764 if ((value
= xmlnode_get_attrib(mrow
, "country")) && strlen(value
) > 0) {
9765 purple_notify_user_info_add_pair(info
, _("Country"), value
);
9767 if (email
&& strlen(email
) > 0) {
9768 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9772 xmlnode_free(searchResults
);
9775 purple_notify_user_info_add_section_break(info
);
9777 if (is_empty(server_alias
)) {
9778 g_free(server_alias
);
9779 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
9781 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9785 /* present alias if it differs from server alias */
9786 if (alias
&& !sipe_strequal(alias
, server_alias
))
9788 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
9791 if (is_empty(email
)) {
9793 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
9795 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9799 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
9801 purple_notify_user_info_add_pair(info
, _("Site"), site
);
9804 sipe_get_first_last_names(sip
, uri
, &first_name
, &last_name
);
9805 if (first_name
&& last_name
) {
9806 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
9808 purple_notify_user_info_add_pair(info
, _("Find on LinkedIn"), link
);
9815 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
9818 /* show a buddy's user info in a nice dialog box */
9819 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
9820 uri
, /* buddy's URI */
9822 NULL
, /* callback called when dialog closed */
9823 NULL
); /* userdata for callback */
9825 g_free(phone_number
);
9826 g_free(server_alias
);
9828 g_free(device_name
);
9834 * AD search first, LDAP based
9836 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
9838 struct sipe_account_data
*sip
= gc
->proto_data
;
9839 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
9840 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
9841 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
9842 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
9844 payload
->destroy
= g_free
;
9845 payload
->data
= g_strdup(username
);
9847 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
9848 send_soap_request_with_cb(sip
, domain_uri
, body
,
9849 (TransCallback
) process_get_info_response
, payload
);
9855 static PurplePlugin
*my_protocol
= NULL
;
9857 static PurplePluginProtocolInfo prpl_info
=
9859 OPT_PROTO_CHAT_TOPIC
,
9860 NULL
, /* user_splits */
9861 NULL
, /* protocol_options */
9862 NO_BUDDY_ICONS
, /* icon_spec */
9863 sipe_list_icon
, /* list_icon */
9864 NULL
, /* list_emblems */
9865 sipe_status_text
, /* status_text */
9866 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
9867 sipe_status_types
, /* away_states */
9868 sipe_blist_node_menu
, /* blist_node_menu */
9869 NULL
, /* chat_info */
9870 NULL
, /* chat_info_defaults */
9871 sipe_login
, /* login */
9872 sipe_close
, /* close */
9873 sipe_im_send
, /* send_im */
9874 NULL
, /* set_info */ // TODO maybe
9875 sipe_send_typing
, /* send_typing */
9876 sipe_get_info
, /* get_info */
9877 sipe_set_status
, /* set_status */
9878 sipe_set_idle
, /* set_idle */
9879 NULL
, /* change_passwd */
9880 sipe_add_buddy
, /* add_buddy */
9881 NULL
, /* add_buddies */
9882 sipe_remove_buddy
, /* remove_buddy */
9883 NULL
, /* remove_buddies */
9884 sipe_add_permit
, /* add_permit */
9885 sipe_add_deny
, /* add_deny */
9886 sipe_add_deny
, /* rem_permit */
9887 sipe_add_permit
, /* rem_deny */
9888 dummy_permit_deny
, /* set_permit_deny */
9889 NULL
, /* join_chat */
9890 NULL
, /* reject_chat */
9891 NULL
, /* get_chat_name */
9892 sipe_chat_invite
, /* chat_invite */
9893 sipe_chat_leave
, /* chat_leave */
9894 NULL
, /* chat_whisper */
9895 sipe_chat_send
, /* chat_send */
9896 sipe_keep_alive
, /* keepalive */
9897 NULL
, /* register_user */
9898 NULL
, /* get_cb_info */ // deprecated
9899 NULL
, /* get_cb_away */ // deprecated
9900 sipe_alias_buddy
, /* alias_buddy */
9901 sipe_group_buddy
, /* group_buddy */
9902 sipe_rename_group
, /* rename_group */
9903 NULL
, /* buddy_free */
9904 sipe_convo_closed
, /* convo_closed */
9905 purple_normalize_nocase
, /* normalize */
9906 NULL
, /* set_buddy_icon */
9907 sipe_remove_group
, /* remove_group */
9908 NULL
, /* get_cb_real_name */ // TODO?
9909 NULL
, /* set_chat_topic */
9910 NULL
, /* find_blist_chat */
9911 NULL
, /* roomlist_get_list */
9912 NULL
, /* roomlist_cancel */
9913 NULL
, /* roomlist_expand_category */
9914 NULL
, /* can_receive_file */
9915 sipe_ft_send_file
, /* send_file */
9916 sipe_ft_new_xfer
, /* new_xfer */
9917 NULL
, /* offline_message */
9918 NULL
, /* whiteboard_prpl_ops */
9919 sipe_send_raw
, /* send_raw */
9920 NULL
, /* roomlist_room_serialize */
9921 NULL
, /* unregister_user */
9922 NULL
, /* send_attention */
9923 NULL
, /* get_attention_types */
9924 #if !PURPLE_VERSION_CHECK(2,5,0)
9925 /* Backward compatibility when compiling against 2.4.x API */
9926 (void (*)(void)) /* _purple_reserved4 */
9928 sizeof(PurplePluginProtocolInfo
), /* struct_size */
9929 #if PURPLE_VERSION_CHECK(2,5,0)
9930 sipe_get_account_text_table
, /* get_account_text_table */
9931 #if PURPLE_VERSION_CHECK(2,6,0)
9932 NULL
, /* initiate_media */
9933 NULL
, /* get_media_caps */
9939 static PurplePluginInfo info
= {
9940 PURPLE_PLUGIN_MAGIC
,
9941 PURPLE_MAJOR_VERSION
,
9942 PURPLE_MINOR_VERSION
,
9943 PURPLE_PLUGIN_PROTOCOL
, /**< type */
9944 NULL
, /**< ui_requirement */
9946 NULL
, /**< dependencies */
9947 PURPLE_PRIORITY_DEFAULT
, /**< priority */
9948 "prpl-sipe", /**< id */
9949 "Office Communicator", /**< name */
9950 SIPE_VERSION
, /**< version */
9951 "Microsoft Office Communicator Protocol Plugin", /**< summary */
9952 "A plugin for the extended SIP/SIMPLE protocol used by " /**< description */
9953 "Microsoft Live/Office Communications Server (LCS2005/OCS2007+)", /**< description */
9954 "Anibal Avelar <avelar@gmail.com>, " /**< author */
9955 "Gabriel Burt <gburt@novell.com>, " /**< author */
9956 "Stefan Becker <stefan.becker@nokia.com>, " /**< author */
9957 "pier11 <pier11@operamail.com>", /**< author */
9958 "http://sipe.sourceforge.net/", /**< homepage */
9959 sipe_plugin_load
, /**< load */
9960 sipe_plugin_unload
, /**< unload */
9961 sipe_plugin_destroy
, /**< destroy */
9962 NULL
, /**< ui_info */
9963 &prpl_info
, /**< extra_info */
9972 static void sipe_plugin_destroy(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9976 entry
= prpl_info
.protocol_options
;
9978 purple_account_option_destroy(entry
->data
);
9979 entry
= g_list_delete_link(entry
, entry
);
9981 prpl_info
.protocol_options
= NULL
;
9983 entry
= prpl_info
.user_splits
;
9985 purple_account_user_split_destroy(entry
->data
);
9986 entry
= g_list_delete_link(entry
, entry
);
9988 prpl_info
.user_splits
= NULL
;
9991 static void init_plugin(PurplePlugin
*plugin
)
9993 PurpleAccountUserSplit
*split
;
9994 PurpleAccountOption
*option
;
9999 purple_debug_info(PACKAGE
, "bindtextdomain = %s\n", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
10000 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s\n",
10001 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
10002 textdomain(GETTEXT_PACKAGE
);
10005 purple_plugin_register(plugin
);
10007 split
= purple_account_user_split_new(_("Login\n user or DOMAIN\\user or\n user@company.com"), NULL
, ',');
10008 purple_account_user_split_set_reverse(split
, FALSE
);
10009 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
10011 option
= purple_account_option_string_new(_("Server[:Port]\n(leave empty for auto-discovery)"), "server", "");
10012 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10014 option
= purple_account_option_list_new(_("Connection type"), "transport", NULL
);
10015 purple_account_option_add_list_item(option
, _("Auto"), "auto");
10016 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
10017 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
10018 purple_account_option_add_list_item(option
, _("UDP"), "udp");
10019 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10021 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
10022 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
10024 option
= purple_account_option_string_new(_("User Agent"), "useragent", "");
10025 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10027 #ifdef USE_KERBEROS
10028 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
10029 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10031 /* Suitable for sspi/NTLM, sspi/Kerberos and krb5 security mechanisms
10032 * No login/password is taken into account if this option present,
10033 * instead used default credentials stored in OS.
10035 option
= purple_account_option_bool_new(_("Use Single Sign-On"), "sso", TRUE
);
10036 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10039 option
= purple_account_option_list_new(_("Calendar source"), "calendar", NULL
);
10040 purple_account_option_add_list_item(option
, _("Exchange 2007/2010"), "EXCH");
10041 purple_account_option_add_list_item(option
, _("None"), "NONE");
10042 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10044 /** Example: https://server.company.com/EWS/Exchange.asmx */
10045 option
= purple_account_option_string_new(_("Email services URL\n(leave empty for auto-discovery)"), "email_url", "");
10046 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10048 option
= purple_account_option_string_new(_("Email address\n(if different from Username)"), "email", "");
10049 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10051 /** Example: DOMAIN\user or user@company.com */
10052 option
= purple_account_option_string_new(_("Email login\n(if different from Login)"), "email_login", "");
10053 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10055 option
= purple_account_option_string_new(_("Email password\n(if different from Password)"), "email_password", "");
10056 purple_account_option_set_masked(option
, TRUE
);
10057 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10059 my_protocol
= plugin
;
10062 PURPLE_INIT_PLUGIN(sipe
, init_plugin
, info
);
10067 c-file-style: "bsd"
10068 indent-tabs-mode: t