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
43 #define _LIBC_INTERNAL_
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <netinet/in.h>
60 #include "accountopt.h"
62 #include "conversation.h"
66 #include "savedstatuses.h"
80 #include "sipe-chat.h"
81 #include "sipe-conf.h"
83 #include "sipe-dialog.h"
85 #include "sipe-session.h"
86 #include "sipe-utils.h"
89 #include "sipe-sign.h"
93 /* Backward compatibility when compiling against 2.4.x API */
94 #if !PURPLE_VERSION_CHECK(2,5,0)
95 #define PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY 0x0100
98 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
100 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
101 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
103 /* Keep in sync with sipe_transport_type! */
104 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
105 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
107 /* Status identifiers (see also: sipe_status_types()) */
108 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
109 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
110 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
111 /* PURPLE_STATUS_UNAVAILABLE: */
112 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
113 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
114 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
115 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
116 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
117 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
118 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
119 /* PURPLE_STATUS_AWAY: */
120 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
121 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
122 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
123 /** Reuters status (user settable) */
124 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
125 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
126 /* ??? PURPLE_STATUS_MOBILE */
127 /* ??? PURPLE_STATUS_TUNE */
129 /* Status attributes (see also sipe_status_types() */
130 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
132 #define SDP_ACCEPT_TYPES "text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml text/x-msmsgsinvite"
134 static struct sipe_activity_map_struct
139 const char *status_id
;
141 } const sipe_activity_map
[] =
143 /* This has nothing to do with Availability numbers, like 3500 (online).
144 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
146 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
147 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
148 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , NULL
},
149 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
150 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("Busy-Idle") , NULL
},
151 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
152 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
153 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
154 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , NULL
},
155 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
156 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , NULL
},
157 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , NULL
},
158 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , NULL
},
159 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
160 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
162 /** @param x is sipe_activity */
163 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
166 /* Action name templates */
167 #define ACTION_NAME_PRESENCE "<presence><%s>"
170 sipe_get_activity_by_token(const char *token
)
174 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
176 if (sipe_strequal(token
, sipe_activity_map
[i
].token
))
177 return sipe_activity_map
[i
].type
;
180 return sipe_activity_map
[0].type
;
184 sipe_get_activity_desc_by_token(const char *token
)
186 if (!token
) return NULL
;
188 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token
));
191 /** Allows to send typed messages from chat window again after account reinstantiation. */
193 sipe_rejoin_chat(PurpleConversation
*conv
)
195 if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
&&
196 PURPLE_CONV_CHAT(conv
)->left
)
198 PURPLE_CONV_CHAT(conv
)->left
= FALSE
;
199 purple_conversation_update(conv
, PURPLE_CONV_UPDATE_CHATLEFT
);
203 static char *genbranch()
205 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
206 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
207 rand() & 0xFFFF, rand() & 0xFFFF);
211 static char *default_ua
= NULL
;
213 sipe_get_useragent(struct sipe_account_data
*sip
)
215 const char *useragent
= purple_account_get_string(sip
->account
, "useragent", "");
216 if (is_empty(useragent
)) {
218 /*@TODO: better approach to define _user_ OS, it's version and host architecture */
220 #if defined(__linux__) || defined(__linux) || defined(__LINUX__)
221 #define SIPE_TARGET_PLATFORM "linux"
222 #elif defined(__NetBSD__) ||defined( __OpenBSD__) || defined(__FreeBSD__)
223 #define SIPE_TARGET_PLATFORM "bsd"
224 #elif defined(__APPLE__) || defined(__MACOS__)
225 #define SIPE_TARGET_PLATFORM "macosx"
226 #elif defined(_AIX) || defined(__AIX__) || defined(__aix__)
227 #define SIPE_TARGET_PLATFORM "aix"
228 #elif defined(__solaris__) || defined(__sun)
229 #define SIPE_TARGET_PLATFORM "sun"
230 #elif defined(_WIN32)
231 #define SIPE_TARGET_PLATFORM "win"
232 #elif defined(__CYGWIN__)
233 #define SIPE_TARGET_PLATFORM "cygwin"
234 #elif defined(__hpux__)
235 #define SIPE_TARGET_PLATFORM "hpux"
236 #elif defined(__sgi__)
237 #define SIPE_TARGET_PLATFORM "irix"
239 #define SIPE_TARGET_PLATFORM "unknown"
242 #if defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)
243 #define SIPE_TARGET_ARCH "x86_64"
244 #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386)
245 #define SIPE_TARGET_ARCH "i386"
246 #elif defined(__ppc64__)
247 #define SIPE_TARGET_ARCH "ppc64"
248 #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR)
249 #define SIPE_TARGET_ARCH "ppc"
250 #elif defined(__hppa__) || defined(__hppa)
251 #define SIPE_TARGET_ARCH "hppa"
252 #elif defined(__mips__) || defined(__mips) || defined(_MIPS_ARCH) || defined(_M_MRX000)
253 #define SIPE_TARGET_ARCH "mips"
254 #elif defined(__s390__) || defined(__s390) || defined(__s390x__) || defined(__s390x)
255 #define SIPE_TARGET_ARCH "s390"
256 #elif defined(__sparc__) || defined(__sparc) || defined(__sparcv8)
257 #define SIPE_TARGET_ARCH "sparc"
258 #elif defined(__arm__)
259 #define SIPE_TARGET_ARCH "arm"
261 #define SIPE_TARGET_ARCH "other"
264 default_ua
= g_strdup_printf("Purple/%s Sipe/" SIPE_VERSION
" (" SIPE_TARGET_PLATFORM
"-" SIPE_TARGET_ARCH
"; %s)",
265 purple_core_get_version(),
266 sip
->server_version
? sip
->server_version
: "");
268 useragent
= default_ua
;
273 static const char *sipe_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount
*a
,
274 SIPE_UNUSED_PARAMETER PurpleBuddy
*b
)
279 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
281 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
);
283 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
284 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
287 static void sipe_close(PurpleConnection
*gc
);
289 static void send_presence_status(struct sipe_account_data
*sip
);
291 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
293 static void sipe_keep_alive(PurpleConnection
*gc
)
295 struct sipe_account_data
*sip
= gc
->proto_data
;
296 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
297 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
298 gchar buf
[2] = {0, 0};
299 purple_debug_info("sipe", "sending keep alive\n");
300 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
302 time_t now
= time(NULL
);
303 if ((sip
->keepalive_timeout
> 0) &&
304 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
) &&
305 ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
307 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
308 sendout_pkt(gc
, "\r\n\r\n");
309 sip
->last_keepalive
= now
;
314 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
316 struct sip_connection
*ret
= NULL
;
317 GSList
*entry
= sip
->openconns
;
320 if (ret
->fd
== fd
) return ret
;
326 static void sipe_auth_free(struct sip_auth
*auth
)
328 g_free(auth
->opaque
);
332 g_free(auth
->target
);
334 auth
->type
= AUTH_TYPE_UNSET
;
337 g_free(auth
->gssapi_data
);
338 auth
->gssapi_data
= NULL
;
339 sip_sec_destroy_context(auth
->gssapi_context
);
340 auth
->gssapi_context
= NULL
;
343 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
345 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
347 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
351 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
353 struct sip_connection
*conn
= connection_find(sip
, fd
);
355 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
356 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
362 static void connection_free_all(struct sipe_account_data
*sip
)
364 struct sip_connection
*ret
= NULL
;
365 GSList
*entry
= sip
->openconns
;
368 connection_remove(sip
, ret
->fd
);
369 entry
= sip
->openconns
;
374 sipe_make_signature(struct sipe_account_data
*sip
,
377 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
380 const char *authuser
= sip
->authuser
;
384 if (!authuser
|| strlen(authuser
) < 1) {
385 authuser
= sip
->username
;
388 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
389 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
392 // If we have a signature for the message, include that
393 if (msg
->signature
) {
394 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
);
397 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
398 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
401 gchar
*sign_str
= NULL
;
403 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
406 purple_account_get_bool(sip
->account
, "sso", TRUE
),
407 sip
->authdomain
? sip
->authdomain
: "",
412 if (!gssapi_data
|| !auth
->gssapi_context
) {
413 sip
->gc
->wants_to_die
= TRUE
;
414 purple_connection_error(sip
->gc
, _("Failed to authenticate to server"));
418 if (auth
->version
> 3) {
419 sipe_make_signature(sip
, msg
);
420 sign_str
= g_strdup_printf(", crand=\"%s\", cnum=\"%s\", response=\"%s\"",
421 msg
->rand
, msg
->num
, msg
->signature
);
423 sign_str
= g_strdup("");
426 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
427 version_str
= auth
->version
> 2 ? g_strdup_printf(", version=%d", auth
->version
) : g_strdup("");
428 ret
= g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"%s%s", auth_protocol
, opaque
, auth
->realm
, auth
->target
, gssapi_data
, version_str
, sign_str
);
436 version_str
= auth
->version
> 2 ? g_strdup_printf(", version=%d", auth
->version
) : g_strdup("");
437 ret
= g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"%s", auth_protocol
, auth
->realm
, auth
->target
, version_str
);
441 } else { /* Digest */
443 /* Calculate new session key */
445 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
446 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
447 authuser
, auth
->realm
, sip
->password
,
448 auth
->gssapi_data
, NULL
);
451 sprintf(noncecount
, "%08d", auth
->nc
++);
452 response
= purple_cipher_http_digest_calculate_response("md5",
453 msg
->method
, msg
->target
, NULL
, NULL
,
454 auth
->gssapi_data
, noncecount
, NULL
,
456 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
458 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
);
464 static char *parse_attribute(const char *attrname
, const char *source
)
466 const char *tmp
, *tmp2
;
468 int len
= strlen(attrname
);
470 if (g_str_has_prefix(source
, attrname
)) {
472 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
474 retval
= g_strndup(tmp
, tmp2
- tmp
);
476 retval
= g_strdup(tmp
);
482 static void fill_auth(const gchar
*hdr
, struct sip_auth
*auth
)
488 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
492 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
493 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
494 auth
->type
= AUTH_TYPE_NTLM
;
497 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
498 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
499 auth
->type
= AUTH_TYPE_KERBEROS
;
503 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
504 auth
->type
= AUTH_TYPE_DIGEST
;
508 parts
= g_strsplit(hdr
, "\", ", 0);
509 for (i
= 0; parts
[i
]; i
++) {
512 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
514 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
515 g_free(auth
->gssapi_data
);
516 auth
->gssapi_data
= tmp
;
518 if (auth
->type
== AUTH_TYPE_NTLM
) {
519 /* NTLM module extracts nonce from gssapi-data */
523 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
524 /* Only used with AUTH_TYPE_DIGEST */
525 g_free(auth
->gssapi_data
);
526 auth
->gssapi_data
= tmp
;
527 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
528 g_free(auth
->opaque
);
530 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
534 if (auth
->type
== AUTH_TYPE_DIGEST
) {
535 /* Throw away old session key */
536 g_free(auth
->opaque
);
540 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
541 g_free(auth
->target
);
543 } else if ((tmp
= parse_attribute("version=", parts
[i
]))) {
544 auth
->version
= atoi(tmp
);
547 // uncomment to revert to previous functionality if version 3+ does not work.
548 // auth->version = 2;
555 static void sipe_canwrite_cb(gpointer data
,
556 SIPE_UNUSED_PARAMETER gint source
,
557 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
559 PurpleConnection
*gc
= data
;
560 struct sipe_account_data
*sip
= gc
->proto_data
;
564 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
566 if (max_write
== 0) {
567 if (sip
->tx_handler
!= 0){
568 purple_input_remove(sip
->tx_handler
);
574 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
576 if (written
< 0 && errno
== EAGAIN
)
578 else if (written
<= 0) {
579 /*TODO: do we really want to disconnect on a failure to write?*/
580 purple_connection_error(gc
, _("Could not write"));
584 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
587 static void sipe_canwrite_cb_ssl(gpointer data
,
588 SIPE_UNUSED_PARAMETER gint src
,
589 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
591 PurpleConnection
*gc
= data
;
592 struct sipe_account_data
*sip
= gc
->proto_data
;
596 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
598 if (max_write
== 0) {
599 if (sip
->tx_handler
!= 0) {
600 purple_input_remove(sip
->tx_handler
);
606 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
608 if (written
< 0 && errno
== EAGAIN
)
610 else if (written
<= 0) {
611 /*TODO: do we really want to disconnect on a failure to write?*/
612 purple_connection_error(gc
, _("Could not write"));
616 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
619 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
621 static void send_later_cb(gpointer data
, gint source
,
622 SIPE_UNUSED_PARAMETER
const gchar
*error
)
624 PurpleConnection
*gc
= data
;
625 struct sipe_account_data
*sip
;
626 struct sip_connection
*conn
;
628 if (!PURPLE_CONNECTION_IS_VALID(gc
))
636 purple_connection_error(gc
, _("Could not connect"));
640 sip
= gc
->proto_data
;
642 sip
->connecting
= FALSE
;
643 sip
->last_keepalive
= time(NULL
);
645 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
647 /* If there is more to write now, we need to register a handler */
648 if (sip
->txbuf
->bufused
> 0)
649 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
651 conn
= connection_create(sip
, source
);
652 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
655 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
657 struct sipe_account_data
*sip
;
659 if (!PURPLE_CONNECTION_IS_VALID(gc
))
661 if (gsc
) purple_ssl_close(gsc
);
665 sip
= gc
->proto_data
;
668 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
669 sip
->connecting
= FALSE
;
670 sip
->last_keepalive
= time(NULL
);
672 connection_create(sip
, gsc
->fd
);
674 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
679 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
680 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
682 PurpleConnection
*gc
= data
;
683 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
684 if (sip
== NULL
) return;
686 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
688 /* If there is more to write now */
689 if (sip
->txbuf
->bufused
> 0) {
690 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
695 static void sendlater(PurpleConnection
*gc
, const char *buf
)
697 struct sipe_account_data
*sip
= gc
->proto_data
;
699 if (!sip
->connecting
) {
700 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
701 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
702 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
704 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
705 purple_connection_error(gc
, _("Could not create socket"));
708 sip
->connecting
= TRUE
;
711 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
712 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
714 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
717 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
719 struct sipe_account_data
*sip
= gc
->proto_data
;
720 time_t currtime
= time(NULL
);
721 int writelen
= strlen(buf
);
724 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sending - %s######\n%s######\n", ctime(&currtime
), tmp
= fix_newlines(buf
));
726 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
727 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
728 purple_debug_info("sipe", "could not send packet\n");
737 if (sip
->tx_handler
) {
742 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
744 ret
= write(sip
->fd
, buf
, writelen
);
748 if (ret
< 0 && errno
== EAGAIN
)
750 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
755 if (ret
< writelen
) {
756 if (!sip
->tx_handler
){
758 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
761 sip
->tx_handler
= purple_input_add(sip
->fd
,
762 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
767 /* XXX: is it OK to do this? You might get part of a request sent
768 with part of another. */
769 if (sip
->txbuf
->bufused
> 0)
770 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
772 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
778 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
780 sendout_pkt(gc
, buf
);
784 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
786 GSList
*tmp
= msg
->headers
;
789 GString
*outstr
= g_string_new("");
790 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
792 name
= ((struct sipnameval
*) (tmp
->data
))->name
;
793 value
= ((struct sipnameval
*) (tmp
->data
))->value
;
794 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
795 tmp
= g_slist_next(tmp
);
797 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
798 sendout_pkt(sip
->gc
, outstr
->str
);
799 g_string_free(outstr
, TRUE
);
803 sipe_make_signature(struct sipe_account_data
*sip
,
806 if (sip
->registrar
.gssapi_context
) {
807 struct sipmsg_breakdown msgbd
;
808 gchar
*signature_input_str
;
810 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
811 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
812 sip
->registrar
.ntlm_num
++;
813 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
814 signature_input_str
= sipmsg_breakdown_get_string(sip
->registrar
.version
, &msgbd
);
815 if (signature_input_str
!= NULL
) {
816 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
817 msg
->signature
= signature_hex
;
818 msg
->rand
= g_strdup(msgbd
.rand
);
819 msg
->num
= g_strdup(msgbd
.num
);
820 g_free(signature_input_str
);
822 sipmsg_breakdown_free(&msgbd
);
826 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
830 if (sip
->registrar
.type
== AUTH_TYPE_UNSET
) {
834 sipe_make_signature(sip
, msg
);
836 if (sip
->registrar
.type
&& sipe_strequal(method
, "REGISTER")) {
837 buf
= auth_header(sip
, &sip
->registrar
, msg
);
839 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
842 } 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")) {
843 sip
->registrar
.nc
= 3;
844 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
846 if (purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
847 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
852 buf
= auth_header(sip
, &sip
->registrar
, msg
);
853 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
856 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
860 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
861 const char *text
, const char *body
)
865 GString
*outstr
= g_string_new("");
866 struct sipe_account_data
*sip
= gc
->proto_data
;
869 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
871 /* Can return NULL! */
872 contact
= get_contact(sip
);
874 sipmsg_add_header(msg
, "Contact", contact
);
879 gchar
*len
= g_strdup_printf("%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
880 sipmsg_add_header(msg
, "Content-Length", len
);
883 sipmsg_add_header(msg
, "Content-Length", "0");
886 msg
->response
= code
;
888 sipmsg_strip_headers(msg
, keepers
);
889 sipmsg_merge_new_headers(msg
);
890 sign_outgoing_message(msg
, sip
, msg
->method
);
892 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
895 name
= ((struct sipnameval
*) (tmp
->data
))->name
;
896 value
= ((struct sipnameval
*) (tmp
->data
))->value
;
898 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
899 tmp
= g_slist_next(tmp
);
901 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
902 sendout_pkt(gc
, outstr
->str
);
903 g_string_free(outstr
, TRUE
);
906 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
908 if (sip
->transactions
) {
909 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
910 purple_debug_info("sipe", "sip->transactions count:%d after removal\n", g_slist_length(sip
->transactions
));
912 if (trans
->msg
) sipmsg_free(trans
->msg
);
913 if (trans
->payload
) {
914 (*trans
->payload
->destroy
)(trans
->payload
->data
);
915 g_free(trans
->payload
);
922 static struct transaction
*
923 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
925 const gchar
*call_id
;
927 struct transaction
*trans
= g_new0(struct transaction
, 1);
929 trans
->time
= time(NULL
);
930 trans
->msg
= (struct sipmsg
*)msg
;
931 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
932 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
933 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
934 trans
->callback
= callback
;
935 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
936 purple_debug_info("sipe", "sip->transactions count:%d after addition\n", g_slist_length(sip
->transactions
));
940 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
942 struct transaction
*trans
;
943 GSList
*transactions
= sip
->transactions
;
944 const gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
945 const gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
948 if (!call_id
|| !cseq
) {
949 purple_debug(PURPLE_DEBUG_ERROR
, "sipe", "transaction_find: no Call-ID or CSeq!\n");
953 key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
954 while (transactions
) {
955 trans
= transactions
->data
;
956 if (!g_strcasecmp(trans
->key
, key
)) {
960 transactions
= transactions
->next
;
968 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
969 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
970 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
972 struct sipe_account_data
*sip
= gc
->proto_data
;
973 const char *addh
= "";
976 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
977 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
978 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
979 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
980 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
981 gchar
*route
= g_strdup("");
982 gchar
*epid
= get_epid(sip
);
983 int cseq
= dialog
? ++dialog
->cseq
: 1 /* as Call-Id is new in this case */;
984 struct transaction
*trans
= NULL
;
986 if (dialog
&& dialog
->routes
)
988 GSList
*iter
= dialog
->routes
;
993 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
995 iter
= g_slist_next(iter
);
999 if (!ourtag
&& !dialog
) {
1003 if (sipe_strequal(method
, "REGISTER")) {
1004 if (sip
->regcallid
) {
1006 callid
= g_strdup(sip
->regcallid
);
1008 sip
->regcallid
= g_strdup(callid
);
1013 if (addheaders
) addh
= addheaders
;
1015 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
1016 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
1017 "From: <sip:%s>%s%s;epid=%s\r\n"
1018 "To: <%s>%s%s%s%s\r\n"
1019 "Max-Forwards: 70\r\n"
1021 "User-Agent: %s\r\n"
1024 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
1026 dialog
&& dialog
->request
? dialog
->request
: url
,
1027 TRANSPORT_DESCRIPTOR
,
1028 purple_network_get_my_ip(-1),
1030 branch
? ";branch=" : "",
1031 branch
? branch
: "",
1033 ourtag
? ";tag=" : "",
1034 ourtag
? ourtag
: "",
1037 theirtag
? ";tag=" : "",
1038 theirtag
? theirtag
: "",
1039 theirepid
? ";epid=" : "",
1040 theirepid
? theirepid
: "",
1043 sipe_get_useragent(sip
),
1047 body
? (gsize
) strlen(body
) : 0,
1051 //printf ("parsing msg buf:\n%s\n\n", buf);
1052 msg
= sipmsg_parse_msg(buf
);
1063 sign_outgoing_message (msg
, sip
, method
);
1065 buf
= sipmsg_to_string (msg
);
1067 /* add to ongoing transactions */
1068 /* ACK isn't supposed to be answered ever. So we do not keep transaction for it. */
1069 if (!sipe_strequal(method
, "ACK")) {
1070 trans
= transactions_add_buf(sip
, msg
, tc
);
1074 sendout_pkt(gc
, buf
);
1081 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
1084 send_soap_request_with_cb(struct sipe_account_data
*sip
,
1087 TransCallback callback
,
1088 struct transaction_payload
*payload
)
1090 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sip
);
1091 gchar
*contact
= get_contact(sip
);
1092 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
1093 "Content-Type: application/SOAP+xml\r\n",contact
);
1095 struct transaction
*trans
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1096 trans
->payload
= payload
;
1103 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1105 send_soap_request_with_cb(sip
, NULL
, body
, NULL
, NULL
);
1108 static char *get_contact_register(struct sipe_account_data
*sip
)
1110 char *epid
= get_epid(sip
);
1111 char *uuid
= generateUUIDfromEPID(epid
);
1112 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
);
1118 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1126 if (!sip
->sipdomain
) return;
1128 uri
= sip_uri_from_name(sip
->sipdomain
);
1129 expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1130 to
= sip_uri_self(sip
);
1131 contact
= get_contact_register(sip
);
1132 hdr
= g_strdup_printf("Contact: %s\r\n"
1133 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1134 "Event: registration\r\n"
1135 "Allow-Events: presence\r\n"
1136 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1137 "%s", contact
, expires
);
1141 sip
->registerstatus
= 1;
1143 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1144 process_register_response
);
1151 static void do_register_cb(struct sipe_account_data
*sip
,
1152 SIPE_UNUSED_PARAMETER
void *unused
)
1154 do_register_exp(sip
, -1);
1155 sip
->reregister_set
= FALSE
;
1158 static void do_register(struct sipe_account_data
*sip
)
1160 do_register_exp(sip
, -1);
1164 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1166 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1167 send_soap_request(sip
, body
);
1172 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1175 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1177 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1180 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1184 void sipe_auth_user_cb(void * data
)
1186 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1189 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1194 void sipe_deny_user_cb(void * data
)
1196 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1199 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1204 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1206 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1207 sipe_contact_allow_deny(sip
, name
, TRUE
);
1211 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1213 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1214 sipe_contact_allow_deny(sip
, name
, FALSE
);
1218 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1220 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1221 sipe_contact_set_acl(sip, name, "");
1225 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1229 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1230 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1232 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| sipe_strequal(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1234 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1235 if (!watchers
) return;
1237 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1238 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1239 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1240 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1242 // TODO pull out optional displayName to pass as alias
1244 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1245 job
->who
= remote_user
;
1247 purple_account_request_authorization(
1261 xmlnode_free(watchers
);
1266 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1268 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1269 if (!purple_group
) {
1270 purple_group
= purple_group_new(group
->name
);
1271 purple_blist_add_group(purple_group
, NULL
);
1275 group
->purple_group
= purple_group
;
1276 sip
->groups
= g_slist_append(sip
->groups
, group
);
1277 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1279 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1283 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1285 struct sipe_group
*group
;
1291 entry
= sip
->groups
;
1293 group
= entry
->data
;
1294 if (group
->id
== id
) {
1297 entry
= entry
->next
;
1302 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1304 struct sipe_group
*group
;
1306 if (!sip
|| !name
) {
1310 entry
= sip
->groups
;
1312 group
= entry
->data
;
1313 if (sipe_strequal(group
->name
, name
)) {
1316 entry
= entry
->next
;
1322 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1325 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1326 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1327 send_soap_request(sip
, body
);
1329 g_free(group
->name
);
1330 group
->name
= g_strdup(name
);
1334 * Only appends if no such value already stored.
1337 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1338 GSList
* res
= list
;
1339 if (!g_slist_find_custom(list
, data
, func
)) {
1340 res
= g_slist_insert_sorted(list
, data
, func
);
1346 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1347 return group1
->id
- group2
->id
;
1351 * Returns string like "2 4 7 8" - group ids buddy belong to.
1354 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1357 //creating array from GList, converting int to gchar*
1358 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1359 GSList
*entry
= buddy
->groups
;
1361 if (!ids_arr
) return NULL
;
1364 struct sipe_group
* group
= entry
->data
;
1365 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1366 entry
= entry
->next
;
1370 res
= g_strjoinv(" ", ids_arr
);
1371 g_strfreev(ids_arr
);
1376 * Sends buddy update to server
1379 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1381 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1382 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1384 if (buddy
&& purple_buddy
) {
1385 const char *alias
= purple_buddy_get_alias(purple_buddy
);
1386 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1389 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1391 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1392 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1394 send_soap_request(sip
, body
);
1401 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1403 if (msg
->response
== 200) {
1404 struct sipe_group
*group
;
1405 struct group_user_context
*ctx
= trans
->payload
->data
;
1409 struct sipe_buddy
*buddy
;
1411 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1416 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1422 group_id
= xmlnode_get_data(node
);
1428 group
= g_new0(struct sipe_group
, 1);
1429 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1431 group
->name
= g_strdup(ctx
->group_name
);
1433 sipe_group_add(sip
, group
);
1435 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1437 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1440 sipe_group_set_user(sip
, ctx
->user_name
);
1448 static void sipe_group_context_destroy(gpointer data
)
1450 struct group_user_context
*ctx
= data
;
1451 g_free(ctx
->group_name
);
1452 g_free(ctx
->user_name
);
1456 static void sipe_group_create (struct sipe_account_data
*sip
, const gchar
*name
, const gchar
* who
)
1458 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
1459 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
1461 ctx
->group_name
= g_strdup(name
);
1462 ctx
->user_name
= g_strdup(who
);
1463 payload
->destroy
= sipe_group_context_destroy
;
1464 payload
->data
= ctx
;
1466 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1467 send_soap_request_with_cb(sip
, NULL
, body
, process_add_group_response
, payload
);
1472 * Data structure for scheduled actions
1475 struct scheduled_action
{
1478 * Format is <Event>[<Data>...]
1479 * Example: <presence><sip:user@domain.com> or <registration>
1482 guint timeout_handler
;
1483 gboolean repetitive
;
1485 GDestroyNotify destroy
;
1486 struct sipe_account_data
*sip
;
1492 * Should return FALSE if repetitive action is not needed
1494 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1497 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1498 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1499 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1500 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1501 ret
= sched_action
->repetitive
;
1502 if (sched_action
->destroy
) {
1503 (*sched_action
->destroy
)(sched_action
->payload
);
1505 g_free(sched_action
->name
);
1506 g_free(sched_action
);
1511 * Kills action timer effectively cancelling
1514 * @param name of action
1516 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1520 if (!sip
->timeouts
|| !name
) return;
1522 entry
= sip
->timeouts
;
1524 struct scheduled_action
*sched_action
= entry
->data
;
1525 if(sipe_strequal(sched_action
->name
, name
)) {
1526 GSList
*to_delete
= entry
;
1527 entry
= entry
->next
;
1528 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1529 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1530 purple_timeout_remove(sched_action
->timeout_handler
);
1531 if (sched_action
->destroy
) {
1532 (*sched_action
->destroy
)(sched_action
->payload
);
1534 g_free(sched_action
->name
);
1535 g_free(sched_action
);
1537 entry
= entry
->next
;
1543 sipe_schedule_action0(const gchar
*name
,
1547 GDestroyNotify destroy
,
1548 struct sipe_account_data
*sip
,
1551 struct scheduled_action
*sched_action
;
1553 /* Make sure each action only exists once */
1554 sipe_cancel_scheduled_action(sip
, name
);
1556 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1557 sched_action
= g_new0(struct scheduled_action
, 1);
1558 sched_action
->repetitive
= FALSE
;
1559 sched_action
->name
= g_strdup(name
);
1560 sched_action
->action
= action
;
1561 sched_action
->destroy
= destroy
;
1562 sched_action
->sip
= sip
;
1563 sched_action
->payload
= payload
;
1564 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1565 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1566 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1567 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1571 sipe_schedule_action(const gchar
*name
,
1574 GDestroyNotify destroy
,
1575 struct sipe_account_data
*sip
,
1578 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1582 * Same as sipe_schedule_action() but timeout is in milliseconds.
1585 sipe_schedule_action_msec(const gchar
*name
,
1588 GDestroyNotify destroy
,
1589 struct sipe_account_data
*sip
,
1592 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1596 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1597 time_t calculate_from
);
1600 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
);
1603 sipe_get_status_by_availability(int avail
,
1607 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
1608 const char *status_id
,
1609 const char *message
,
1610 time_t do_not_publish
[]);
1613 sipe_apply_calendar_status(struct sipe_account_data
*sip
,
1614 struct sipe_buddy
*sbuddy
,
1615 const char *status_id
)
1617 time_t cal_avail_since
;
1618 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
1622 if (!sbuddy
) return;
1624 if (cal_status
< SIPE_CAL_NO_DATA
) {
1625 purple_debug_info("sipe", "sipe_apply_calendar_status: cal_status : %d for %s\n", cal_status
, sbuddy
->name
);
1626 purple_debug_info("sipe", "sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
1629 /* scheduled Cal update call */
1631 status_id
= sbuddy
->last_non_cal_status_id
;
1632 g_free(sbuddy
->activity
);
1633 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
1637 purple_debug_info("sipe", "sipe_apply_calendar_status: status_id is NULL for %s, exiting.\n",
1638 sbuddy
->name
? sbuddy
->name
: "" );
1642 /* adjust to calendar status */
1643 if (cal_status
!= SIPE_CAL_NO_DATA
) {
1644 purple_debug_info("sipe", "sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
1646 if (cal_status
== SIPE_CAL_BUSY
1647 && cal_avail_since
> sbuddy
->user_avail_since
1648 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
1650 status_id
= SIPE_STATUS_ID_BUSY
;
1651 g_free(sbuddy
->activity
);
1652 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
));
1654 avail
= sipe_get_availability_by_status(status_id
, NULL
);
1656 purple_debug_info("sipe", "sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
1657 if (cal_avail_since
> sbuddy
->activity_since
) {
1658 if (cal_status
== SIPE_CAL_OOF
1659 && avail
>= 15000) /* 12000 in 2007 */
1661 g_free(sbuddy
->activity
);
1662 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
1667 /* then set status_id actually */
1668 purple_debug_info("sipe", "sipe_apply_calendar_status: to %s for %s\n", status_id
, sbuddy
->name
? sbuddy
->name
: "" );
1669 purple_prpl_got_user_status(sip
->account
, sbuddy
->name
, status_id
, NULL
);
1671 /* set our account state to the one in roaming (including calendar info) */
1672 self_uri
= sip_uri_self(sip
);
1673 if (sip
->initial_state_published
&& sipe_strequal(sbuddy
->name
, self_uri
)) {
1674 if (sipe_strequal(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
1675 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
1678 purple_debug_info("sipe", "sipe_apply_calendar_status: switch to '%s' for the account\n", sip
->status
);
1679 sipe_set_purple_account_status_and_note(sip
->account
, status_id
, sip
->note
, sip
->do_not_publish
);
1685 sipe_got_user_status(struct sipe_account_data
*sip
,
1687 const char *status_id
)
1689 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
1691 if (!sbuddy
) return;
1693 /* Check if on 2005 system contact's calendar,
1694 * then set/preserve it.
1696 if (!sip
->ocs2007
) {
1697 sipe_apply_calendar_status(sip
, sbuddy
, status_id
);
1699 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
1704 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
1705 struct sipe_buddy
*sbuddy
,
1706 struct sipe_account_data
*sip
)
1708 sipe_apply_calendar_status(sip
, sbuddy
, NULL
);
1712 * Updates contact's status
1713 * based on their calendar information.
1715 * Applicability: 2005 systems
1718 update_calendar_status(struct sipe_account_data
*sip
)
1720 purple_debug_info("sipe", "update_calendar_status() started.\n");
1721 g_hash_table_foreach(sip
->buddies
, (GHFunc
)update_calendar_status_cb
, (gpointer
)sip
);
1723 /* repeat scheduling */
1724 sipe_sched_calendar_status_update(sip
, time(NULL
) + 3*60 /* 3 min */);
1728 * Schedules process of contacts' status update
1729 * based on their calendar information.
1730 * Should be scheduled to the beginning of every
1731 * 15 min interval, like:
1732 * 13:00, 13:15, 13:30, 13:45, etc.
1734 * Applicability: 2005 systems
1737 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1738 time_t calculate_from
)
1740 int interval
= 15*60;
1741 /** start of the beginning of closest 15 min interval. */
1742 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1744 purple_debug_info("sipe", "sipe_sched_calendar_status_update: calculate_from time: %s",
1745 asctime(localtime(&calculate_from
)));
1746 purple_debug_info("sipe", "sipe_sched_calendar_status_update: next start time : %s",
1747 asctime(localtime(&next_start
)));
1749 sipe_schedule_action("<+2005-cal-status>",
1750 (int)(next_start
- time(NULL
)),
1751 (Action
)update_calendar_status
,
1758 * Schedules process of self status publish
1759 * based on own calendar information.
1760 * Should be scheduled to the beginning of every
1761 * 15 min interval, like:
1762 * 13:00, 13:15, 13:30, 13:45, etc.
1764 * Applicability: 2007+ systems
1767 sipe_sched_calendar_status_self_publish(struct sipe_account_data
*sip
,
1768 time_t calculate_from
)
1770 int interval
= 5*60;
1771 /** start of the beginning of closest 5 min interval. */
1772 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1774 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: calculate_from time: %s",
1775 asctime(localtime(&calculate_from
)));
1776 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: next start time : %s",
1777 asctime(localtime(&next_start
)));
1779 sipe_schedule_action("<+2007-cal-status>",
1780 (int)(next_start
- time(NULL
)),
1781 (Action
)publish_calendar_status_self
,
1787 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1789 /** Should be g_free()'d
1792 sipe_get_subscription_key(const gchar
*event
,
1797 if (is_empty(event
)) return NULL
;
1799 if (event
&& !g_ascii_strcasecmp(event
, "presence")) {
1800 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1801 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, with
);
1803 /* @TODO drop participated buddies' just_added flag */
1805 /* Subscription is identified by <event> key */
1806 key
= g_strdup_printf("<%s>", event
);
1812 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
1813 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
1815 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
1816 const gchar
*event
= sipmsg_find_header(msg
, "Event");
1819 /* The case with 2005 Public IM Connectivity (PIC) - no Event header */
1821 struct sipmsg
*request_msg
= trans
->msg
;
1822 event
= sipmsg_find_header(request_msg
, "Event");
1825 key
= sipe_get_subscription_key(event
, with
);
1827 /* 200 OK; 481 Call Leg Does Not Exist */
1828 if (key
&& (msg
->response
== 200 || msg
->response
== 481)) {
1829 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
1830 g_hash_table_remove(sip
->subscriptions
, key
);
1831 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
1835 /* create/store subscription dialog if not yet */
1836 if (msg
->response
== 200) {
1837 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1838 gchar
*cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
1841 struct sip_subscription
*subscription
= g_new0(struct sip_subscription
, 1);
1842 g_hash_table_insert(sip
->subscriptions
, g_strdup(key
), subscription
);
1844 subscription
->dialog
.callid
= g_strdup(callid
);
1845 subscription
->dialog
.cseq
= atoi(cseq
);
1846 subscription
->dialog
.with
= g_strdup(with
);
1847 subscription
->event
= g_strdup(event
);
1848 sipe_dialog_parse(&subscription
->dialog
, msg
, TRUE
);
1850 purple_debug_info("sipe", "process_subscribe_response: subscription dialog added for: %s\n", key
);
1859 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1861 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1866 static void sipe_subscribe_resource_uri(const char *name
,
1867 SIPE_UNUSED_PARAMETER gpointer value
,
1868 gchar
**resources_uri
)
1870 gchar
*tmp
= *resources_uri
;
1871 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1875 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1877 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1878 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1879 gchar
*tmp
= *resources_uri
;
1881 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
1883 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
1888 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1889 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1890 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1891 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1892 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1895 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1898 gchar
*contact
= get_contact(sip
);
1901 gchar
*require
= "";
1903 gchar
*autoextend
= "";
1904 gchar
*content_type
;
1905 struct sip_dialog
*dialog
;
1908 require
= ", categoryList";
1909 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1910 content_type
= "application/msrtc-adrl-categorylist+xml";
1911 content
= g_strdup_printf(
1912 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1913 "<action name=\"subscribe\" id=\"63792024\">\n"
1914 "<adhocList>\n%s</adhocList>\n"
1915 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1916 "<category name=\"calendarData\"/>\n"
1917 "<category name=\"contactCard\"/>\n"
1918 "<category name=\"note\"/>\n"
1919 "<category name=\"state\"/>\n"
1922 "</batchSub>", sip
->username
, resources_uri
);
1924 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1925 content_type
= "application/adrl+xml";
1926 content
= g_strdup_printf(
1927 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1928 "<create xmlns=\"\">\n%s</create>\n"
1929 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1931 g_free(resources_uri
);
1933 request
= g_strdup_printf(
1934 "Require: adhoclist%s\r\n"
1935 "Supported: eventlist\r\n"
1936 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1937 "Supported: ms-piggyback-first-notify\r\n"
1938 "%sSupported: ms-benotify\r\n"
1939 "Proxy-Require: ms-benotify\r\n"
1940 "Event: presence\r\n"
1941 "Content-Type: %s\r\n"
1942 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1945 /* subscribe to buddy presence */
1946 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1947 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1948 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1949 purple_debug_info("sipe", "sipe_subscribe_presence_batched_to: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
1951 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
1959 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
,
1960 SIPE_UNUSED_PARAMETER
void *unused
)
1962 gchar
*to
= sip_uri_self(sip
);
1963 gchar
*resources_uri
= g_strdup("");
1965 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1967 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1970 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1973 struct presence_batched_routed
{
1978 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1980 struct presence_batched_routed
*data
= payload
;
1981 GSList
*buddies
= data
->buddies
;
1983 g_free(buddies
->data
);
1984 buddies
= buddies
->next
;
1986 g_slist_free(data
->buddies
);
1991 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
1993 struct presence_batched_routed
*data
= payload
;
1994 GSList
*buddies
= data
->buddies
;
1995 gchar
*resources_uri
= g_strdup("");
1997 gchar
*tmp
= resources_uri
;
1998 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
2000 buddies
= buddies
->next
;
2002 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
2003 g_strdup(data
->host
));
2007 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
2008 * The user sends a single SUBSCRIBE request to the subscribed contact.
2009 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
2013 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
2017 gchar
*to
= sip_uri((char *)buddy_name
);
2018 gchar
*tmp
= get_contact(sip
);
2020 gchar
*content
= NULL
;
2021 gchar
*autoextend
= "";
2022 gchar
*content_type
= "";
2023 struct sip_dialog
*dialog
;
2024 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, to
);
2025 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
2027 if (sbuddy
) sbuddy
->just_added
= FALSE
;
2030 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
2032 autoextend
= "Supported: com.microsoft.autoextend\r\n";
2035 request
= g_strdup_printf(
2036 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
2037 "Supported: ms-piggyback-first-notify\r\n"
2038 "%s%sSupported: ms-benotify\r\n"
2039 "Proxy-Require: ms-benotify\r\n"
2040 "Event: presence\r\n"
2041 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
2044 content
= g_strdup_printf(
2045 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
2046 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
2047 "<resource uri=\"%s\"%s\n"
2049 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
2050 "<category name=\"calendarData\"/>\n"
2051 "<category name=\"contactCard\"/>\n"
2052 "<category name=\"note\"/>\n"
2053 "<category name=\"state\"/>\n"
2056 "</batchSub>", sip
->username
, to
, context
);
2061 /* subscribe to buddy presence */
2062 /* Subscription is identified by ACTION_NAME_PRESENCE key */
2063 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
2064 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2065 purple_debug_info("sipe", "sipe_subscribe_presence_single: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2067 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
2075 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
2077 purple_debug_info("sipe", "sipe_set_status: status=%s\n", purple_status_get_id(status
));
2079 if (!purple_status_is_active(status
))
2083 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
2088 time_t now
= time(NULL
);
2089 const char *status_id
= purple_status_get_id(status
);
2090 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
2091 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
2092 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
2094 /* when other point of presence clears note, but we are keeping
2095 * state if OOF note.
2097 if (do_not_publish
&& !note
&& sip
->ews
&& sip
->ews
->oof_note
) {
2098 purple_debug_info("sipe", "sipe_set_status: enabling publication as OOF note keepers.\n");
2099 do_not_publish
= FALSE
;
2102 purple_debug_info("sipe", "sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d\n",
2103 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
2105 sip
->do_not_publish
[activity
] = 0;
2106 purple_debug_info("sipe", "sipe_set_status: set: sip->do_not_publish[%s]=%d [0]\n",
2107 status_id
, (int)sip
->do_not_publish
[activity
]);
2111 purple_debug_info("sipe", "sipe_set_status: publication was switched off, exiting.\n");
2115 g_free(sip
->status
);
2116 sip
->status
= g_strdup(status_id
);
2118 /* hack to escape apostrof before comparison */
2119 tmp
= note
? purple_strreplace(note
, "'", "'") : NULL
;
2121 /* this will preserve OOF flag as well */
2122 if (!sipe_strequal(tmp
, sip
->note
)) {
2123 sip
->is_oof_note
= FALSE
;
2125 sip
->note
= g_strdup(note
);
2126 sip
->note_since
= time(NULL
);
2130 /* schedule 2 sec to capture idle flag */
2131 action_name
= g_strdup_printf("<%s>", "+set-status");
2132 sipe_schedule_action(action_name
, SIPE_IDLE_SET_DELAY
, (Action
)send_presence_status
, NULL
, sip
, NULL
);
2133 g_free(action_name
);
2138 sipe_set_idle(PurpleConnection
* gc
,
2141 purple_debug_info("sipe", "sipe_set_idle: interval=%d\n", interval
);
2144 struct sipe_account_data
*sip
= gc
->proto_data
;
2147 sip
->idle_switch
= time(NULL
);
2148 purple_debug_info("sipe", "sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
2154 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
,
2155 SIPE_UNUSED_PARAMETER
const char *alias
)
2157 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2158 sipe_group_set_user(sip
, name
);
2162 sipe_group_buddy(PurpleConnection
*gc
,
2164 const char *old_group_name
,
2165 const char *new_group_name
)
2167 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2168 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
2169 struct sipe_group
* old_group
= NULL
;
2170 struct sipe_group
* new_group
;
2172 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
2173 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
2175 if(!buddy
) { // buddy not in roaming list
2179 if (old_group_name
) {
2180 old_group
= sipe_group_find_by_name(sip
, old_group_name
);
2182 new_group
= sipe_group_find_by_name(sip
, new_group_name
);
2185 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
2186 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
2190 sipe_group_create(sip
, new_group_name
, who
);
2192 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
2193 sipe_group_set_user(sip
, who
);
2197 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2199 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2201 /* libpurple can call us with undefined buddy or group */
2202 if (buddy
&& group
) {
2203 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2205 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2206 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
2207 purple_blist_rename_buddy(buddy
, buddy_name
);
2210 /* Prepend sip: if needed */
2211 if (!g_str_has_prefix(buddy
->name
, "sip:")) {
2212 gchar
*buf
= sip_uri_from_name(buddy
->name
);
2213 purple_blist_rename_buddy(buddy
, buf
);
2217 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
2218 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
2219 purple_debug_info("sipe", "sipe_add_buddy: adding %s\n", buddy
->name
);
2220 b
->name
= g_strdup(buddy
->name
);
2221 b
->just_added
= TRUE
;
2222 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
2223 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
2224 /* @TODO should go to callback */
2225 sipe_subscribe_presence_single(sip
, b
->name
);
2227 purple_debug_info("sipe", "sipe_add_buddy: buddy %s already in internal list\n", buddy
->name
);
2232 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
2236 * We are calling g_hash_table_foreach_steal(). That means that no
2237 * key/value deallocation functions are called. Therefore the glib
2238 * hash code does not touch the key (buddy->name) or value (buddy)
2239 * of the to-be-deleted hash node at all. It follows that we
2241 * - MUST free the memory for the key ourselves and
2242 * - ARE allowed to do it in this function
2244 * Conclusion: glib must be broken on the Windows platform if sipe
2245 * crashes with SIGTRAP when closing. You'll have to live
2246 * with the memory leak until this is fixed.
2248 g_free(buddy
->name
);
2250 g_free(buddy
->activity
);
2251 g_free(buddy
->meeting_subject
);
2252 g_free(buddy
->meeting_location
);
2253 g_free(buddy
->note
);
2255 g_free(buddy
->cal_start_time
);
2256 g_free(buddy
->cal_free_busy_base64
);
2257 g_free(buddy
->cal_free_busy
);
2258 g_free(buddy
->last_non_cal_activity
);
2260 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
2262 g_free(buddy
->device_name
);
2263 g_slist_free(buddy
->groups
);
2268 * Unassociates buddy from group first.
2269 * Then see if no groups left, removes buddy completely.
2270 * Otherwise updates buddy groups on server.
2272 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2274 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2275 struct sipe_buddy
*b
;
2276 struct sipe_group
*g
= NULL
;
2278 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2281 b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
2285 g
= sipe_group_find_by_name(sip
, group
->name
);
2289 b
->groups
= g_slist_remove(b
->groups
, g
);
2290 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
2293 if (g_slist_length(b
->groups
) < 1) {
2294 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
2295 sipe_cancel_scheduled_action(sip
, action_name
);
2296 g_free(action_name
);
2298 g_hash_table_remove(sip
->buddies
, buddy
->name
);
2301 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
2302 send_soap_request(sip
, body
);
2308 //updates groups on server
2309 sipe_group_set_user(sip
, b
->name
);
2315 sipe_rename_group(PurpleConnection
*gc
,
2316 const char *old_name
,
2318 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
2320 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2321 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, old_name
);
2323 sipe_group_rename(sip
, s_group
, group
->name
);
2325 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
2330 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
2332 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2333 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
2336 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
2337 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
2338 send_soap_request(sip
, body
);
2341 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
2342 g_free(s_group
->name
);
2345 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
2349 /** All statuses need message attribute to pass Note */
2350 static GList
*sipe_status_types(SIPE_UNUSED_PARAMETER PurpleAccount
*acc
)
2352 PurpleStatusType
*type
;
2353 GList
*types
= NULL
;
2355 /* Macros to reduce code repetition.
2356 Translators: noun */
2357 #define SIPE_ADD_STATUS(prim,id,name,user) type = purple_status_type_new_with_attrs( \
2359 TRUE, user, FALSE, \
2360 SIPE_STATUS_ATTR_ID_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), \
2362 types = g_list_append(types, type);
2365 SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE
,
2371 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2372 sipe_activity_map
[SIPE_ACTIVITY_BUSY
].status_id
,
2373 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSY
),
2376 /* Do Not Disturb */
2377 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2378 sipe_activity_map
[SIPE_ACTIVITY_DND
].status_id
,
2383 /* Goes first in the list as
2384 * purple picks the first status with the AWAY type
2387 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2393 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2394 sipe_activity_map
[SIPE_ACTIVITY_BRB
].status_id
,
2395 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BRB
),
2398 /* Appear Offline */
2399 SIPE_ADD_STATUS(PURPLE_STATUS_INVISIBLE
,
2405 type
= purple_status_type_new(PURPLE_STATUS_OFFLINE
,
2409 types
= g_list_append(types
, type
);
2415 * A callback for g_hash_table_foreach
2418 sipe_buddy_subscribe_cb(char *buddy_name
,
2419 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
2420 struct sipe_account_data
*sip
)
2422 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy_name
);
2423 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
2424 guint time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
2425 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
2427 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy_name
));
2428 g_free(action_name
);
2432 * Removes entries from purple buddy list
2433 * that does not correspond ones in the roaming contact list.
2435 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
2436 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
2437 GSList
*entry
= buddies
;
2438 struct sipe_buddy
*buddy
;
2442 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
2443 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
2446 g
= purple_buddy_get_group(b
);
2447 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
2449 gboolean in_sipe_groups
= FALSE
;
2450 GSList
*entry2
= buddy
->groups
;
2452 struct sipe_group
*group
= entry2
->data
;
2453 if (sipe_strequal(group
->name
, g
->name
)) {
2454 in_sipe_groups
= TRUE
;
2457 entry2
= entry2
->next
;
2459 if(!in_sipe_groups
) {
2460 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
2461 purple_blist_remove_buddy(b
);
2464 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
2465 purple_blist_remove_buddy(b
);
2467 entry
= entry
->next
;
2469 g_slist_free(buddies
);
2472 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2474 int len
= msg
->bodylen
;
2476 const gchar
*tmp
= sipmsg_find_header(msg
, "Event");
2479 const gchar
*contacts_delta
;
2480 xmlnode
*group_node
;
2481 if (!g_str_has_prefix(tmp
, "vnd-microsoft-roaming-contacts")) {
2485 /* Convert the contact from XML to Purple Buddies */
2486 isc
= xmlnode_from_str(msg
->body
, len
);
2491 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
2492 if (contacts_delta
) {
2493 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2496 if (sipe_strequal(isc
->name
, "contactList")) {
2499 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
2500 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2501 const char *name
= xmlnode_get_attrib(group_node
, "name");
2503 if (g_str_has_prefix(name
, "~")) {
2504 name
= _("Other Contacts");
2506 group
->name
= g_strdup(name
);
2507 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
2509 sipe_group_add(sip
, group
);
2512 // Make sure we have at least one group
2513 if (g_slist_length(sip
->groups
) == 0) {
2514 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2515 PurpleGroup
*purple_group
;
2516 group
->name
= g_strdup(_("Other Contacts"));
2518 purple_group
= purple_group_new(group
->name
);
2519 purple_blist_add_group(purple_group
, NULL
);
2520 sip
->groups
= g_slist_append(sip
->groups
, group
);
2523 /* Parse contacts */
2524 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
2525 const gchar
*uri
= xmlnode_get_attrib(item
, "uri");
2526 const gchar
*name
= xmlnode_get_attrib(item
, "name");
2528 struct sipe_buddy
*buddy
= NULL
;
2530 gchar
**item_groups
;
2533 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2534 tmp
= sip_uri_from_name(uri
);
2535 buddy_name
= g_ascii_strdown(tmp
, -1);
2538 /* assign to group Other Contacts if nothing else received */
2539 tmp
= g_strdup(xmlnode_get_attrib(item
, "groups"));
2541 struct sipe_group
*group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
2543 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
2545 item_groups
= g_strsplit(tmp
, " ", 0);
2548 while (item_groups
[i
]) {
2549 struct sipe_group
*group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2551 // If couldn't find the right group for this contact, just put them in the first group we have
2552 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2553 group
= sip
->groups
->data
;
2556 if (group
!= NULL
) {
2557 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2559 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2560 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2562 purple_debug_info("sipe", "Created new buddy %s with alias %s\n", buddy_name
, uri
);
2565 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
2566 if (name
!= NULL
&& strlen(name
) != 0) {
2567 purple_blist_alias_buddy(b
, name
);
2569 purple_debug_info("sipe", "Replaced buddy %s alias with %s\n", buddy_name
, name
);
2574 buddy
= g_new0(struct sipe_buddy
, 1);
2575 buddy
->name
= g_strdup(b
->name
);
2576 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2579 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2581 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2583 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2588 } // while, contact groups
2589 g_strfreev(item_groups
);
2594 sipe_cleanup_local_blist(sip
);
2596 /* Add self-contact if not there yet. 2005 systems. */
2597 /* This will resemble subscription to roaming_self in 2007 systems */
2598 if (!sip
->ocs2007
) {
2599 gchar
*self_uri
= sip_uri_self(sip
);
2600 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, self_uri
);
2603 buddy
= g_new0(struct sipe_buddy
, 1);
2604 buddy
->name
= g_strdup(self_uri
);
2605 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2612 /* subscribe to buddies */
2613 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2614 if (sip
->batched_support
) {
2615 sipe_subscribe_presence_batched(sip
, NULL
);
2617 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2619 sip
->subscribed_buddies
= TRUE
;
2621 /* for 2005 systems schedule contacts' status update
2622 * based on their calendar information
2624 if (!sip
->ocs2007
) {
2625 sipe_sched_calendar_status_update(sip
, time(NULL
));
2632 * Subscribe roaming contacts
2634 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
)
2636 gchar
*to
= sip_uri_self(sip
);
2637 gchar
*tmp
= get_contact(sip
);
2638 gchar
*hdr
= g_strdup_printf(
2639 "Event: vnd-microsoft-roaming-contacts\r\n"
2640 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2641 "Supported: com.microsoft.autoextend\r\n"
2642 "Supported: ms-benotify\r\n"
2643 "Proxy-Require: ms-benotify\r\n"
2644 "Supported: ms-piggyback-first-notify\r\n"
2645 "Contact: %s\r\n", tmp
);
2648 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2653 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
,
2654 SIPE_UNUSED_PARAMETER
void *unused
)
2657 struct sip_dialog
*dialog
;
2658 gchar
*to
= sip_uri_self(sip
);
2659 gchar
*tmp
= get_contact(sip
);
2660 gchar
*hdr
= g_strdup_printf(
2661 "Event: presence.wpending\r\n"
2662 "Accept: text/xml+msrtc.wpending\r\n"
2663 "Supported: com.microsoft.autoextend\r\n"
2664 "Supported: ms-benotify\r\n"
2665 "Proxy-Require: ms-benotify\r\n"
2666 "Supported: ms-piggyback-first-notify\r\n"
2667 "Contact: %s\r\n", tmp
);
2670 /* Subscription is identified by <event> key */
2671 key
= g_strdup_printf("<%s>", "presence.wpending");
2672 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2673 purple_debug_info("sipe", "sipe_subscribe_presence_wpending: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2675 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", dialog
, process_subscribe_response
);
2683 * Fires on deregistration event initiated by server.
2684 * [MS-SIPREGE] SIP extension.
2689 // Content-Type: text/registration-event
2690 // subscription-state: terminated;expires=0
2691 // ms-diagnostics-public: 4141;reason="User disabled"
2693 // deregistered;event=rejected
2695 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2697 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2698 gchar
*event
= NULL
;
2699 gchar
*reason
= NULL
;
2700 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
2703 diagnostics
= diagnostics
? diagnostics
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2704 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2706 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2707 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2708 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2709 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2711 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2715 if (diagnostics
!= NULL
) {
2716 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
2717 } else { // for LCS2005
2719 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2720 error_id
= 4140; // [MS-SIPREGE]
2721 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2722 reason
= g_strdup(_("you are already signed in at another location"));
2723 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2725 reason
= g_strdup(_("user disabled")); // [MS-OCER]
2726 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2728 reason
= g_strdup(_("user moved")); // [MS-OCER]
2732 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
2735 sip
->gc
->wants_to_die
= TRUE
;
2736 purple_connection_error(sip
->gc
, warning
);
2741 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2743 xmlnode
*xn_provision_group_list
;
2746 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2748 /* provisionGroup */
2749 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2750 if (sipe_strequal("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2751 g_free(sip
->focus_factory_uri
);
2752 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2753 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2754 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2758 xmlnode_free(xn_provision_group_list
);
2761 /** for 2005 system */
2763 sipe_process_provisioning(struct sipe_account_data
*sip
,
2766 xmlnode
*xn_provision
;
2769 xn_provision
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2770 if ((node
= xmlnode_get_child(xn_provision
, "user"))) {
2771 purple_debug_info("sipe", "sipe_process_provisioning: uri=%s\n", xmlnode_get_attrib(node
, "uri"));
2772 if ((node
= xmlnode_get_child(node
, "line"))) {
2773 const gchar
*line_uri
= xmlnode_get_attrib(node
, "uri");
2774 const gchar
*server
= xmlnode_get_attrib(node
, "server");
2775 purple_debug_info("sipe", "sipe_process_provisioning: line_uri=%s server=%s\n", line_uri
, server
);
2776 sip_csta_open(sip
, line_uri
, server
);
2779 xmlnode_free(xn_provision
);
2782 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2784 const gchar
*contacts_delta
;
2787 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2793 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2796 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2803 free_container(struct sipe_container
*container
)
2807 if (!container
) return;
2809 entry
= container
->members
;
2811 void *data
= entry
->data
;
2812 entry
= g_slist_remove(entry
, data
);
2819 * Finds locally stored MS-PRES container member
2821 static struct sipe_container_member
*
2822 sipe_find_container_member(struct sipe_container
*container
,
2826 struct sipe_container_member
*member
;
2829 if (container
== NULL
|| type
== NULL
) {
2833 entry
= container
->members
;
2835 member
= entry
->data
;
2836 if (!g_strcasecmp(member
->type
, type
)
2837 && ((!member
->value
&& !value
)
2838 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2842 entry
= entry
->next
;
2848 * Finds locally stored MS-PRES container by id
2850 static struct sipe_container
*
2851 sipe_find_container(struct sipe_account_data
*sip
,
2854 struct sipe_container
*container
;
2861 entry
= sip
->containers
;
2863 container
= entry
->data
;
2864 if (id
== container
->id
) {
2867 entry
= entry
->next
;
2881 sipe_find_access_level(struct sipe_account_data
*sip
,
2885 guint containers
[] = {32000, 400, 300, 200, 100};
2888 for (i
= 0; i
< 5; i
++) {
2889 struct sipe_container_member
*member
;
2890 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2891 if (!container
) continue;
2893 member
= sipe_find_container_member(container
, type
, value
);
2895 return containers
[i
];
2903 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2905 guint container_version
,
2906 const gchar
* action
,
2910 gchar
*self
= sip_uri_self(sip
);
2911 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2914 gchar
*body
= g_strdup_printf(
2915 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2916 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2917 "</setContainerMembers>",
2925 contact
= get_contact(sip
);
2926 hdr
= g_strdup_printf("Contact: %s\r\n"
2927 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2930 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2938 free_publication(struct sipe_publication
*publication
)
2940 g_free(publication
->category
);
2941 g_free(publication
->cal_event_hash
);
2942 g_free(publication
->note
);
2944 g_free(publication
->working_hours_xml_str
);
2945 g_free(publication
->fb_start_str
);
2946 g_free(publication
->free_busy_base64
);
2948 g_free(publication
);
2951 /* key is <category><instance><container> */
2953 sipe_is_our_publication(struct sipe_account_data
*sip
,
2958 /* filling keys for our publications if not yet cached */
2959 if (!sip
->our_publication_keys
) {
2960 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
2961 guint machine_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
2962 guint user_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
);
2963 guint calendar_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
2964 guint cal_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
);
2965 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
2966 guint note_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
);
2968 purple_debug_info("sipe", "* Our Publication Instances *\n");
2969 purple_debug_info("sipe", "\tDevice : %u\t0x%08X\n", device_instance
, device_instance
);
2970 purple_debug_info("sipe", "\tMachine State : %u\t0x%08X\n", machine_instance
, machine_instance
);
2971 purple_debug_info("sipe", "\tUser Stare : %u\t0x%08X\n", user_instance
, user_instance
);
2972 purple_debug_info("sipe", "\tCalendar State : %u\t0x%08X\n", calendar_instance
, calendar_instance
);
2973 purple_debug_info("sipe", "\tCalendar OOF State : %u\t0x%08X\n", cal_oof_instance
, cal_oof_instance
);
2974 purple_debug_info("sipe", "\tCalendar FreeBusy : %u\t0x%08X\n", cal_data_instance
, cal_data_instance
);
2975 purple_debug_info("sipe", "\tOOF Note : %u\t0x%08X\n", note_oof_instance
, note_oof_instance
);
2976 purple_debug_info("sipe", "\tNote : %u\n", 0);
2977 purple_debug_info("sipe", "\tCalendar WorkingHours: %u\n", 0);
2980 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2981 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
2983 /* state:machineState */
2984 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2985 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
2986 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2987 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
2989 /* state:userState */
2990 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2991 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
2992 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2993 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
2995 /* state:calendarState */
2996 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2997 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
2998 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2999 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
3001 /* state:calendarState OOF */
3002 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3003 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
3004 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3005 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
3008 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3009 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
3010 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3011 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
3012 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3013 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
3016 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3017 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
3018 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3019 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
3020 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3021 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
3023 /* calendarData:WorkingHours */
3024 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3025 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
3026 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3027 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
3028 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3029 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
3030 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3031 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
3032 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3033 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
3034 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3035 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
3037 /* calendarData:FreeBusy */
3038 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3039 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
3040 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3041 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
3042 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3043 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
3044 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3045 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
3046 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3047 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
3048 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3049 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
3051 //purple_debug_info("sipe", "sipe_is_our_publication: sip->our_publication_keys length=%d\n",
3052 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
3055 //purple_debug_info("sipe", "sipe_is_our_publication: key=%s\n", key);
3057 entry
= sip
->our_publication_keys
;
3059 //purple_debug_info("sipe", " sipe_is_our_publication: entry->data=%s\n", entry->data);
3060 if (sipe_strequal(entry
->data
, key
)) {
3063 entry
= entry
->next
;
3068 /** Property names to store in blist.xml */
3069 #define ALIAS_PROP "alias"
3070 #define EMAIL_PROP "email"
3071 #define PHONE_PROP "phone"
3072 #define PHONE_DISPLAY_PROP "phone-display"
3073 #define PHONE_MOBILE_PROP "phone-mobile"
3074 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
3075 #define PHONE_HOME_PROP "phone-home"
3076 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
3077 #define PHONE_OTHER_PROP "phone-other"
3078 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
3079 #define PHONE_CUSTOM1_PROP "phone-custom1"
3080 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
3081 #define SITE_PROP "site"
3082 #define COMPANY_PROP "company"
3083 #define DEPARTMENT_PROP "department"
3084 #define TITLE_PROP "title"
3085 #define OFFICE_PROP "office"
3086 /** implies work address */
3087 #define ADDRESS_STREET_PROP "address-street"
3088 #define ADDRESS_CITY_PROP "address-city"
3089 #define ADDRESS_STATE_PROP "address-state"
3090 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
3091 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
3094 * Tries to figure out user first and last name
3095 * based on Display Name and email properties.
3097 * Allocates memory - must be g_free()'d
3099 * Examples to parse:
3101 * First Last - Company Name
3104 * Last, First (C)(STP) (Company)
3105 * first.last@company.com (preprocessed as "first last")
3106 * first.last.company.com@reuters.net (preprocessed as "first last company com")
3108 * Unusable examples:
3109 * user@company.com (preprocessed as "user")
3110 * first.m.last@company.com (preprocessed as "first m last")
3111 * user.company.com@reuters.net (preprocessed as "user company com")
3114 sipe_get_first_last_names(struct sipe_account_data
*sip
,
3119 PurpleBuddy
*p_buddy
;
3122 const char *first
, *last
;
3125 gboolean has_comma
= FALSE
;
3127 if (!sip
|| !uri
) return;
3129 p_buddy
= purple_find_buddy(sip
->account
, uri
);
3131 if (!p_buddy
) return;
3133 display_name
= g_strdup(purple_buddy_get_alias(p_buddy
));
3134 email
= purple_blist_node_get_string(&p_buddy
->node
, EMAIL_PROP
);
3136 if (!display_name
&& !email
) return;
3138 /* if no display name, make "first last anything_else" out of email */
3139 if (email
&& !display_name
) {
3140 display_name
= g_strndup(email
, strstr(email
, "@") - email
);
3141 display_name
= purple_strreplace((tmp
= display_name
), ".", " ");
3146 has_comma
= (strstr(display_name
, ",") != NULL
);
3147 display_name
= purple_strreplace((tmp
= display_name
), ", ", " ");
3149 display_name
= purple_strreplace((tmp
= display_name
), ",", " ");
3153 parts
= g_strsplit(display_name
, " ", 0);
3155 if (!parts
[0] || !parts
[1]) {
3156 g_free(display_name
);
3170 *first_name
= g_strstrip(g_strdup(first
));
3174 *last_name
= g_strstrip(g_strdup(last
));
3177 g_free(display_name
);
3182 * Update user information
3184 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3185 * @param property_name
3186 * @param property_value may be modified to strip white space
3189 sipe_update_user_info(struct sipe_account_data
*sip
,
3191 const char *property_name
,
3192 char *property_value
)
3194 GSList
*buddies
, *entry
;
3196 if (!property_name
|| strlen(property_name
) == 0) return;
3199 property_value
= g_strstrip(property_value
);
3201 entry
= buddies
= purple_find_buddies(sip
->account
, uri
); /* all buddies in different groups */
3203 const char *prop_str
;
3204 const char *server_alias
;
3205 PurpleBuddy
*p_buddy
= entry
->data
;
3207 /* for Display Name */
3208 if (sipe_strequal(property_name
, ALIAS_PROP
)) {
3209 if (property_value
&& sipe_is_bad_alias(uri
, purple_buddy_get_alias(p_buddy
))) {
3210 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, property_value
);
3211 purple_blist_alias_buddy(p_buddy
, property_value
);
3214 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3215 if (!is_empty(property_value
) &&
3216 (!sipe_strequal(property_value
, server_alias
) || is_empty(server_alias
)) )
3218 purple_blist_server_alias_buddy(p_buddy
, property_value
);
3221 /* for other properties */
3223 if (!is_empty(property_value
)) {
3224 prop_str
= purple_blist_node_get_string(&p_buddy
->node
, property_name
);
3225 if (!prop_str
|| g_ascii_strcasecmp(prop_str
, property_value
)) {
3226 purple_blist_node_set_string(&p_buddy
->node
, property_name
, property_value
);
3231 entry
= entry
->next
;
3233 g_slist_free(buddies
);
3238 * Suitable for both 2005 and 2007 systems.
3240 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3242 * @param phone may be modified to strip white space
3243 * @param phone_display_string may be modified to strip white space
3246 sipe_update_user_phone(struct sipe_account_data
*sip
,
3248 const gchar
*phone_type
,
3250 gchar
*phone_display_string
)
3252 const char *phone_node
= PHONE_PROP
; /* work phone by default */
3253 const char *phone_display_node
= PHONE_DISPLAY_PROP
; /* work phone by default */
3255 if(!phone
|| strlen(phone
) == 0) return;
3257 if ((sipe_strequal(phone_type
, "mobile") || sipe_strequal(phone_type
, "cell"))) {
3258 phone_node
= PHONE_MOBILE_PROP
;
3259 phone_display_node
= PHONE_MOBILE_DISPLAY_PROP
;
3260 } else if (sipe_strequal(phone_type
, "home")) {
3261 phone_node
= PHONE_HOME_PROP
;
3262 phone_display_node
= PHONE_HOME_DISPLAY_PROP
;
3263 } else if (sipe_strequal(phone_type
, "other")) {
3264 phone_node
= PHONE_OTHER_PROP
;
3265 phone_display_node
= PHONE_OTHER_DISPLAY_PROP
;
3266 } else if (sipe_strequal(phone_type
, "custom1")) {
3267 phone_node
= PHONE_CUSTOM1_PROP
;
3268 phone_display_node
= PHONE_CUSTOM1_DISPLAY_PROP
;
3271 sipe_update_user_info(sip
, uri
, phone_node
, phone
);
3272 if (phone_display_string
) {
3273 sipe_update_user_info(sip
, uri
, phone_display_node
, phone_display_string
);
3278 sipe_update_calendar(struct sipe_account_data
*sip
)
3280 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
3282 purple_debug_info("sipe", "sipe_update_calendar: started.\n");
3284 if (sipe_strequal(calendar
, "EXCH")) {
3285 sipe_ews_update_calendar(sip
);
3288 /* schedule repeat */
3289 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_INTERVAL
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3291 purple_debug_info("sipe", "sipe_update_calendar: finished.\n");
3295 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
3296 * by using standard Purple's means of signals and saved statuses.
3298 * Thus all UI elements get updated: Status Button with Note, docklet.
3299 * This is ablolutely important as both our status and note can come
3300 * inbound (roaming) or be updated programmatically (e.g. based on our
3304 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
3305 const char *status_id
,
3306 const char *message
,
3307 time_t do_not_publish
[])
3309 PurpleStatus
*status
= purple_account_get_active_status(account
);
3310 gboolean changed
= TRUE
;
3312 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
3313 sipe_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
3318 if (purple_savedstatus_is_idleaway()) {
3323 PurpleSavedStatus
*saved_status
;
3324 const PurpleStatusType
*acct_status_type
=
3325 purple_status_type_find_with_id(account
->status_types
, status_id
);
3326 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
3327 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
3329 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
3331 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
3334 /* If this type+message is unique then create a new transient saved status
3335 * Ref: gtkstatusbox.c
3337 if (!saved_status
) {
3339 GList
*active_accts
= purple_accounts_get_all_active();
3341 saved_status
= purple_savedstatus_new(NULL
, primitive
);
3342 purple_savedstatus_set_message(saved_status
, message
);
3344 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
3345 purple_savedstatus_set_substatus(saved_status
,
3346 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
3348 g_list_free(active_accts
);
3351 do_not_publish
[activity
] = time(NULL
);
3352 purple_debug_info("sipe", "sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]\n",
3353 status_id
, (int)do_not_publish
[activity
]);
3355 /* Set the status for each account */
3356 purple_savedstatus_activate(saved_status
);
3360 struct hash_table_delete_payload
{
3361 GHashTable
*hash_table
;
3366 sipe_remove_category_container_publications_cb(const char *name
,
3367 struct sipe_publication
*publication
,
3368 struct hash_table_delete_payload
*payload
)
3370 if (publication
->container
== payload
->container
) {
3371 g_hash_table_remove(payload
->hash_table
, name
);
3375 sipe_remove_category_container_publications(GHashTable
*our_publications
,
3376 const char *category
,
3379 struct hash_table_delete_payload payload
;
3380 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
3382 if (!payload
.hash_table
) return;
3384 payload
.container
= container
;
3385 g_hash_table_foreach(payload
.hash_table
, (GHFunc
)sipe_remove_category_container_publications_cb
, &payload
);
3389 send_publish_category_initial(struct sipe_account_data
*sip
);
3392 * When we receive some self (BE) NOTIFY with a new subscriber
3393 * we sends a setSubscribers request to him [SIP-PRES] 4.8
3396 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3403 char *display_name
= NULL
;
3405 GSList
*category_names
= NULL
;
3406 int aggreg_avail
= 0;
3407 static sipe_activity aggreg_activity
= SIPE_ACTIVITY_UNSET
;
3408 gboolean do_update_status
= FALSE
;
3409 gboolean has_note_cleaned
= FALSE
;
3411 purple_debug_info("sipe", "sipe_process_roaming_self\n");
3413 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3416 contact
= get_contact(sip
);
3417 to
= sip_uri_self(sip
);
3421 /* set list of categories participating in this XML */
3422 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3423 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3424 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
3426 purple_debug_info("sipe", "sipe_process_roaming_self: category_names length=%d\n",
3427 category_names
? (int) g_slist_length(category_names
) : -1);
3428 /* drop category information */
3429 if (category_names
) {
3430 GSList
*entry
= category_names
;
3432 GHashTable
*cat_publications
;
3433 const gchar
*category
= entry
->data
;
3434 entry
= entry
->next
;
3435 purple_debug_info("sipe", "sipe_process_roaming_self: dropping category: %s\n", category
);
3436 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
3437 if (cat_publications
) {
3438 g_hash_table_remove(sip
->our_publications
, category
);
3439 purple_debug_info("sipe", " sipe_process_roaming_self: dropped category: %s\n", category
);
3443 g_slist_free(category_names
);
3444 /* filling our categories reflected in roaming data */
3445 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3447 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3448 guint container
= xmlnode_get_int_attrib(node
, "container", -1);
3449 guint instance
= xmlnode_get_int_attrib(node
, "instance", -1);
3450 guint version
= xmlnode_get_int_attrib(node
, "version", 0);
3451 time_t publish_time
= (tmp
= xmlnode_get_attrib(node
, "publishTime")) ?
3452 sipe_utils_str_to_time(tmp
) : 0;
3454 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
3456 /* Ex. clear note: <category name="note"/> */
3457 if (container
== (guint
)-1) {
3460 do_update_status
= TRUE
;
3464 /* Ex. clear note: <category name="note" container="200"/> */
3465 if (instance
== (guint
)-1) {
3466 if (container
== 200) {
3469 do_update_status
= TRUE
;
3471 purple_debug_info("sipe", "sipe_process_roaming_self: removing publications for: %s/%u\n", name
, container
);
3472 sipe_remove_category_container_publications(
3473 sip
->our_publications
, name
, container
);
3477 /* key is <category><instance><container> */
3478 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
3479 purple_debug_info("sipe", "sipe_process_roaming_self: key=%s version=%d\n", key
, version
);
3481 /* capture all userState publication for later clean up if required */
3482 if (sipe_strequal(name
, "state") && (container
== 2 || container
== 3)) {
3483 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3485 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "userState")) {
3486 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3487 publication
->category
= g_strdup(name
);
3488 publication
->instance
= instance
;
3489 publication
->container
= container
;
3490 publication
->version
= version
;
3492 if (!sip
->user_state_publications
) {
3493 sip
->user_state_publications
= g_hash_table_new_full(
3494 g_str_hash
, g_str_equal
,
3495 g_free
, (GDestroyNotify
)free_publication
);
3497 g_hash_table_insert(sip
->user_state_publications
, g_strdup(key
), publication
);
3498 purple_debug_info("sipe", "sipe_process_roaming_self: added to user_state_publications key=%s version=%d\n",
3503 if (sipe_is_our_publication(sip
, key
)) {
3504 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3506 publication
->category
= g_strdup(name
);
3507 publication
->instance
= instance
;
3508 publication
->container
= container
;
3509 publication
->version
= version
;
3511 /* filling publication->availability */
3512 if (sipe_strequal(name
, "state")) {
3513 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3514 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3517 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3519 publication
->availability
= atoi(avail_str
);
3523 /* for calendarState */
3524 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "calendarState")) {
3525 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3526 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
3528 event
->start_time
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "startTime"));
3530 if (sipe_strequal(xmlnode_get_attrib(xn_activity
, "token"),
3531 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
))
3533 event
->is_meeting
= TRUE
;
3536 event
->subject
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingSubject"));
3537 event
->location
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingLocation"));
3539 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
3540 purple_debug_info("sipe", "sipe_process_roaming_self: hash=%s\n",
3541 publication
->cal_event_hash
);
3542 sipe_cal_event_free(event
);
3545 /* filling publication->note */
3546 if (sipe_strequal(name
, "note")) {
3547 xmlnode
*xn_body
= xmlnode_get_descendant(node
, "note", "body", NULL
);
3549 if (!has_note_cleaned
) {
3550 has_note_cleaned
= TRUE
;
3554 sip
->note_since
= publish_time
;
3556 do_update_status
= TRUE
;
3559 g_free(publication
->note
);
3560 publication
->note
= NULL
;
3564 publication
->note
= g_markup_escape_text((tmp
= xmlnode_get_data(xn_body
)), -1);
3566 if (publish_time
>= sip
->note_since
) {
3568 sip
->note
= g_strdup(publication
->note
);
3569 sip
->note_since
= publish_time
;
3570 sip
->is_oof_note
= sipe_strequal(xmlnode_get_attrib(xn_body
, "type"), "OOF");
3572 do_update_status
= TRUE
;
3577 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
3578 if (sipe_strequal(name
, "calendarData") && (publication
->container
== 300)) {
3579 xmlnode
*xn_free_busy
= xmlnode_get_descendant(node
, "calendarData", "freeBusy", NULL
);
3580 xmlnode
*xn_working_hours
= xmlnode_get_descendant(node
, "calendarData", "WorkingHours", NULL
);
3582 publication
->fb_start_str
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
3583 publication
->free_busy_base64
= xmlnode_get_data(xn_free_busy
);
3585 if (xn_working_hours
) {
3586 publication
->working_hours_xml_str
= xmlnode_to_str(xn_working_hours
, NULL
);
3590 if (!cat_publications
) {
3591 cat_publications
= g_hash_table_new_full(
3592 g_str_hash
, g_str_equal
,
3593 g_free
, (GDestroyNotify
)free_publication
);
3594 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
3595 purple_debug_info("sipe", "sipe_process_roaming_self: added GHashTable cat=%s\n", name
);
3597 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
3598 purple_debug_info("sipe", "sipe_process_roaming_self: added key=%s version=%d\n", key
, version
);
3602 /* aggregateState (not an our publication) from 2-nd container */
3603 if (sipe_strequal(name
, "state") && container
== 2) {
3604 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3606 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "aggregateState")) {
3607 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3608 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3611 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3613 aggreg_avail
= atoi(avail_str
);
3619 const char *activity_token
= xmlnode_get_attrib(xn_activity
, "token");
3621 aggreg_activity
= sipe_get_activity_by_token(activity_token
);
3624 do_update_status
= TRUE
;
3628 /* userProperties published by server from AD */
3629 if (!sip
->csta
&& sipe_strequal(name
, "userProperties")) {
3631 /* line, for Remote Call Control (RCC) */
3632 for (line
= xmlnode_get_descendant(node
, "userProperties", "lines", "line", NULL
); line
; line
= xmlnode_get_next_twin(line
)) {
3633 const gchar
*line_server
= xmlnode_get_attrib(line
, "lineServer");
3634 const gchar
*line_type
= xmlnode_get_attrib(line
, "lineType");
3637 if (!line_server
|| !(sipe_strequal(line_type
, "Rcc") || sipe_strequal(line_type
, "Dual"))) continue;
3639 line_uri
= xmlnode_get_data(line
);
3641 purple_debug_info("sipe", "sipe_process_roaming_self: line_uri=%s server=%s\n", line_uri
, line_server
);
3642 sip_csta_open(sip
, line_uri
, line_server
);
3650 purple_debug_info("sipe", "sipe_process_roaming_self: sip->our_publications size=%d\n",
3651 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
3654 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3655 guint id
= xmlnode_get_int_attrib(node
, "id", 0);
3656 struct sipe_container
*container
= sipe_find_container(sip
, id
);
3659 sip
->containers
= g_slist_remove(sip
->containers
, container
);
3660 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
3661 free_container(container
);
3663 container
= g_new0(struct sipe_container
, 1);
3665 container
->version
= xmlnode_get_int_attrib(node
, "version", 0);
3666 sip
->containers
= g_slist_append(sip
->containers
, container
);
3667 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
3669 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
3670 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
3671 member
->type
= xmlnode_get_attrib(node2
, "type");
3672 member
->value
= xmlnode_get_attrib(node2
, "value");
3673 container
->members
= g_slist_append(container
->members
, member
);
3674 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
3675 member
->type
, member
->value
? member
->value
: "");
3679 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
3680 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
3681 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
3682 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
3683 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
3684 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
3685 /* initial set-up to let counterparties see your status */
3686 if (sameEnterpriseAL
< 0) {
3687 struct sipe_container
*container
= sipe_find_container(sip
, 200);
3688 guint version
= container
? container
->version
: 0;
3689 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
3691 if (federatedAL
< 0) {
3692 struct sipe_container
*container
= sipe_find_container(sip
, 100);
3693 guint version
= container
? container
->version
: 0;
3694 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
3696 sip
->access_level_set
= TRUE
;
3700 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3702 const char *acknowledged
;
3706 user
= xmlnode_get_attrib(node
, "user"); /* without 'sip:' prefix */
3707 if (!user
) continue;
3708 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
3709 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
3710 uri
= sip_uri_from_name(user
);
3712 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
3714 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
3715 if(!g_ascii_strcasecmp(acknowledged
,"false")){
3716 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
3717 if (!purple_find_buddy(sip
->account
, uri
)) {
3718 purple_account_request_add(sip
->account
, uri
, _("you"), display_name
, NULL
);
3721 hdr
= g_strdup_printf(
3723 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
3725 body
= g_strdup_printf(
3726 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
3727 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
3728 "</setSubscribers>", user
);
3730 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
3734 g_free(display_name
);
3741 /* Publish initial state if not yet.
3742 * Assuming this happens on initial responce to subscription to roaming-self
3743 * so we've already updated our roaming data in full.
3746 if (!sip
->initial_state_published
) {
3747 send_publish_category_initial(sip
);
3748 sip
->initial_state_published
= TRUE
;
3750 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3751 do_update_status
= FALSE
;
3752 } else if (aggreg_avail
) {
3754 g_free(sip
->status
);
3755 if (aggreg_avail
&& aggreg_avail
< 18000) { /* not offline */
3756 sip
->status
= g_strdup(sipe_get_status_by_availability(aggreg_avail
, NULL
));
3758 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
3762 if (do_update_status
) {
3763 purple_debug_info("sipe", "sipe_process_roaming_self: switch to '%s' for the account\n", sip
->status
);
3764 sipe_set_purple_account_status_and_note(sip
->account
, sip
->status
, sip
->note
, sip
->do_not_publish
);
3770 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
)
3772 gchar
*to
= sip_uri_self(sip
);
3773 gchar
*tmp
= get_contact(sip
);
3774 gchar
*hdr
= g_strdup_printf(
3775 "Event: vnd-microsoft-roaming-ACL\r\n"
3776 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
3777 "Supported: com.microsoft.autoextend\r\n"
3778 "Supported: ms-benotify\r\n"
3779 "Proxy-Require: ms-benotify\r\n"
3780 "Supported: ms-piggyback-first-notify\r\n"
3781 "Contact: %s\r\n", tmp
);
3784 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
3790 * To request for presence information about the user, access level settings that have already been configured by the user
3791 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
3792 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
3795 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
)
3797 gchar
*to
= sip_uri_self(sip
);
3798 gchar
*tmp
= get_contact(sip
);
3799 gchar
*hdr
= g_strdup_printf(
3800 "Event: vnd-microsoft-roaming-self\r\n"
3801 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
3802 "Supported: ms-benotify\r\n"
3803 "Proxy-Require: ms-benotify\r\n"
3804 "Supported: ms-piggyback-first-notify\r\n"
3806 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
3808 gchar
*body
=g_strdup(
3809 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
3810 "<roaming type=\"categories\"/>"
3811 "<roaming type=\"containers\"/>"
3812 "<roaming type=\"subscribers\"/></roamingList>");
3815 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3824 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
)
3826 gchar
*to
= sip_uri_self(sip
);
3827 gchar
*tmp
= get_contact(sip
);
3828 gchar
*hdr
= g_strdup_printf(
3829 "Event: vnd-microsoft-provisioning\r\n"
3830 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
3831 "Supported: com.microsoft.autoextend\r\n"
3832 "Supported: ms-benotify\r\n"
3833 "Proxy-Require: ms-benotify\r\n"
3834 "Supported: ms-piggyback-first-notify\r\n"
3836 "Contact: %s\r\n", tmp
);
3839 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
3844 /** Subscription for provisioning information to help with initial
3845 * configuration. This subscription is a one-time query (denoted by the Expires header,
3846 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
3847 * configuration, meeting policies, and policy settings that Communicator must enforce.
3848 * TODO: for what we need this information.
3851 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
)
3853 gchar
*to
= sip_uri_self(sip
);
3854 gchar
*tmp
= get_contact(sip
);
3855 gchar
*hdr
= g_strdup_printf(
3856 "Event: vnd-microsoft-provisioning-v2\r\n"
3857 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
3858 "Supported: com.microsoft.autoextend\r\n"
3859 "Supported: ms-benotify\r\n"
3860 "Proxy-Require: ms-benotify\r\n"
3861 "Supported: ms-piggyback-first-notify\r\n"
3864 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
3865 gchar
*body
= g_strdup(
3866 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
3867 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
3868 "<provisioningGroup name=\"ucPolicy\"/>"
3869 "</provisioningGroupList>");
3872 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3879 sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key
,
3880 gpointer value
, gpointer user_data
)
3882 struct sip_subscription
*subscription
= value
;
3883 struct sip_dialog
*dialog
= &subscription
->dialog
;
3884 struct sipe_account_data
*sip
= user_data
;
3885 gchar
*tmp
= get_contact(sip
);
3886 gchar
*hdr
= g_strdup_printf(
3889 "Contact: %s\r\n", subscription
->event
, tmp
);
3892 /* Rate limit to max. 25 requests per seconds */
3893 g_usleep(1000000 / 25);
3895 send_sip_request(sip
->gc
, "SUBSCRIBE", dialog
->with
, dialog
->with
, hdr
, NULL
, dialog
, NULL
);
3899 /* IM Session (INVITE and MESSAGE methods) */
3901 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
3903 get_end_points (struct sipe_account_data
*sip
,
3904 struct sip_session
*session
)
3908 if (session
== NULL
) {
3912 res
= g_strdup_printf("<sip:%s>", sip
->username
);
3914 SIPE_DIALOG_FOREACH
{
3916 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
3919 if (dialog
->theirepid
) {
3921 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
3924 } SIPE_DIALOG_FOREACH_END
;
3930 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_account_data
*sip
,
3932 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3934 gboolean ret
= TRUE
;
3936 if (msg
->response
!= 200) {
3937 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
3941 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
3947 * Asks UA/proxy about its capabilities.
3949 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
3951 gchar
*to
= sip_uri(who
);
3952 gchar
*contact
= get_contact(sip
);
3953 gchar
*request
= g_strdup_printf(
3954 "Accept: application/sdp\r\n"
3955 "Contact: %s\r\n", contact
);
3958 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
3965 sipe_notify_user(struct sipe_account_data
*sip
,
3966 struct sip_session
*session
,
3967 PurpleMessageFlags flags
,
3968 const gchar
*message
)
3970 PurpleConversation
*conv
;
3972 if (!session
->conv
) {
3973 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
3975 conv
= session
->conv
;
3977 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
3981 sipe_present_info(struct sipe_account_data
*sip
,
3982 struct sip_session
*session
,
3983 const gchar
*message
)
3985 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
3989 sipe_present_err(struct sipe_account_data
*sip
,
3990 struct sip_session
*session
,
3991 const gchar
*message
)
3993 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_ERROR
, message
);
3997 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
3998 struct sip_session
*session
,
4002 const gchar
*message
)
4004 char *msg
, *msg_tmp
, *msg_tmp2
;
4007 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
4008 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
4010 /* Service unavailable; Server Internal Error; Server Time-out */
4011 if (sip_error
== 606 && sip_warning
== 309) { /* Not acceptable all. */ /* Message contents not allowed by policy */
4012 label
= _("Your message or invitation was not delivered, possibly because it contains a hyperlink or other content that the system administrator has blocked.");
4015 } else if (sip_error
== 503 || sip_error
== 500 || sip_error
== 504) {
4016 label
= _("This message was not delivered to %s because the service is not available");
4017 } else if (sip_error
== 486) { /* Busy Here */
4018 label
= _("This message was not delivered to %s because one or more recipients do not want to be disturbed");
4020 label
= _("This message was not delivered to %s because one or more recipients are offline");
4023 msg_tmp
= g_strdup_printf( "%s%s\n%s" ,
4024 msg_tmp2
= g_strdup_printf(label
, who
? who
: ""),
4027 sipe_present_err(sip
, session
, msg_tmp
);
4035 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4036 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
4038 gboolean ret
= TRUE
;
4039 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
4040 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
4041 struct sip_dialog
*dialog
;
4044 struct queued_message
*message
;
4047 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
4052 dialog
= sipe_dialog_find(session
, with
);
4054 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
4059 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4060 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
4062 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4064 if (msg
->response
>= 400) {
4065 PurpleBuddy
*pbuddy
;
4066 const char *alias
= with
;
4067 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
4070 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
4073 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
4075 warning
= atoi(parts
[0]);
4080 /* cancel file transfer as rejected by server */
4081 if (msg
->response
== 606 && /* Not acceptable all. */
4082 warning
== 309 && /* Message contents not allowed by policy */
4083 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
4085 GSList
*parsed_body
= sipe_ft_parse_msg_body(msg
->body
);
4086 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4087 sipe_utils_nameval_free(parsed_body
);
4090 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4091 alias
= purple_buddy_get_alias(pbuddy
);
4094 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, warning
, alias
, (message
? message
->body
: NULL
));
4096 /* drop dangling IM sessions: assume that BYE from remote never reached us */
4097 if (msg
->response
== 408 || /* Request timeout */
4098 msg
->response
== 480 || /* Temporarily Unavailable */
4099 msg
->response
== 481) { /* Call/Transaction Does Not Exist */
4100 purple_debug_info("sipe", "process_message_response: assuming dangling IM session, dropping it.\n");
4101 send_sip_request(sip
->gc
, "BYE", with
, with
, NULL
, NULL
, dialog
, NULL
);
4106 const gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
4108 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
->body
));
4109 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
4110 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
4113 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4114 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
4115 key
, g_hash_table_size(session
->unconfirmed_messages
));
4121 if (ret
) sipe_im_process_queue(sip
, session
);
4126 sipe_is_election_finished(struct sip_session
*session
);
4129 sipe_election_result(struct sipe_account_data
*sip
,
4133 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4134 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
4136 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4137 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4138 struct sip_dialog
*dialog
;
4139 struct sip_session
*session
;
4141 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4143 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
4147 if (msg
->response
== 200 && g_str_has_prefix(contenttype
, "application/x-ms-mim")) {
4148 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4149 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
4150 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
4152 if (xn_request_rm_response
) {
4153 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
4154 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
4156 dialog
= sipe_dialog_find(session
, with
);
4158 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
4159 xmlnode_free(xn_action
);
4163 if (allow
&& !g_strcasecmp(allow
, "true")) {
4164 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
4165 dialog
->election_vote
= 1;
4166 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
4167 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
4168 dialog
->election_vote
= -1;
4171 if (sipe_is_election_finished(session
)) {
4172 sipe_election_result(sip
, session
);
4175 } else if (xn_set_rm_response
) {
4178 xmlnode_free(xn_action
);
4185 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
, const char *content_type
)
4189 char *msgtext
= NULL
;
4190 const gchar
*msgr
= "";
4193 if (!g_str_has_prefix(content_type
, "text/x-msmsgsinvite")) {
4197 sipe_parse_html(msg
, &msgformat
, &msgtext
);
4198 purple_debug_info("sipe", "sipe_send_message: msgformat=%s\n", msgformat
);
4200 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4203 msgr
= tmp2
= g_strdup_printf(";msgr=%s", msgr_value
);
4207 msgtext
= g_strdup(msg
);
4210 tmp
= get_contact(sip
);
4211 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
4212 //hdr = g_strdup("Content-Type: text/rtf\r\n");
4213 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
4214 if (content_type
== NULL
)
4215 content_type
= "text/plain";
4217 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp
, content_type
, msgr
);
4221 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
4228 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
4230 GSList
*entry2
= session
->outgoing_message_queue
;
4232 struct queued_message
*msg
= entry2
->data
;
4234 /* for multiparty chat or conference */
4235 if (session
->is_multiparty
|| session
->focus_uri
) {
4236 gchar
*who
= sip_uri_self(sip
);
4237 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
4238 PURPLE_MESSAGE_SEND
, msg
->body
, time(NULL
));
4242 SIPE_DIALOG_FOREACH
{
4244 struct queued_message
*message
;
4246 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
4248 message
= g_new0(struct queued_message
,1);
4249 message
->body
= g_strdup(msg
->body
);
4250 if (msg
->content_type
!= NULL
)
4251 message
->content_type
= g_strdup(msg
->content_type
);
4253 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
4254 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
4255 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
4256 key
, g_hash_table_size(session
->unconfirmed_messages
));
4259 sipe_send_message(sip
, dialog
, msg
->body
, msg
->content_type
);
4260 } SIPE_DIALOG_FOREACH_END
;
4262 entry2
= sipe_session_dequeue_message(session
);
4267 sipe_refer_notify(struct sipe_account_data
*sip
,
4268 struct sip_session
*session
,
4275 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4277 hdr
= g_strdup_printf(
4279 "Subscription-State: %s\r\n"
4280 "Content-Type: message/sipfrag\r\n",
4281 status
>= 200 ? "terminated" : "active");
4283 body
= g_strdup_printf(
4284 "SIP/2.0 %d %s\r\n",
4287 send_sip_request(sip
->gc
, "NOTIFY", who
, who
, hdr
, body
, dialog
, NULL
);
4294 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
4296 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
4297 struct sip_session
*session
;
4298 struct sip_dialog
*dialog
;
4301 struct queued_message
*message
;
4302 struct sipmsg
*request_msg
= trans
->msg
;
4304 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4307 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4309 session
= sipe_session_find_im(sip
, with
);
4312 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
4317 dialog
= sipe_dialog_find(session
, with
);
4319 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
4324 sipe_dialog_parse(dialog
, msg
, TRUE
);
4326 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4327 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
4329 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4331 if (msg
->response
!= 200) {
4332 PurpleBuddy
*pbuddy
;
4333 const char *alias
= with
;
4334 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
4337 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
4340 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
4342 warning
= atoi(parts
[0]);
4347 /* cancel file transfer as rejected by server */
4348 if (msg
->response
== 606 && /* Not acceptable all. */
4349 warning
== 309 && /* Message contents not allowed by policy */
4350 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
4352 GSList
*parsed_body
= sipe_ft_parse_msg_body(message
->body
);
4353 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4354 sipe_utils_nameval_free(parsed_body
);
4357 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4358 alias
= purple_buddy_get_alias(pbuddy
);
4362 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, warning
, alias
, message
->body
);
4364 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
4365 sipe_present_err(sip
, session
, tmp_msg
);
4369 sipe_dialog_remove(session
, with
);
4377 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4378 dialog
->outgoing_invite
= NULL
;
4379 dialog
->is_established
= TRUE
;
4381 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
4383 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
4384 g_free(referred_by
);
4387 /* add user to chat if it is a multiparty session */
4388 if (session
->is_multiparty
) {
4389 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4391 PURPLE_CBFLAGS_NONE
, TRUE
);
4394 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
4395 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
4396 sipe_session_dequeue_message(session
);
4399 sipe_im_process_queue(sip
, session
);
4401 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4402 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
4403 key
, g_hash_table_size(session
->unconfirmed_messages
));
4412 sipe_invite(struct sipe_account_data
*sip
,
4413 struct sip_session
*session
,
4415 const gchar
*msg_body
,
4416 const gchar
*msg_content_type
,
4417 const gchar
*referred_by
,
4418 const gboolean is_triggered
)
4425 char *ms_text_format
= NULL
;
4426 gchar
*roster_manager
;
4428 gchar
*referred_by_str
;
4429 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4431 if (dialog
&& dialog
->is_established
) {
4432 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
4437 dialog
= sipe_dialog_add(session
);
4438 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
4439 dialog
->with
= g_strdup(who
);
4442 if (!(dialog
->ourtag
)) {
4443 dialog
->ourtag
= gentag();
4449 char *msgtext
= NULL
;
4451 const gchar
*msgr
= "";
4453 struct queued_message
*message
;
4456 if (!g_str_has_prefix(msg_content_type
, "text/x-msmsgsinvite")) {
4460 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
4461 purple_debug_info("sipe", "sipe_invite: msgformat=%s\n", msgformat
);
4463 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4466 msgr
= tmp
= g_strdup_printf(";msgr=%s", msgr_value
);
4470 msgtext
= g_strdup(msg_body
);
4473 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
4474 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
,
4475 msg_content_type
? msg_content_type
: "text/plain",
4482 message
= g_new0(struct queued_message
,1);
4483 message
->body
= g_strdup(msg_body
);
4484 if (msg_content_type
!= NULL
)
4485 message
->content_type
= g_strdup(msg_content_type
);
4487 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
4488 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
4489 purple_debug_info("sipe", "sipe_invite: added message %s to unconfirmed_messages(count=%d)\n",
4490 key
, g_hash_table_size(session
->unconfirmed_messages
));
4494 contact
= get_contact(sip
);
4495 end_points
= get_end_points(sip
, session
);
4496 self
= sip_uri_self(sip
);
4497 roster_manager
= g_strdup_printf(
4498 "Roster-Manager: %s\r\n"
4499 "EndPoints: %s\r\n",
4502 referred_by_str
= referred_by
?
4504 "Referred-By: %s\r\n",
4507 hdr
= g_strdup_printf(
4508 "Supported: ms-sender\r\n"
4514 "Content-Type: application/sdp\r\n",
4515 sipe_strequal(session
->roster_manager
, self
) ? roster_manager
: "",
4517 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
4518 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
4520 ms_text_format
? ms_text_format
: "");
4521 g_free(ms_text_format
);
4524 body
= g_strdup_printf(
4526 "o=- 0 0 IN IP4 %s\r\n"
4530 "m=%s %d sip null\r\n"
4531 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
4532 purple_network_get_my_ip(-1),
4533 purple_network_get_my_ip(-1),
4534 sip
->ocs2007
? "message" : "x-ms-message",
4537 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
4538 to
, to
, hdr
, body
, dialog
, process_invite_response
);
4541 g_free(roster_manager
);
4543 g_free(referred_by_str
);
4550 sipe_refer(struct sipe_account_data
*sip
,
4551 struct sip_session
*session
,
4556 gchar
*epid
= get_epid(sip
);
4557 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
4558 session
->roster_manager
);
4559 const char *ourtag
= dialog
&& dialog
->ourtag
? dialog
->ourtag
: NULL
;
4561 contact
= get_contact(sip
);
4562 hdr
= g_strdup_printf(
4564 "Refer-to: <%s>\r\n"
4565 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
4566 "Require: com.microsoft.rtc-multiparty\r\n",
4570 ourtag
? ";tag=" : "",
4571 ourtag
? ourtag
: "",
4575 send_sip_request(sip
->gc
, "REFER",
4576 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
4583 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
4584 struct sip_dialog
*dialog
,
4587 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4589 gchar
*body
= g_strdup_printf(
4590 "<?xml version=\"1.0\"?>\r\n"
4591 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4592 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
4593 sip
->username
, bid
);
4595 send_sip_request(sip
->gc
, "INFO",
4596 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4602 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
4603 struct sip_dialog
*dialog
)
4605 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4607 gchar
*body
= g_strdup_printf(
4608 "<?xml version=\"1.0\"?>\r\n"
4609 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4610 "<SetRM uri=\"sip:%s\"/></action>\r\n",
4613 send_sip_request(sip
->gc
, "INFO",
4614 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4620 sipe_session_close(struct sipe_account_data
*sip
,
4621 struct sip_session
* session
)
4623 if (session
&& session
->focus_uri
) {
4624 sipe_conf_immcu_closed(sip
, session
);
4625 conf_session_close(sip
, session
);
4629 SIPE_DIALOG_FOREACH
{
4630 /* @TODO slow down BYE message sending rate */
4631 /* @see single subscription code */
4632 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4633 } SIPE_DIALOG_FOREACH_END
;
4635 sipe_session_remove(sip
, session
);
4640 sipe_session_close_all(struct sipe_account_data
*sip
)
4643 while ((entry
= sip
->sessions
) != NULL
) {
4644 sipe_session_close(sip
, entry
->data
);
4649 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
4651 struct sipe_account_data
*sip
= gc
->proto_data
;
4653 purple_debug_info("sipe", "conversation with %s closed\n", who
);
4654 sipe_session_close(sip
, sipe_session_find_im(sip
, who
));
4658 sipe_chat_leave (PurpleConnection
*gc
, int id
)
4660 struct sipe_account_data
*sip
= gc
->proto_data
;
4661 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
4663 sipe_session_close(sip
, session
);
4666 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
4667 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4669 struct sipe_account_data
*sip
= gc
->proto_data
;
4670 struct sip_session
*session
;
4671 struct sip_dialog
*dialog
;
4672 gchar
*uri
= sip_uri(who
);
4674 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
4676 session
= sipe_session_find_or_add_im(sip
, uri
);
4677 dialog
= sipe_dialog_find(session
, uri
);
4679 // Queue the message
4680 sipe_session_enqueue_message(session
, what
, NULL
);
4682 if (dialog
&& !dialog
->outgoing_invite
) {
4683 sipe_im_process_queue(sip
, session
);
4684 } else if (!dialog
|| !dialog
->outgoing_invite
) {
4685 // Need to send the INVITE to get the outgoing dialog setup
4686 sipe_invite(sip
, session
, uri
, what
, NULL
, NULL
, FALSE
);
4693 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
4694 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4696 struct sipe_account_data
*sip
= gc
->proto_data
;
4697 struct sip_session
*session
;
4699 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
4701 session
= sipe_session_find_chat_by_id(sip
, id
);
4703 // Queue the message
4704 if (session
&& session
->dialogs
) {
4705 sipe_session_enqueue_message(session
,what
,NULL
);
4706 sipe_im_process_queue(sip
, session
);
4708 gchar
*chat_name
= purple_find_chat(sip
->gc
, id
)->name
;
4709 const gchar
*proto_chat_id
= sipe_chat_find_name(chat_name
);
4711 purple_debug_info("sipe", "sipe_chat_send: chat_name='%s'\n", chat_name
? chat_name
: "NULL");
4712 purple_debug_info("sipe", "sipe_chat_send: proto_chat_id='%s'\n", proto_chat_id
? proto_chat_id
: "NULL");
4715 struct sip_session
*session
= sipe_session_add_chat(sip
);
4717 session
->is_multiparty
= FALSE
;
4718 session
->focus_uri
= g_strdup(proto_chat_id
);
4719 sipe_session_enqueue_message(session
, what
, NULL
);
4720 sipe_invite_conf_focus(sip
, session
);
4727 /* End IM Session (INVITE and MESSAGE methods) */
4729 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4731 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4732 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4734 struct sip_session
*session
;
4736 purple_debug_info("sipe", "process_incoming_info: \n%s\n", msg
->body
? msg
->body
: "");
4738 /* Call Control protocol */
4739 if (g_str_has_prefix(contenttype
, "application/csta+xml"))
4741 process_incoming_info_csta(sip
, msg
);
4745 from
= parse_from(sipmsg_find_header(msg
, "From"));
4746 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4748 session
= sipe_session_find_im(sip
, from
);
4755 if (g_str_has_prefix(contenttype
, "application/x-ms-mim"))
4757 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4758 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
4759 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
4761 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
4763 if (xn_request_rm
) {
4764 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
4765 int bid
= xmlnode_get_int_attrib(xn_request_rm
, "bid", 0);
4766 gchar
*body
= g_strdup_printf(
4767 "<?xml version=\"1.0\"?>\r\n"
4768 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4769 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
4771 session
->bid
< bid
? "true" : "false");
4772 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4774 } else if (xn_set_rm
) {
4776 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
4777 g_free(session
->roster_manager
);
4778 session
->roster_manager
= g_strdup(rm
);
4780 body
= g_strdup_printf(
4781 "<?xml version=\"1.0\"?>\r\n"
4782 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4783 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
4785 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4788 xmlnode_free(xn_action
);
4793 /* looks like purple lacks typing notification for chat */
4794 if (!session
->is_multiparty
&& !session
->focus_uri
) {
4795 xmlnode
*xn_keyboard_activity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4796 const char *status
= xmlnode_get_attrib(xmlnode_get_child(xn_keyboard_activity
, "status"),
4798 if (sipe_strequal(status
, "type")) {
4799 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
4800 } else if (sipe_strequal(status
, "idle")) {
4801 serv_got_typing_stopped(sip
->gc
, from
);
4803 xmlnode_free(xn_keyboard_activity
);
4806 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4811 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4813 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4814 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4815 struct sip_session
*session
;
4816 struct sip_dialog
*dialog
;
4818 /* collect dialog identification
4819 * we need callid, ourtag and theirtag to unambiguously identify dialog
4821 /* take data before 'msg' will be modified by send_sip_response */
4822 dialog
= g_new0(struct sip_dialog
, 1);
4823 dialog
->callid
= g_strdup(callid
);
4824 dialog
->cseq
= parse_cseq(sipmsg_find_header(msg
, "CSeq"));
4825 dialog
->with
= g_strdup(from
);
4826 sipe_dialog_parse(dialog
, msg
, FALSE
);
4828 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4830 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4832 session
= sipe_session_find_im(sip
, from
);
4835 sipe_dialog_free(dialog
);
4840 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
4841 g_free(session
->roster_manager
);
4842 session
->roster_manager
= NULL
;
4845 /* This what BYE is essentially for - terminating dialog */
4846 sipe_dialog_remove_3(session
, dialog
);
4847 sipe_dialog_free(dialog
);
4848 if (session
->focus_uri
&& !g_strcasecmp(from
, session
->im_mcu_uri
)) {
4849 sipe_conf_immcu_closed(sip
, session
);
4850 } else if (session
->is_multiparty
) {
4851 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
4857 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4859 gchar
*self
= sip_uri_self(sip
);
4860 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4861 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4862 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
4863 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
4864 struct sip_session
*session
;
4865 struct sip_dialog
*dialog
;
4867 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4868 dialog
= sipe_dialog_find(session
, from
);
4870 if (!session
|| !dialog
|| !session
->roster_manager
|| !sipe_strequal(session
->roster_manager
, self
)) {
4871 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
4873 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
4875 sipe_invite(sip
, session
, refer_to
, NULL
, NULL
, referred_by
, FALSE
);
4881 g_free(referred_by
);
4885 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
4887 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
4888 struct sip_session
*session
;
4889 struct sip_dialog
*dialog
;
4891 if (state
== PURPLE_NOT_TYPING
)
4894 session
= sipe_session_find_im(sip
, who
);
4895 dialog
= sipe_dialog_find(session
, who
);
4897 if (session
&& dialog
&& dialog
->is_established
) {
4898 send_sip_request(gc
, "INFO", who
, who
,
4899 "Content-Type: application/xml\r\n",
4900 SIPE_SEND_TYPING
, dialog
, NULL
);
4902 return SIPE_TYPING_SEND_TIMEOUT
;
4905 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
4907 GSList
*tmp
= sip
->transactions
;
4908 time_t currtime
= time(NULL
);
4910 struct transaction
*trans
= tmp
->data
;
4912 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
4913 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
4916 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
4918 sendout_sipmsg(sip
, trans
->msg
);
4925 static void do_reauthenticate_cb(struct sipe_account_data
*sip
,
4926 SIPE_UNUSED_PARAMETER
void *unused
)
4928 /* register again when security token expires */
4929 /* we have to start a new authentication as the security token
4930 * is almost expired by sending a not signed REGISTER message */
4931 purple_debug_info("sipe", "do a full reauthentication\n");
4932 sipe_auth_free(&sip
->registrar
);
4933 sipe_auth_free(&sip
->proxy
);
4934 sip
->registerstatus
= 0;
4936 sip
->reauthenticate_set
= FALSE
;
4940 sipe_process_incoming_x_msmsgsinvite(struct sipe_account_data
*sip
,
4942 GSList
*parsed_body
)
4944 gboolean found
= FALSE
;
4947 const gchar
*invitation_command
= sipe_utils_nameval_find(parsed_body
, "Invitation-Command");
4949 if (sipe_strequal(invitation_command
, "INVITE")) {
4950 sipe_ft_incoming_transfer(sip
->gc
->account
, msg
, parsed_body
);
4952 } else if (sipe_strequal(invitation_command
, "CANCEL")) {
4953 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4955 } else if (sipe_strequal(invitation_command
, "ACCEPT")) {
4956 sipe_ft_incoming_accept(sip
->gc
->account
, parsed_body
);
4963 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4966 const gchar
*contenttype
;
4967 gboolean found
= FALSE
;
4969 from
= parse_from(sipmsg_find_header(msg
, "From"));
4973 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
4975 contenttype
= sipmsg_find_header(msg
, "Content-Type");
4976 if (g_str_has_prefix(contenttype
, "text/plain")
4977 || g_str_has_prefix(contenttype
, "text/html")
4978 || g_str_has_prefix(contenttype
, "multipart/related")
4979 || g_str_has_prefix(contenttype
, "multipart/alternative"))
4981 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4982 gchar
*html
= get_html_message(contenttype
, msg
->body
);
4984 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
4986 session
= sipe_session_find_im(sip
, from
);
4989 if (session
&& session
->focus_uri
) { /* a conference */
4990 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
4991 gchar
*sender
= parse_from(tmp
);
4993 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
4994 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4996 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
4997 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
4998 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5000 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
5003 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5006 } else if (g_str_has_prefix(contenttype
, "application/im-iscomposing+xml")) {
5007 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5012 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
5017 state
= xmlnode_get_child(isc
, "state");
5020 purple_debug_info("sipe", "process_incoming_message: no state found\n");
5026 statedata
= xmlnode_get_data(state
);
5028 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
5029 else serv_got_typing_stopped(sip
->gc
, from
);
5034 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5036 } else if (g_str_has_prefix(contenttype
, "text/x-msmsgsinvite")) {
5037 GSList
*body
= sipe_ft_parse_msg_body(msg
->body
);
5038 found
= sipe_process_incoming_x_msmsgsinvite(sip
, msg
, body
);
5039 sipe_utils_nameval_free(body
);
5041 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5045 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
5046 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
5048 session
= sipe_session_find_im(sip
, from
);
5051 gchar
*errmsg
= g_strdup_printf(_("Received a message with unrecognized contents from %s"),
5053 sipe_present_err(sip
, session
, errmsg
);
5057 purple_debug_info("sipe", "got unknown mime-type '%s'\n", contenttype
);
5058 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
5063 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5067 const gchar
*oldHeader
;
5069 gboolean is_multiparty
= FALSE
;
5070 gboolean is_triggered
= FALSE
;
5071 gboolean was_multiparty
= TRUE
;
5072 gboolean just_joined
= FALSE
;
5074 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
5075 const gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
5076 const gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
5077 const gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
5078 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
5079 GSList
*end_points
= NULL
;
5081 struct sip_session
*session
;
5082 const gchar
*ms_text_format
;
5084 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? tmp
= fix_newlines(msg
->body
) : "");
5087 /* Invitation to join conference */
5088 if (g_str_has_prefix(content_type
, "application/ms-conf-invite+xml")) {
5089 process_incoming_invite_conf(sip
, msg
);
5093 /* Only accept text invitations */
5094 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
5095 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
5099 // TODO There *must* be a better way to clean up the To header to add a tag...
5100 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
5101 oldHeader
= sipmsg_find_header(msg
, "To");
5103 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
5104 sipmsg_remove_header_now(msg
, "To");
5105 sipmsg_add_header_now(msg
, "To", newHeader
);
5108 if (end_points_hdr
) {
5109 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
5111 if (g_slist_length(end_points
) > 2) {
5112 is_multiparty
= TRUE
;
5115 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
5116 is_triggered
= TRUE
;
5117 is_multiparty
= TRUE
;
5120 session
= sipe_session_find_chat_by_callid(sip
, callid
);
5121 /* Convert to multiparty */
5122 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
5123 g_free(session
->with
);
5124 session
->with
= NULL
;
5125 was_multiparty
= FALSE
;
5126 session
->is_multiparty
= TRUE
;
5127 session
->chat_id
= rand();
5130 if (!session
&& is_multiparty
) {
5131 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
5134 from
= parse_from(sipmsg_find_header(msg
, "From"));
5136 session
= sipe_session_find_or_add_im(sip
, from
);
5140 g_free(session
->callid
);
5141 session
->callid
= g_strdup(callid
);
5143 session
->is_multiparty
= is_multiparty
;
5144 if (roster_manager
) {
5145 session
->roster_manager
= g_strdup(roster_manager
);
5149 if (is_multiparty
&& end_points
) {
5150 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
5151 GSList
*entry
= end_points
;
5153 struct sip_dialog
*dialog
;
5154 struct sipendpoint
*end_point
= entry
->data
;
5155 entry
= entry
->next
;
5157 if (!g_strcasecmp(from
, end_point
->contact
) ||
5158 !g_strcasecmp(to
, end_point
->contact
))
5161 dialog
= sipe_dialog_find(session
, end_point
->contact
);
5163 g_free(dialog
->theirepid
);
5164 dialog
->theirepid
= end_point
->epid
;
5165 end_point
->epid
= NULL
;
5167 dialog
= sipe_dialog_add(session
);
5169 dialog
->callid
= g_strdup(session
->callid
);
5170 dialog
->with
= end_point
->contact
;
5171 end_point
->contact
= NULL
;
5172 dialog
->theirepid
= end_point
->epid
;
5173 end_point
->epid
= NULL
;
5177 /* send triggered INVITE */
5178 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, NULL
, TRUE
);
5185 GSList
*entry
= end_points
;
5187 struct sipendpoint
*end_point
= entry
->data
;
5188 entry
= entry
->next
;
5189 g_free(end_point
->contact
);
5190 g_free(end_point
->epid
);
5193 g_slist_free(end_points
);
5197 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
5199 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
5200 sipe_dialog_parse_routes(dialog
, msg
, FALSE
);
5202 dialog
= sipe_dialog_add(session
);
5204 dialog
->callid
= g_strdup(session
->callid
);
5205 dialog
->with
= g_strdup(from
);
5206 sipe_dialog_parse(dialog
, msg
, FALSE
);
5208 if (!dialog
->ourtag
) {
5209 dialog
->ourtag
= newTag
;
5216 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
5220 if (is_multiparty
&& !session
->conv
) {
5221 gchar
*chat_title
= sipe_chat_get_name(callid
);
5222 gchar
*self
= sip_uri_self(sip
);
5223 /* create prpl chat */
5224 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_title
);
5225 session
->chat_title
= g_strdup(chat_title
);
5226 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
5228 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5230 PURPLE_CBFLAGS_NONE
, FALSE
);
5235 if (is_multiparty
&& !was_multiparty
) {
5236 /* add current IM counterparty to chat */
5237 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5238 sipe_dialog_first(session
)->with
, NULL
,
5239 PURPLE_CBFLAGS_NONE
, FALSE
);
5242 /* add inviting party to chat */
5243 if (just_joined
&& session
->conv
) {
5244 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5246 PURPLE_CBFLAGS_NONE
, TRUE
);
5249 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
5251 /* This used only in 2005 official client, not 2007 or Reuters.
5252 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
5253 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
5255 /* also enabled for 2005 file transfer. Didn't work otherwise. */
5256 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
5257 if (is_multiparty
||
5258 (ms_text_format
&& g_str_has_prefix(ms_text_format
, "text/x-msmsgsinvite")) )
5260 if (ms_text_format
) {
5261 if (g_str_has_prefix(ms_text_format
, "text/x-msmsgsinvite"))
5263 gchar
*tmp
= sipmsg_find_part_of_header(ms_text_format
, "ms-body=", NULL
, NULL
);
5265 gchar
*body
= (gchar
*) purple_base64_decode(tmp
, NULL
);
5267 GSList
*parsed_body
= sipe_ft_parse_msg_body(body
);
5269 sipe_process_incoming_x_msmsgsinvite(sip
, msg
, parsed_body
);
5270 sipe_utils_nameval_free(parsed_body
);
5271 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5275 else if (g_str_has_prefix(ms_text_format
, "text/plain") || g_str_has_prefix(ms_text_format
, "text/html"))
5277 /* please do not optimize logic inside as this code may be re-enabled for other cases */
5278 gchar
*html
= get_html_message(ms_text_format
, NULL
);
5280 if (is_multiparty
) {
5281 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
5282 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5284 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
5287 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5295 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
5296 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5297 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5299 body
= g_strdup_printf(
5301 "o=- 0 0 IN IP4 %s\r\n"
5305 "m=%s %d sip sip:%s\r\n"
5306 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
5307 purple_network_get_my_ip(-1),
5308 purple_network_get_my_ip(-1),
5309 sip
->ocs2007
? "message" : "x-ms-message",
5312 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5316 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5320 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
5321 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5322 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5324 body
= g_strdup_printf(
5326 "o=- 0 0 IN IP4 0.0.0.0\r\n"
5328 "c=IN IP4 0.0.0.0\r\n"
5330 "m=%s %d sip sip:%s\r\n"
5331 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
5332 sip
->ocs2007
? "message" : "x-ms-message",
5335 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5340 sipe_get_auth_scheme_name(struct sipe_account_data
*sip
)
5342 const char *res
= "NTLM";
5344 if (purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5348 (void) sip
; /* make compiler happy */
5353 static void sipe_connection_cleanup(struct sipe_account_data
*);
5354 static void create_connection(struct sipe_account_data
*, gchar
*, int);
5356 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
5357 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5360 const gchar
*expires_header
;
5362 GSList
*hdr
= msg
->headers
;
5363 struct sipnameval
*elem
;
5365 expires_header
= sipmsg_find_header(msg
, "Expires");
5366 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
5367 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
5369 switch (msg
->response
) {
5372 sip
->registerstatus
= 0;
5374 const gchar
*contact_hdr
;
5379 const gchar
*server_hdr
= sipmsg_find_header(msg
, "Server");
5380 const char *auth_scheme
;
5382 if (!sip
->reregister_set
) {
5383 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
5384 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
5385 g_free(action_name
);
5386 sip
->reregister_set
= TRUE
;
5389 sip
->registerstatus
= 3;
5391 if (server_hdr
&& !sip
->server_version
) {
5392 sip
->server_version
= g_strdup(server_hdr
);
5397 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5398 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5401 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
5402 fill_auth(tmp
, &sip
->registrar
);
5405 if (!sip
->reauthenticate_set
) {
5406 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
5407 guint reauth_timeout
;
5408 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
5409 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
5410 reauth_timeout
= sip
->registrar
.expires
- 300;
5412 /* NTLM: we have to reauthenticate as our security token expires
5413 after eight hours (be five minutes early) */
5414 reauth_timeout
= (8 * 3600) - 300;
5416 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
5417 g_free(action_name
);
5418 sip
->reauthenticate_set
= TRUE
;
5421 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
5423 epid
= get_epid(sip
);
5424 uuid
= generateUUIDfromEPID(epid
);
5427 // There can be multiple Contact headers (one per location where the user is logged in) so
5428 // make sure to only get the one for this uuid
5429 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
5430 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
5431 if (valid_contact
) {
5432 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
5433 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
5434 g_free(valid_contact
);
5437 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
5442 g_free(sip
->contact
);
5444 sip
->contact
= g_strdup_printf("<%s>", gruu
);
5447 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
5448 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
);
5450 sip
->ocs2007
= FALSE
;
5451 sip
->batched_support
= FALSE
;
5456 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
5457 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
5458 /* We interpret this as OCS2007+ indicator */
5459 sip
->ocs2007
= TRUE
;
5460 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s (indicates OCS2007+)\n", elem
->value
);
5462 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
5463 sip
->batched_support
= TRUE
;
5464 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s\n", elem
->value
);
5467 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
5468 gchar
**caps
= g_strsplit(elem
->value
,",",0);
5471 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
5472 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
5477 hdr
= g_slist_next(hdr
);
5480 /* rejoin open chats to be able to use them by continue to send messages */
5481 purple_conversation_foreach(sipe_rejoin_chat
);
5484 if (!sip
->subscribed
) { //do it just once, not every re-register
5486 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
5487 (GCompareFunc
)g_ascii_strcasecmp
)) {
5488 sipe_subscribe_roaming_contacts(sip
);
5491 /* For 2007+ it does not make sence to subscribe to:
5492 * vnd-microsoft-roaming-ACL
5493 * vnd-microsoft-provisioning (not v2)
5495 * These are for backward compatibility.
5499 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
5500 (GCompareFunc
)g_ascii_strcasecmp
)) {
5501 sipe_subscribe_roaming_self(sip
);
5503 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
5504 (GCompareFunc
)g_ascii_strcasecmp
)) {
5505 sipe_subscribe_roaming_provisioning_v2(sip
);
5508 /* For 2005- servers */
5511 //sipe_options_request(sip, sip->sipdomain);
5513 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
5514 (GCompareFunc
)g_ascii_strcasecmp
)) {
5515 sipe_subscribe_roaming_acl(sip
);
5517 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
5518 (GCompareFunc
)g_ascii_strcasecmp
)) {
5519 sipe_subscribe_roaming_provisioning(sip
);
5521 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
5522 (GCompareFunc
)g_ascii_strcasecmp
)) {
5523 sipe_subscribe_presence_wpending(sip
, msg
);
5526 /* For 2007+ we publish our initial statuses and calendar data only after
5527 * received our existing publications in sipe_process_roaming_self()
5528 * Only in this case we know versions of current publications made
5531 /* For 2005- we publish our initial statuses only after
5532 * received our existing UserInfo data in response to
5533 * self subscription.
5534 * Only in this case we won't override existing UserInfo data
5535 * set earlier or by other client on our behalf.
5539 sip
->subscribed
= TRUE
;
5542 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
5543 "timeout=", ";", NULL
);
5544 if (timeout
!= NULL
) {
5545 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
5546 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
5547 sip
->keepalive_timeout
);
5551 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\n", sip
->cseq
);
5556 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
5558 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
5559 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
5563 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
5566 tmp
= g_strsplit(parts
[0], ":", 0);
5567 hostname
= g_strdup(tmp
[0]);
5568 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
5572 tmp
= g_strsplit(parts
[i
], "=", 0);
5574 if (g_strcasecmp("transport", tmp
[0]) == 0) {
5575 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
5576 transport
= SIPE_TRANSPORT_TCP
;
5577 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
5578 transport
= SIPE_TRANSPORT_UDP
;
5587 /* Close old connection */
5588 sipe_connection_cleanup(sip
);
5590 /* Create new connection */
5591 sip
->transport
= transport
;
5592 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
5593 hostname
, port
, TRANSPORT_DESCRIPTOR
);
5594 create_connection(sip
, hostname
, port
);
5600 if (sip
->registerstatus
!= 2) {
5601 const char *auth_scheme
;
5602 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
5603 if (sip
->registrar
.retries
> 3) {
5604 sip
->gc
->wants_to_die
= TRUE
;
5605 purple_connection_error(sip
->gc
, _("Wrong password"));
5609 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5610 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5612 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
? tmp
: "");
5614 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
5615 sip
->gc
->wants_to_die
= TRUE
;
5616 purple_connection_error(sip
->gc
, tmp2
);
5620 fill_auth(tmp
, &sip
->registrar
);
5621 sip
->registerstatus
= 2;
5622 if (sip
->account
->disconnecting
) {
5623 do_register_exp(sip
, 0);
5631 const gchar
*diagnostics
= sipmsg_find_header(msg
, "Warning");
5632 gchar
**reason
= NULL
;
5634 if (diagnostics
!= NULL
) {
5636 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
5638 reason
= g_strsplit(diagnostics
, "\"", 0);
5640 warning
= g_strdup_printf(_("You have been rejected by the server: %s"),
5641 (reason
&& reason
[1]) ? reason
[1] : _("no reason given"));
5644 sip
->gc
->wants_to_die
= TRUE
;
5645 purple_connection_error(sip
->gc
, warning
);
5652 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
5653 gchar
*reason
= NULL
;
5655 if (diagnostics
!= NULL
) {
5656 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
5658 warning
= g_strdup_printf(_("Not found: %s. Please contact your Administrator"),
5659 diagnostics
? (reason
? reason
: _("no reason given")) :
5660 _("SIP is either not enabled for the destination URI or it does not exist"));
5663 sip
->gc
->wants_to_die
= TRUE
;
5664 purple_connection_error(sip
->gc
, warning
);
5670 case 504: /* Server time-out */
5672 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
5673 gchar
*reason
= NULL
;
5675 if (diagnostics
!= NULL
) {
5676 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
5678 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
5681 sip
->gc
->wants_to_die
= TRUE
;
5682 purple_connection_error(sip
->gc
, warning
);
5692 * Returns 2005-style activity and Availability.
5694 * @param status Sipe statis id.
5697 sipe_get_act_avail_by_status_2005(const char *status
,
5701 int avail
= 300; /* online */
5702 int act
= 400; /* Available */
5704 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
5706 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
5708 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
5710 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
5712 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
5714 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
5715 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
5717 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
5718 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
5719 avail
= 0; /* offline */
5722 act
= 400; /* Available */
5725 if (activity
) *activity
= act
;
5726 if (availability
) *availability
= avail
;
5732 * @param activity 2005 aggregated activity. Ex.: 600
5733 * @param availablity 2005 aggregated availablity. Ex.: 300
5736 sipe_get_status_by_act_avail_2005(const int activity
,
5737 const int availablity
,
5738 char **activity_desc
)
5740 const char *status_id
= NULL
;
5741 const char *act
= NULL
;
5743 if (activity
< 150) {
5744 status_id
= SIPE_STATUS_ID_AWAY
;
5745 } else if (activity
< 200) {
5746 //status_id = SIPE_STATUS_ID_LUNCH;
5747 status_id
= SIPE_STATUS_ID_AWAY
;
5748 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
5749 } else if (activity
< 300) {
5750 //status_id = SIPE_STATUS_ID_IDLE;
5751 status_id
= SIPE_STATUS_ID_AWAY
;
5752 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5753 } else if (activity
< 400) {
5754 status_id
= SIPE_STATUS_ID_BRB
;
5755 } else if (activity
< 500) {
5756 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5757 } else if (activity
< 600) {
5758 //status_id = SIPE_STATUS_ID_ON_PHONE;
5759 status_id
= SIPE_STATUS_ID_BUSY
;
5760 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
5761 } else if (activity
< 700) {
5762 status_id
= SIPE_STATUS_ID_BUSY
;
5763 } else if (activity
< 800) {
5764 status_id
= SIPE_STATUS_ID_AWAY
;
5766 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5769 if (availablity
< 100)
5770 status_id
= SIPE_STATUS_ID_OFFLINE
;
5772 if (activity_desc
&& act
) {
5773 g_free(*activity_desc
);
5774 *activity_desc
= g_strdup(act
);
5781 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
5784 sipe_get_status_by_availability(int avail
,
5785 char** activity_desc
)
5788 const char *act
= NULL
;
5791 status
= SIPE_STATUS_ID_OFFLINE
;
5792 } else if (avail
< 4500) {
5793 status
= SIPE_STATUS_ID_AVAILABLE
;
5794 } else if (avail
< 6000) {
5795 //status = SIPE_STATUS_ID_IDLE;
5796 status
= SIPE_STATUS_ID_AVAILABLE
;
5797 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5798 } else if (avail
< 7500) {
5799 status
= SIPE_STATUS_ID_BUSY
;
5800 } else if (avail
< 9000) {
5801 //status = SIPE_STATUS_ID_BUSYIDLE;
5802 status
= SIPE_STATUS_ID_BUSY
;
5803 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
5804 } else if (avail
< 12000) {
5805 status
= SIPE_STATUS_ID_DND
;
5806 } else if (avail
< 15000) {
5807 status
= SIPE_STATUS_ID_BRB
;
5808 } else if (avail
< 18000) {
5809 status
= SIPE_STATUS_ID_AWAY
;
5811 status
= SIPE_STATUS_ID_OFFLINE
;
5814 if (activity_desc
&& act
) {
5815 g_free(*activity_desc
);
5816 *activity_desc
= g_strdup(act
);
5823 * Returns 2007-style availability value
5825 * @param sipe_status_id (in)
5826 * @param activity_token (out) Must be g_free()'d after use if consumed.
5829 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
5832 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
5834 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
5835 availability
= 15500;
5836 if (!activity_token
|| !(*activity_token
)) {
5837 activity
= SIPE_ACTIVITY_AWAY
;
5839 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
5840 availability
= 12500;
5841 activity
= SIPE_ACTIVITY_BRB
;
5842 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
5843 availability
= 9500;
5844 activity
= SIPE_ACTIVITY_DND
;
5845 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
5846 availability
= 6500;
5847 if (!activity_token
|| !(*activity_token
)) {
5848 activity
= SIPE_ACTIVITY_BUSY
;
5850 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
5851 availability
= 3500;
5852 activity
= SIPE_ACTIVITY_ONLINE
;
5853 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
5856 // Offline or invisible
5857 availability
= 18500;
5858 activity
= SIPE_ACTIVITY_OFFLINE
;
5861 if (activity_token
) {
5862 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
5864 return availability
;
5867 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5870 xmlnode
*xn_categories
;
5871 xmlnode
*xn_category
;
5873 const char *status
= NULL
;
5874 gboolean do_update_status
= FALSE
;
5875 gboolean has_note_cleaned
= FALSE
;
5876 gboolean has_free_busy_cleaned
= FALSE
;
5878 xn_categories
= xmlnode_from_str(data
, len
);
5879 uri
= xmlnode_get_attrib(xn_categories
, "uri"); /* with 'sip:' prefix */
5881 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
5883 xn_category
= xmlnode_get_next_twin(xn_category
) )
5886 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
5887 time_t publish_time
= (tmp
= xmlnode_get_attrib(xn_category
, "publishTime")) ?
5888 sipe_utils_str_to_time(tmp
) : 0;
5891 if (sipe_strequal(attrVar
, "contactCard"))
5894 /* identity - Display Name and email */
5895 node
= xmlnode_get_descendant(xn_category
, "contactCard", "identity", NULL
);
5897 char* display_name
= xmlnode_get_data(
5898 xmlnode_get_descendant(node
, "name", "displayName", NULL
));
5899 char* email
= xmlnode_get_data(
5900 xmlnode_get_child(node
, "email"));
5902 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5903 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
5905 g_free(display_name
);
5909 node
= xmlnode_get_descendant(xn_category
, "contactCard", "company", NULL
);
5911 char* company
= xmlnode_get_data(node
);
5912 sipe_update_user_info(sip
, uri
, COMPANY_PROP
, company
);
5916 node
= xmlnode_get_descendant(xn_category
, "contactCard", "department", NULL
);
5918 char* department
= xmlnode_get_data(node
);
5919 sipe_update_user_info(sip
, uri
, DEPARTMENT_PROP
, department
);
5923 node
= xmlnode_get_descendant(xn_category
, "contactCard", "title", NULL
);
5925 char* title
= xmlnode_get_data(node
);
5926 sipe_update_user_info(sip
, uri
, TITLE_PROP
, title
);
5930 node
= xmlnode_get_descendant(xn_category
, "contactCard", "office", NULL
);
5932 char* office
= xmlnode_get_data(node
);
5933 sipe_update_user_info(sip
, uri
, OFFICE_PROP
, office
);
5937 node
= xmlnode_get_descendant(xn_category
, "contactCard", "url", NULL
);
5939 char* site
= xmlnode_get_data(node
);
5940 sipe_update_user_info(sip
, uri
, SITE_PROP
, site
);
5944 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "phone", NULL
);
5946 node
= xmlnode_get_next_twin(node
))
5948 const char *phone_type
= xmlnode_get_attrib(node
, "type");
5949 char* phone
= xmlnode_get_data(xmlnode_get_child(node
, "uri"));
5950 char* phone_display_string
= xmlnode_get_data(xmlnode_get_child(node
, "displayString"));
5952 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, phone_display_string
);
5955 g_free(phone_display_string
);
5958 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "address", NULL
);
5960 node
= xmlnode_get_next_twin(node
))
5962 if (sipe_strequal(xmlnode_get_attrib(node
, "type"), "work")) {
5963 char* street
= xmlnode_get_data(xmlnode_get_child(node
, "street"));
5964 char* city
= xmlnode_get_data(xmlnode_get_child(node
, "city"));
5965 char* state
= xmlnode_get_data(xmlnode_get_child(node
, "state"));
5966 char* zipcode
= xmlnode_get_data(xmlnode_get_child(node
, "zipcode"));
5967 char* country_code
= xmlnode_get_data(xmlnode_get_child(node
, "countryCode"));
5969 sipe_update_user_info(sip
, uri
, ADDRESS_STREET_PROP
, street
);
5970 sipe_update_user_info(sip
, uri
, ADDRESS_CITY_PROP
, city
);
5971 sipe_update_user_info(sip
, uri
, ADDRESS_STATE_PROP
, state
);
5972 sipe_update_user_info(sip
, uri
, ADDRESS_ZIPCODE_PROP
, zipcode
);
5973 sipe_update_user_info(sip
, uri
, ADDRESS_COUNTRYCODE_PROP
, country_code
);
5979 g_free(country_code
);
5986 else if (sipe_strequal(attrVar
, "note"))
5989 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
5991 if (!has_note_cleaned
) {
5992 has_note_cleaned
= TRUE
;
5994 g_free(sbuddy
->note
);
5995 sbuddy
->note
= NULL
;
5996 sbuddy
->is_oof_note
= FALSE
;
5997 sbuddy
->note_since
= publish_time
;
5999 do_update_status
= TRUE
;
6001 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
6002 /* clean up in case no 'note' element is supplied
6003 * which indicate note removal in client
6005 g_free(sbuddy
->note
);
6006 sbuddy
->note
= NULL
;
6007 sbuddy
->is_oof_note
= FALSE
;
6008 sbuddy
->note_since
= publish_time
;
6010 xn_node
= xmlnode_get_descendant(xn_category
, "note", "body", NULL
);
6013 sbuddy
->note
= g_markup_escape_text((tmp
= xmlnode_get_data(xn_node
)), -1);
6015 sbuddy
->is_oof_note
= sipe_strequal(xmlnode_get_attrib(xn_node
, "type"), "OOF");
6016 sbuddy
->note_since
= publish_time
;
6018 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s), note(%s)\n",
6019 uri
, sbuddy
->note
? sbuddy
->note
: "");
6021 /* to trigger UI refresh in case no status info is supplied in this update */
6022 do_update_status
= TRUE
;
6027 else if(sipe_strequal(attrVar
, "state"))
6031 xmlnode
*xn_availability
;
6032 xmlnode
*xn_activity
;
6033 xmlnode
*xn_meeting_subject
;
6034 xmlnode
*xn_meeting_location
;
6035 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
6037 xn_node
= xmlnode_get_child(xn_category
, "state");
6038 if (!xn_node
) continue;
6039 xn_availability
= xmlnode_get_child(xn_node
, "availability");
6040 if (!xn_availability
) continue;
6041 xn_activity
= xmlnode_get_child(xn_node
, "activity");
6042 xn_meeting_subject
= xmlnode_get_child(xn_node
, "meetingSubject");
6043 xn_meeting_location
= xmlnode_get_child(xn_node
, "meetingLocation");
6045 tmp
= xmlnode_get_data(xn_availability
);
6046 availability
= atoi(tmp
);
6049 /* activity, meeting_subject, meeting_location */
6054 g_free(sbuddy
->activity
);
6055 sbuddy
->activity
= NULL
;
6057 const char *token
= xmlnode_get_attrib(xn_activity
, "token");
6058 xmlnode
*xn_custom
= xmlnode_get_child(xn_activity
, "custom");
6061 if (!is_empty(token
)) {
6062 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
6064 /* from custom element */
6066 char *custom
= xmlnode_get_data(xn_custom
);
6068 if (!is_empty(custom
)) {
6069 sbuddy
->activity
= custom
;
6075 /* meeting_subject */
6076 g_free(sbuddy
->meeting_subject
);
6077 sbuddy
->meeting_subject
= NULL
;
6078 if (xn_meeting_subject
) {
6079 char *meeting_subject
= xmlnode_get_data(xn_meeting_subject
);
6081 if (!is_empty(meeting_subject
)) {
6082 sbuddy
->meeting_subject
= meeting_subject
;
6083 meeting_subject
= NULL
;
6085 g_free(meeting_subject
);
6087 /* meeting_location */
6088 g_free(sbuddy
->meeting_location
);
6089 sbuddy
->meeting_location
= NULL
;
6090 if (xn_meeting_location
) {
6091 char *meeting_location
= xmlnode_get_data(xn_meeting_location
);
6093 if (!is_empty(meeting_location
)) {
6094 sbuddy
->meeting_location
= meeting_location
;
6095 meeting_location
= NULL
;
6097 g_free(meeting_location
);
6100 status
= sipe_get_status_by_availability(availability
, &tmp
);
6101 if (sbuddy
->activity
&& tmp
) {
6102 char *tmp2
= sbuddy
->activity
;
6104 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
6108 sbuddy
->activity
= tmp
;
6112 do_update_status
= TRUE
;
6115 else if(sipe_strequal(attrVar
, "calendarData"))
6117 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
6118 xmlnode
*xn_free_busy
= xmlnode_get_descendant(xn_category
, "calendarData", "freeBusy", NULL
);
6119 xmlnode
*xn_working_hours
= xmlnode_get_descendant(xn_category
, "calendarData", "WorkingHours", NULL
);
6121 if (sbuddy
&& xn_free_busy
) {
6122 if (!has_free_busy_cleaned
) {
6123 has_free_busy_cleaned
= TRUE
;
6125 g_free(sbuddy
->cal_start_time
);
6126 sbuddy
->cal_start_time
= NULL
;
6128 g_free(sbuddy
->cal_free_busy_base64
);
6129 sbuddy
->cal_free_busy_base64
= NULL
;
6131 g_free(sbuddy
->cal_free_busy
);
6132 sbuddy
->cal_free_busy
= NULL
;
6134 sbuddy
->cal_free_busy_published
= publish_time
;
6137 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
6138 g_free(sbuddy
->cal_start_time
);
6139 sbuddy
->cal_start_time
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
6141 sbuddy
->cal_granularity
= !g_ascii_strcasecmp(xmlnode_get_attrib(xn_free_busy
, "granularity"), "PT15M") ?
6144 g_free(sbuddy
->cal_free_busy_base64
);
6145 sbuddy
->cal_free_busy_base64
= xmlnode_get_data(xn_free_busy
);
6147 g_free(sbuddy
->cal_free_busy
);
6148 sbuddy
->cal_free_busy
= NULL
;
6150 sbuddy
->cal_free_busy_published
= publish_time
;
6152 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
);
6156 if (sbuddy
&& xn_working_hours
) {
6157 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
6162 if (do_update_status
) {
6163 if (!status
) { /* no status category in this update, using contact's current status */
6164 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
6165 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
6166 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
6167 status
= purple_status_get_id(pstatus
);
6170 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", status
);
6171 sipe_got_user_status(sip
, uri
, status
);
6174 xmlnode_free(xn_categories
);
6177 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
6179 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6180 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
6181 payload
->host
= g_strdup(host
);
6182 payload
->buddies
= server
;
6183 sipe_subscribe_presence_batched_routed(sip
, payload
);
6184 sipe_subscribe_presence_batched_routed_free(payload
);
6187 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6190 xmlnode
*xn_resource
;
6191 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
6196 xn_list
= xmlnode_from_str(data
, len
);
6198 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
6200 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
6202 const char *uri
, *state
;
6203 xmlnode
*xn_instance
;
6205 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
6206 if (!xn_instance
) continue;
6208 uri
= xmlnode_get_attrib(xn_resource
, "uri");
6209 state
= xmlnode_get_attrib(xn_instance
, "state");
6210 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
6212 if (strstr(state
, "resubscribe")) {
6213 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
6215 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
6216 gchar
*user
= g_strdup(uri
);
6217 host
= g_strdup(poolFqdn
);
6218 server
= g_hash_table_lookup(servers
, host
);
6219 server
= g_slist_append(server
, user
);
6220 g_hash_table_insert(servers
, host
, server
);
6222 sipe_subscribe_presence_single(sip
, (void *) uri
);
6227 /* Send out any deferred poolFqdn subscriptions */
6228 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
6229 g_hash_table_destroy(servers
);
6231 xmlnode_free(xn_list
);
6234 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6238 gchar
*activity
= NULL
;
6240 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
6241 gboolean isonline
= FALSE
;
6242 xmlnode
*display_name_node
;
6244 pidf
= xmlnode_from_str(data
, len
);
6246 purple_debug_info("sipe", "process_incoming_notify_pidf: no parseable pidf:%s\n",data
);
6250 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
6252 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6253 basicstatus
= xmlnode_get_child(status
, "basic");
6258 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic found\n");
6263 getbasic
= xmlnode_get_data(basicstatus
);
6265 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic data found\n");
6270 purple_debug_info("sipe", "process_incoming_notify_pidf: basic-status(%s)\n", getbasic
);
6271 if (strstr(getbasic
, "open")) {
6276 uri
= sip_uri(xmlnode_get_attrib(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
6278 display_name_node
= xmlnode_get_child(pidf
, "display-name");
6279 if (display_name_node
) {
6280 char * display_name
= xmlnode_get_data(display_name_node
);
6282 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6283 g_free(display_name
);
6286 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
6287 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6288 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
6289 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
6290 activity
= xmlnode_get_data(basicstatus
);
6291 purple_debug_info("sipe", "process_incoming_notify_pidf: activity(%s)\n", activity
);
6298 const gchar
* status_id
= NULL
;
6300 if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
6301 status_id
= SIPE_STATUS_ID_BUSY
;
6302 } else if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
6303 status_id
= SIPE_STATUS_ID_AWAY
;
6308 status_id
= SIPE_STATUS_ID_AVAILABLE
;
6311 purple_debug_info("sipe", "process_incoming_notify_pidf: status_id(%s)\n", status_id
);
6312 sipe_got_user_status(sip
, uri
, status_id
);
6314 sipe_got_user_status(sip
, uri
, SIPE_STATUS_ID_OFFLINE
);
6324 sipe_user_info_has_updated(struct sipe_account_data
*sip
,
6325 xmlnode
*xn_userinfo
)
6327 if (sip
->user_info
) {
6328 xmlnode_free(sip
->user_info
);
6330 sip
->user_info
= xmlnode_copy(xn_userinfo
);
6332 /* Publish initial state if not yet.
6333 * Assuming this happens on initial responce to self subscription
6334 * so we've already updated our UserInfo.
6336 if (!sip
->initial_state_published
) {
6337 send_presence_soap(sip
, FALSE
);
6339 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
6343 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6345 char *activity
= NULL
;
6347 const char *status_id
= NULL
;
6350 char *self_uri
= sip_uri_self(sip
);
6353 const char *device_name
= NULL
;
6354 const char *cal_start_time
= NULL
;
6355 const char *cal_granularity
= NULL
;
6356 char *cal_free_busy_base64
= NULL
;
6357 struct sipe_buddy
*sbuddy
;
6359 xmlnode
*xn_presentity
;
6360 xmlnode
*xn_availability
;
6361 xmlnode
*xn_activity
;
6362 xmlnode
*xn_display_name
;
6364 xmlnode
*xn_phone_number
;
6365 xmlnode
*xn_userinfo
;
6369 xmlnode
*xn_contact
;
6371 char *free_activity
;
6373 const char *user_avail_nil
;
6375 time_t user_avail_since
= 0;
6376 time_t activity_since
= 0;
6378 /* fix for Reuters environment on Linux */
6379 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
6381 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
6382 xn_presentity
= xmlnode_from_str(tmp_data
, strlen(tmp_data
));
6385 xn_presentity
= xmlnode_from_str(data
, len
);
6388 xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
6389 xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
6390 xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
6391 xn_email
= xmlnode_get_child(xn_presentity
, "email");
6392 xn_phone_number
= xmlnode_get_child(xn_presentity
, "phoneNumber");
6393 xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
6394 xn_oof
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "oof") : NULL
;
6395 xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
): NULL
;
6396 user_avail
= xn_state
? xmlnode_get_int_attrib(xn_state
, "avail", 0) : 0;
6397 user_avail_since
= xn_state
? sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since")) : 0;
6398 user_avail_nil
= xn_state
? xmlnode_get_attrib(xn_state
, "nil") : NULL
;
6399 xn_contact
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "contact") : NULL
;
6400 xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
6401 note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
6403 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
6405 user_avail_since
= 0;
6408 free_activity
= NULL
;
6410 name
= xmlnode_get_attrib(xn_presentity
, "uri"); /* without 'sip:' prefix */
6411 uri
= sip_uri_from_name(name
);
6412 avl
= xmlnode_get_int_attrib(xn_availability
, "aggregate", 0);
6413 epid
= xmlnode_get_attrib(xn_availability
, "epid");
6414 act
= xmlnode_get_int_attrib(xn_activity
, "aggregate", 0);
6416 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
6417 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
6418 if (user_avail
> res_avail
) {
6419 res_avail
= user_avail
;
6420 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
6423 if (xn_display_name
) {
6424 char *display_name
= g_strdup(xmlnode_get_attrib(xn_display_name
, "displayName"));
6425 char *email
= xn_email
? g_strdup(xmlnode_get_attrib(xn_email
, "email")) : NULL
;
6426 char *phone_label
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "label")) : NULL
;
6427 char *phone_number
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "number")) : NULL
;
6428 char *tel_uri
= sip_to_tel_uri(phone_number
);
6430 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6431 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
6432 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
6433 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, !is_empty(phone_label
) ? phone_label
: phone_number
);
6436 g_free(phone_label
);
6437 g_free(phone_number
);
6439 g_free(display_name
);
6444 for (node
= xmlnode_get_child(xn_contact
, "tel"); node
; node
= xmlnode_get_next_twin(node
))
6446 /* Ex.: <tel type="work">tel:+3222220000</tel> */
6447 const char *phone_type
= xmlnode_get_attrib(node
, "type");
6448 char* phone
= xmlnode_get_data(node
);
6450 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, NULL
);
6456 /* devicePresence */
6457 for (node
= xmlnode_get_descendant(xn_presentity
, "devices", "devicePresence", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
6458 xmlnode
*xn_device_name
;
6459 xmlnode
*xn_calendar_info
;
6464 if (sipe_strequal(xmlnode_get_attrib(node
, "epid"), epid
)) {
6465 xn_device_name
= xmlnode_get_child(node
, "deviceName");
6466 device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
6470 xn_calendar_info
= xmlnode_get_child(node
, "calendarInfo");
6471 if (xn_calendar_info
) {
6472 const char *cal_start_time_tmp
= xmlnode_get_attrib(xn_calendar_info
, "startTime");
6474 if (cal_start_time
) {
6475 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
6476 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
6478 if (cal_start_time_t_tmp
> cal_start_time_t
) {
6479 cal_start_time
= cal_start_time_tmp
;
6480 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6481 g_free(cal_free_busy_base64
);
6482 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6484 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
);
6487 cal_start_time
= cal_start_time_tmp
;
6488 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6489 g_free(cal_free_busy_base64
);
6490 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6492 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
);
6497 xn_state
= xmlnode_get_descendant(node
, "states", "state", NULL
);
6499 int dev_avail
= xmlnode_get_int_attrib(xn_state
, "avail", 0);
6500 time_t dev_avail_since
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since"));
6502 state
= xmlnode_get_data(xn_state
);
6503 if (dev_avail_since
> user_avail_since
&&
6504 dev_avail
>= res_avail
)
6506 res_avail
= dev_avail
;
6507 if (!is_empty(state
))
6509 if (sipe_strequal(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
6511 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
6512 } else if (sipe_strequal(state
, "presenting")) {
6514 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
6519 activity_since
= dev_avail_since
;
6521 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
6528 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
6530 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
6534 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
6537 g_free(sbuddy
->activity
);
6538 sbuddy
->activity
= activity
;
6541 sbuddy
->activity_since
= activity_since
;
6543 sbuddy
->user_avail
= user_avail
;
6544 sbuddy
->user_avail_since
= user_avail_since
;
6546 g_free(sbuddy
->note
);
6547 sbuddy
->note
= NULL
;
6548 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
6550 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
6552 g_free(sbuddy
->device_name
);
6553 sbuddy
->device_name
= NULL
;
6554 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
6556 if (!is_empty(cal_free_busy_base64
)) {
6557 g_free(sbuddy
->cal_start_time
);
6558 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
6560 sbuddy
->cal_granularity
= !g_ascii_strcasecmp(cal_granularity
, "PT15M") ? 15 : 0;
6562 g_free(sbuddy
->cal_free_busy_base64
);
6563 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
6564 cal_free_busy_base64
= NULL
;
6566 g_free(sbuddy
->cal_free_busy
);
6567 sbuddy
->cal_free_busy
= NULL
;
6570 sbuddy
->last_non_cal_status_id
= status_id
;
6571 g_free(sbuddy
->last_non_cal_activity
);
6572 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
6574 if (sipe_strequal(sbuddy
->name
, self_uri
)) {
6575 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
6577 sip
->is_oof_note
= sbuddy
->is_oof_note
;
6580 sip
->note
= g_strdup(sbuddy
->note
);
6582 sip
->note_since
= time(NULL
);
6585 g_free(sip
->status
);
6586 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
6589 g_free(cal_free_busy_base64
);
6592 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", status_id
);
6593 sipe_got_user_status(sip
, uri
, status_id
);
6595 if (!sip
->ocs2007
&& sipe_strequal(self_uri
, uri
)) {
6596 sipe_user_info_has_updated(sip
, xn_userinfo
);
6600 xmlnode_free(xn_presentity
);
6605 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
6607 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6609 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
6611 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
6612 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
6614 const char *content
= msg
->body
;
6615 unsigned length
= msg
->bodylen
;
6616 PurpleMimeDocument
*mime
= NULL
;
6618 if (strstr(ctype
, "multipart"))
6620 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6621 const char *content_type
;
6623 mime
= purple_mime_document_parse(doc
);
6624 parts
= purple_mime_document_get_parts(mime
);
6626 content
= purple_mime_part_get_data(parts
->data
);
6627 length
= purple_mime_part_get_length(parts
->data
);
6628 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
6629 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
6631 process_incoming_notify_rlmi_resub(sip
, content
, length
);
6633 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
6635 process_incoming_notify_msrtc(sip
, content
, length
);
6639 process_incoming_notify_rlmi(sip
, content
, length
);
6641 parts
= parts
->next
;
6647 purple_mime_document_free(mime
);
6650 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
6652 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
6654 else if(strstr(ctype
, "application/rlmi+xml"))
6656 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
6659 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
6661 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
6665 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
6669 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
6671 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6672 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6674 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
6677 strstr(ctype
, "multipart") &&
6678 (strstr(ctype
, "application/rlmi+xml") ||
6679 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
6680 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6681 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
6682 GList
*parts
= purple_mime_document_get_parts(mime
);
6683 GSList
*buddies
= NULL
;
6684 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6687 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
6688 purple_mime_part_get_length(parts
->data
));
6690 if (xml
&& !sipe_strequal(xml
->name
, "list")) {
6691 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
6693 buddies
= g_slist_append(buddies
, uri
);
6697 parts
= parts
->next
;
6700 if (mime
) purple_mime_document_free(mime
);
6702 payload
->host
= g_strdup(who
);
6703 payload
->buddies
= buddies
;
6704 sipe_schedule_action(action_name
, timeout
,
6705 sipe_subscribe_presence_batched_routed
,
6706 sipe_subscribe_presence_batched_routed_free
,
6708 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
6711 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6712 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
6714 g_free(action_name
);
6718 * Dispatcher for all incoming subscription information
6719 * whether it comes from NOTIFY, BENOTIFY requests or
6720 * piggy-backed to subscription's OK responce.
6722 * @param request whether initiated from BE/NOTIFY request or OK-response message.
6723 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
6725 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
6727 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
6728 const gchar
*event
= sipmsg_find_header(msg
, "Event");
6729 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
6732 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n",
6734 tmp
= fix_newlines(msg
->body
));
6736 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n", subscription_state
? subscription_state
: "");
6738 /* implicit subscriptions */
6739 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
6740 sipe_process_imdn(sip
, msg
);
6744 /* for one off subscriptions (send with Expire: 0) */
6745 if (!g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning-v2"))
6747 sipe_process_provisioning_v2(sip
, msg
);
6749 else if (!g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning"))
6751 sipe_process_provisioning(sip
, msg
);
6753 else if (!g_ascii_strcasecmp(event
, "presence"))
6755 sipe_process_presence(sip
, msg
);
6757 else if (!g_ascii_strcasecmp(event
, "registration-notify"))
6759 sipe_process_registration_notify(sip
, msg
);
6762 if (!subscription_state
|| strstr(subscription_state
, "active"))
6764 if (!g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
6766 sipe_process_roaming_contacts(sip
, msg
);
6768 else if (!g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self"))
6770 sipe_process_roaming_self(sip
, msg
);
6772 else if (!g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
6774 sipe_process_roaming_acl(sip
, msg
);
6776 else if (!g_ascii_strcasecmp(event
, "presence.wpending"))
6778 sipe_process_presence_wpending(sip
, msg
);
6780 else if (!g_ascii_strcasecmp(event
, "conference"))
6782 sipe_process_conference(sip
, msg
);
6787 /* The server sends status 'terminated' */
6788 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
6789 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
6790 gchar
*key
= sipe_get_subscription_key(event
, who
);
6792 purple_debug_info("sipe", "process_incoming_notify: server says that subscription to %s was terminated.\n", who
);
6795 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
6796 g_hash_table_remove(sip
->subscriptions
, key
);
6797 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
6803 if (!request
&& event
) {
6804 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
6805 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
6806 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n", timeout
);
6809 /* 2 min ahead of expiration */
6810 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
6812 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
6813 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
6815 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
6816 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
6817 g_free(action_name
);
6819 else if (!g_ascii_strcasecmp(event
, "presence") &&
6820 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
6822 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
6823 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6825 if (sip
->batched_support
) {
6826 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
6829 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6830 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
, timeout
);
6832 g_free(action_name
);
6838 /* The client responses on received a NOTIFY message */
6839 if (request
&& !benotify
)
6841 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
6846 * Whether user manually changed status or
6847 * it was changed automatically due to user
6848 * became inactive/active again
6851 sipe_is_user_state(struct sipe_account_data
*sip
)
6854 time_t now
= time(NULL
);
6856 purple_debug_info("sipe", "sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
6857 purple_debug_info("sipe", "sipe_is_user_state: now : %s", asctime(localtime(&now
)));
6859 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
6861 purple_debug_info("sipe", "sipe_is_user_state: res = %s\n", res
? "USER" : "MACHINE");
6866 send_presence_soap0(struct sipe_account_data
*sip
,
6867 gboolean do_publish_calendar
,
6868 gboolean do_reset_status
)
6870 struct sipe_ews
* ews
= sip
->ews
;
6871 int availability
= 0;
6876 gchar
*res_note
= NULL
;
6877 gchar
*res_oof
= NULL
;
6878 const gchar
*note_pub
= NULL
;
6879 gchar
*states
= NULL
;
6880 gchar
*calendar_data
= NULL
;
6881 gchar
*epid
= get_epid(sip
);
6882 time_t now
= time(NULL
);
6883 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
6884 const gchar
*oof_note
= ews
? sipe_ews_get_oof_note(ews
) : NULL
;
6885 const char *user_input
;
6886 gboolean pub_oof
= ews
&& oof_note
&& (!sip
->note
|| ews
->updated
> sip
->note_since
);
6888 if (oof_note
&& sip
->note
) {
6889 purple_debug_info("sipe", "ews->oof_start : %s", asctime(localtime(&(ews
->oof_start
))));
6890 purple_debug_info("sipe", "sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
6893 purple_debug_info("sipe", "sip->note : %s", sip
->note
? sip
->note
: "");
6895 if (!sip
->initial_state_published
||
6898 g_free(sip
->status
);
6899 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
6902 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
6906 note_pub
= oof_note
;
6907 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
6908 ews
->published
= TRUE
;
6909 } else if (sip
->note
) {
6910 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in ews already */
6913 sip
->is_oof_note
= FALSE
;
6914 sip
->note_since
= 0;
6916 note_pub
= sip
->note
;
6917 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
6923 /* to protocol internal plain text format */
6924 tmp
= purple_markup_strip_html(note_pub
);
6925 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
6930 if (!do_reset_status
) {
6931 if (sipe_is_user_state(sip
) && !do_publish_calendar
&& sip
->initial_state_published
)
6933 gchar
*activity_token
= NULL
;
6934 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
6936 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
6941 g_free(activity_token
);
6943 else /* preserve existing publication */
6946 if (sip
->user_info
&& (xn_states
= xmlnode_get_child(sip
->user_info
, "states"))) {
6947 states
= xmlnode_to_str(xn_states
, NULL
);
6948 /* this is a hack-around to remove added newline after inner element,
6949 * state in this case, where it shouldn't be.
6950 * After several use of xmlnode_to_str, amount of added newlines
6951 * grows significantly.
6953 purple_str_strip_char(states
, '\n');
6954 //purple_str_strip_char(states, '\r');
6958 /* do nothing - then User state will be erased */
6960 sip
->initial_state_published
= TRUE
;
6963 if (ews
&& (!is_empty(ews
->legacy_dn
) || !is_empty(ews
->email
)) && ews
->fb_start
&& !is_empty(ews
->free_busy
))
6965 char *fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
6966 char *free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
6967 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
6968 !is_empty(ews
->legacy_dn
) ? ews
->legacy_dn
: ews
->email
,
6971 g_free(fb_start_str
);
6972 g_free(free_busy_base64
);
6975 user_input
= !sipe_is_user_state(sip
) && sip
->status
!= SIPE_STATUS_ID_AVAILABLE
? "idle" : "active";
6977 /* forming resulting XML */
6978 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
6982 (tmp
= g_ascii_strup(sipe_get_host_name(), -1)),
6983 res_note
? res_note
: "",
6984 res_oof
? res_oof
: "",
6985 states
? states
: "",
6986 calendar_data
? calendar_data
: "",
6995 g_free(calendar_data
);
6997 send_soap_request(sip
, body
);
7000 g_free(since_time_str
);
7005 send_presence_soap(struct sipe_account_data
*sip
,
7006 gboolean do_publish_calendar
)
7008 return send_presence_soap0(sip
, do_publish_calendar
, FALSE
);
7013 process_send_presence_category_publish_response(struct sipe_account_data
*sip
,
7015 struct transaction
*trans
)
7017 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
7019 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
7025 gboolean has_device_publication
= FALSE
;
7027 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
7029 /* test if version mismatch fault */
7030 fault_code
= xmlnode_get_data(xmlnode_get_child(xml
, "Faultcode"));
7031 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
7032 purple_debug_info("sipe", "process_send_presence_category_publish_response: unsupported fault code:%s returning.\n", fault_code
);
7039 /* accumulating information about faulty versions */
7040 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
7041 for (node
= xmlnode_get_descendant(xml
, "details", "operation", NULL
);
7043 node
= xmlnode_get_next_twin(node
))
7045 const gchar
*index
= xmlnode_get_attrib(node
, "index");
7046 const gchar
*curVersion
= xmlnode_get_attrib(node
, "curVersion");
7048 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
7049 purple_debug_info("sipe", "fault added: index:%s curVersion:%s\n", index
, curVersion
);
7053 /* here we are parsing own request to figure out what publication
7054 * referensed here only by index went wrong
7056 xml
= xmlnode_from_str(trans
->msg
->body
, trans
->msg
->bodylen
);
7059 for (node
= xmlnode_get_descendant(xml
, "publications", "publication", NULL
),
7060 index_our
= 1; /* starts with 1 - our first publication */
7062 node
= xmlnode_get_next_twin(node
), index_our
++)
7064 gchar
*idx
= g_strdup_printf("%d", index_our
);
7065 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
7066 const gchar
*categoryName
= xmlnode_get_attrib(node
, "categoryName");
7069 if (sipe_strequal("device", categoryName
)) {
7070 has_device_publication
= TRUE
;
7073 if (curVersion
) { /* fault exist on this index */
7074 const gchar
*container
= xmlnode_get_attrib(node
, "container");
7075 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
7076 /* key is <category><instance><container> */
7077 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
7078 struct sipe_publication
*publication
=
7079 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, categoryName
), key
);
7081 purple_debug_info("sipe", "key is %s\n", key
);
7084 purple_debug_info("sipe", "Updating %s with version %s. Was %d before.\n",
7085 key
, curVersion
, publication
->version
);
7086 /* updating publication's version to the correct one */
7087 publication
->version
= atoi(curVersion
);
7093 g_hash_table_destroy(faults
);
7095 /* rebublishing with right versions */
7096 if (has_device_publication
) {
7097 send_publish_category_initial(sip
);
7099 send_presence_status(sip
);
7106 * Returns 'device' XML part for publication.
7107 * Must be g_free'd after use.
7110 sipe_publish_get_category_device(struct sipe_account_data
*sip
)
7114 gchar
*epid
= get_epid(sip
);
7115 gchar
*uuid
= generateUUIDfromEPID(epid
);
7116 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
7117 /* key is <category><instance><container> */
7118 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
7119 struct sipe_publication
*publication
=
7120 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
7125 uri
= sip_uri_self(sip
);
7126 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
7128 publication
? publication
->version
: 0,
7131 "00:00:00+01:00", /* @TODO make timezone real*/
7132 sipe_get_host_name()
7142 * A service method - use
7143 * - send_publish_get_category_state_machine and
7144 * - send_publish_get_category_state_user instead.
7145 * Must be g_free'd after use.
7148 sipe_publish_get_category_state(struct sipe_account_data
*sip
,
7149 gboolean is_user_state
)
7151 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
7152 guint instance
= is_user_state
? sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
) :
7153 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
7154 /* key is <category><instance><container> */
7155 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7156 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7157 struct sipe_publication
*publication_2
=
7158 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7159 struct sipe_publication
*publication_3
=
7160 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7165 if (publication_2
&& (publication_2
->availability
== availability
))
7167 purple_debug_info("sipe", "sipe_publish_get_category_state: state has NOT changed. Exiting.\n");
7168 return NULL
; /* nothing to update */
7171 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
7173 publication_2
? publication_2
->version
: 0,
7176 publication_3
? publication_3
->version
: 0,
7181 * Only Busy and OOF calendar event are published.
7182 * Different instances are used for that.
7184 * Must be g_free'd after use.
7187 sipe_publish_get_category_state_calendar(struct sipe_account_data
*sip
,
7188 struct sipe_cal_event
*event
,
7192 gchar
*start_time_str
;
7193 int availability
= 0;
7196 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
7197 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
) :
7198 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
7200 /* key is <category><instance><container> */
7201 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7202 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7203 struct sipe_publication
*publication_2
=
7204 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7205 struct sipe_publication
*publication_3
=
7206 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7211 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
7212 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7213 "Exiting as no publication and no event for cal_satus:%d\n", cal_satus
);
7219 (publication_3
->availability
== availability
) &&
7220 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
7223 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7224 "cal state has NOT changed for cal_satus:%d. Exiting.\n", cal_satus
);
7225 return NULL
; /* nothing to update */
7230 (event
->cal_status
== SIPE_CAL_BUSY
||
7231 event
->cal_status
== SIPE_CAL_OOF
))
7233 gchar
*availability_xml_str
= NULL
;
7234 gchar
*activity_xml_str
= NULL
;
7236 if (event
->cal_status
== SIPE_CAL_BUSY
) {
7237 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
7240 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
7241 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7242 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
7243 "minAvailability=\"6500\"",
7244 "maxAvailability=\"8999\"");
7245 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
7246 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7247 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
7248 "minAvailability=\"12000\"",
7251 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
7253 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
7255 publication_2
? publication_2
->version
: 0,
7258 availability_xml_str
? availability_xml_str
: "",
7259 activity_xml_str
? activity_xml_str
: "",
7260 event
->subject
? event
->subject
: "",
7261 event
->location
? event
->location
: "",
7264 publication_3
? publication_3
->version
: 0,
7267 availability_xml_str
? availability_xml_str
: "",
7268 activity_xml_str
? activity_xml_str
: "",
7269 event
->subject
? event
->subject
: "",
7270 event
->location
? event
->location
: ""
7272 g_free(start_time_str
);
7273 g_free(availability_xml_str
);
7274 g_free(activity_xml_str
);
7277 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
7279 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
7281 publication_2
? publication_2
->version
: 0,
7284 publication_3
? publication_3
->version
: 0
7292 * Returns 'machineState' XML part for publication.
7293 * Must be g_free'd after use.
7296 sipe_publish_get_category_state_machine(struct sipe_account_data
*sip
)
7298 return sipe_publish_get_category_state(sip
, FALSE
);
7302 * Returns 'userState' XML part for publication.
7303 * Must be g_free'd after use.
7306 sipe_publish_get_category_state_user(struct sipe_account_data
*sip
)
7308 return sipe_publish_get_category_state(sip
, TRUE
);
7312 * Returns 'note' XML part for publication.
7313 * Must be g_free'd after use.
7315 * Protocol format for Note is plain text.
7317 * @param note a note in Sipe internal HTML format
7318 * @param note_type either personal or OOF
7321 sipe_publish_get_category_note(struct sipe_account_data
*sip
,
7322 const char *note
, /* html */
7323 const char *note_type
,
7327 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
) : 0;
7328 /* key is <category><instance><container> */
7329 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
7330 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
7331 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
7333 struct sipe_publication
*publication_note_200
=
7334 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
7335 struct sipe_publication
*publication_note_300
=
7336 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
7337 struct sipe_publication
*publication_note_400
=
7338 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
7340 char *tmp
= note
? purple_markup_strip_html(note
) : NULL
;
7341 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
7342 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
7343 char *res
, *tmp1
, *tmp2
, *tmp3
;
7344 char *start_time_attr
;
7345 char *end_time_attr
;
7349 g_free(key_note_200
);
7350 g_free(key_note_300
);
7351 g_free(key_note_400
);
7353 /* we even need to republish empty note */
7354 if (sipe_strequal(n1
, n2
))
7356 purple_debug_info("sipe", "sipe_publish_get_category_note: note has NOT changed. Exiting.\n");
7358 return NULL
; /* nothing to update */
7361 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
7364 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
7368 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7371 publication_note_200
? publication_note_200
->version
: 0,
7373 start_time_attr
? start_time_attr
: "",
7374 end_time_attr
? end_time_attr
: "",
7377 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7380 publication_note_300
? publication_note_300
->version
: 0,
7382 start_time_attr
? start_time_attr
: "",
7383 end_time_attr
? end_time_attr
: "",
7386 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7389 publication_note_400
? publication_note_400
->version
: 0,
7391 start_time_attr
? start_time_attr
: "",
7392 end_time_attr
? end_time_attr
: "",
7395 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7399 publication_note_200
? publication_note_200
->version
: 0,
7401 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7405 publication_note_200
? publication_note_200
->version
: 0,
7407 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7411 publication_note_200
? publication_note_200
->version
: 0,
7414 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
7416 g_free(start_time_attr
);
7417 g_free(end_time_attr
);
7427 * Returns 'calendarData' XML part with WorkingHours for publication.
7428 * Must be g_free'd after use.
7431 sipe_publish_get_category_cal_working_hours(struct sipe_account_data
*sip
)
7433 struct sipe_ews
* ews
= sip
->ews
;
7435 /* key is <category><instance><container> */
7436 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
7437 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
7438 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
7439 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
7440 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
7441 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
7443 struct sipe_publication
*publication_cal_1
=
7444 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7445 struct sipe_publication
*publication_cal_100
=
7446 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7447 struct sipe_publication
*publication_cal_200
=
7448 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7449 struct sipe_publication
*publication_cal_300
=
7450 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7451 struct sipe_publication
*publication_cal_400
=
7452 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7453 struct sipe_publication
*publication_cal_32000
=
7454 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7456 const char *n1
= ews
? ews
->working_hours_xml_str
: NULL
;
7457 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
7460 g_free(key_cal_100
);
7461 g_free(key_cal_200
);
7462 g_free(key_cal_300
);
7463 g_free(key_cal_400
);
7464 g_free(key_cal_32000
);
7466 if (!ews
|| is_empty(ews
->email
) || is_empty(ews
->working_hours_xml_str
)) {
7467 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: no data to publish, exiting\n");
7471 if (sipe_strequal(n1
, n2
))
7473 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.\n");
7474 return NULL
; /* nothing to update */
7477 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
7479 publication_cal_1
? publication_cal_1
->version
: 0,
7481 ews
->working_hours_xml_str
,
7483 publication_cal_100
? publication_cal_100
->version
: 0,
7485 publication_cal_200
? publication_cal_200
->version
: 0,
7487 ews
->working_hours_xml_str
,
7489 publication_cal_300
? publication_cal_300
->version
: 0,
7491 ews
->working_hours_xml_str
,
7492 /* 400 - Personal */
7493 publication_cal_400
? publication_cal_400
->version
: 0,
7495 ews
->working_hours_xml_str
,
7496 /* 32000 - Blocked */
7497 publication_cal_32000
? publication_cal_32000
->version
: 0
7502 * Returns 'calendarData' XML part with FreeBusy for publication.
7503 * Must be g_free'd after use.
7506 sipe_publish_get_category_cal_free_busy(struct sipe_account_data
*sip
)
7508 struct sipe_ews
* ews
= sip
->ews
;
7509 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
7511 char *free_busy_base64
;
7516 /* key is <category><instance><container> */
7517 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
7518 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
7519 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
7520 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
7521 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
7522 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
7524 struct sipe_publication
*publication_cal_1
=
7525 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7526 struct sipe_publication
*publication_cal_100
=
7527 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7528 struct sipe_publication
*publication_cal_200
=
7529 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7530 struct sipe_publication
*publication_cal_300
=
7531 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7532 struct sipe_publication
*publication_cal_400
=
7533 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7534 struct sipe_publication
*publication_cal_32000
=
7535 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7538 g_free(key_cal_100
);
7539 g_free(key_cal_200
);
7540 g_free(key_cal_300
);
7541 g_free(key_cal_400
);
7542 g_free(key_cal_32000
);
7544 if (!ews
|| is_empty(ews
->email
) || !ews
->fb_start
|| is_empty(ews
->free_busy
)) {
7545 purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: no data to publish, exiting\n");
7549 fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
7550 free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
7552 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
7553 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
7555 /* we will rebuplish the same data to refresh publication time,
7556 * so if data from multiple sources, most recent will be choosen
7558 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
7560 // purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.\n");
7561 // g_free(fb_start_str);
7562 // g_free(free_busy_base64);
7563 // return NULL; /* nothing to update */
7566 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
7569 publication_cal_1
? publication_cal_1
->version
: 0,
7572 publication_cal_100
? publication_cal_100
->version
: 0,
7575 publication_cal_200
? publication_cal_200
->version
: 0,
7581 publication_cal_300
? publication_cal_300
->version
: 0,
7585 /* 400 - Personal */
7587 publication_cal_400
? publication_cal_400
->version
: 0,
7591 /* 32000 - Blocked */
7593 publication_cal_32000
? publication_cal_32000
->version
: 0
7596 g_free(fb_start_str
);
7597 g_free(free_busy_base64
);
7601 static void send_presence_publish(struct sipe_account_data
*sip
, const char *publications
)
7608 uri
= sip_uri_self(sip
);
7609 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
7613 tmp
= get_contact(sip
);
7614 hdr
= g_strdup_printf("Contact: %s\r\n"
7615 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
7617 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
7626 send_publish_category_initial(struct sipe_account_data
*sip
)
7628 gchar
*pub_device
= sipe_publish_get_category_device(sip
);
7630 gchar
*publications
;
7632 g_free(sip
->status
);
7633 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
7635 pub_machine
= sipe_publish_get_category_state_machine(sip
);
7636 publications
= g_strdup_printf("%s%s",
7638 pub_machine
? pub_machine
: "");
7640 g_free(pub_machine
);
7642 send_presence_publish(sip
, publications
);
7643 g_free(publications
);
7647 send_presence_category_publish(struct sipe_account_data
*sip
)
7649 gchar
*pub_state
= sipe_is_user_state(sip
) ?
7650 sipe_publish_get_category_state_user(sip
) :
7651 sipe_publish_get_category_state_machine(sip
);
7652 gchar
*pub_note
= sipe_publish_get_category_note(sip
,
7654 sip
->is_oof_note
? "OOF" : "personal",
7657 gchar
*publications
;
7659 if (!pub_state
&& !pub_note
) {
7660 purple_debug_info("sipe", "send_presence_category_publish: nothing has changed. Exiting.\n");
7664 publications
= g_strdup_printf("%s%s",
7665 pub_state
? pub_state
: "",
7666 pub_note
? pub_note
: "");
7671 send_presence_publish(sip
, publications
);
7672 g_free(publications
);
7676 * Publishes self status
7677 * based on own calendar information.
7682 publish_calendar_status_self(struct sipe_account_data
*sip
)
7684 struct sipe_cal_event
* event
= NULL
;
7685 gchar
*pub_cal_working_hours
= NULL
;
7686 gchar
*pub_cal_free_busy
= NULL
;
7687 gchar
*pub_calendar
= NULL
;
7688 gchar
*pub_calendar2
= NULL
;
7689 gchar
*pub_oof_note
= NULL
;
7690 const gchar
*oof_note
;
7691 time_t oof_start
= 0;
7695 purple_debug_info("sipe", "publish_calendar_status_self() no calendar data.\n");
7699 purple_debug_info("sipe", "publish_calendar_status_self() started.\n");
7700 if (sip
->ews
->cal_events
) {
7701 event
= sipe_cal_get_event(sip
->ews
->cal_events
, time(NULL
));
7705 purple_debug_info("sipe", "publish_calendar_status_self: current event is NULL\n");
7707 char *desc
= sipe_cal_event_describe(event
);
7708 purple_debug_info("sipe", "publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
7714 OOF publish, Busy clean
7716 OOF clean, Busy publish
7718 OOF clean, Busy clean
7720 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
7721 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_OOF
);
7722 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7723 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
7724 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7725 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7727 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7728 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7731 oof_note
= sipe_ews_get_oof_note(sip
->ews
);
7732 if (sipe_strequal("Scheduled", sip
->ews
->oof_state
)) {
7733 oof_start
= sip
->ews
->oof_start
;
7734 oof_end
= sip
->ews
->oof_end
;
7736 pub_oof_note
= sipe_publish_get_category_note(sip
, oof_note
, "OOF", oof_start
, oof_end
);
7738 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sip
);
7739 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sip
);
7741 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
7742 purple_debug_info("sipe", "publish_calendar_status_self: nothing has changed.\n");
7744 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
7745 pub_cal_working_hours
? pub_cal_working_hours
: "",
7746 pub_cal_free_busy
? pub_cal_free_busy
: "",
7747 pub_calendar
? pub_calendar
: "",
7748 pub_calendar2
? pub_calendar2
: "",
7749 pub_oof_note
? pub_oof_note
: "");
7751 send_presence_publish(sip
, publications
);
7752 g_free(publications
);
7755 g_free(pub_cal_working_hours
);
7756 g_free(pub_cal_free_busy
);
7757 g_free(pub_calendar
);
7758 g_free(pub_calendar2
);
7759 g_free(pub_oof_note
);
7761 /* repeat scheduling */
7762 sipe_sched_calendar_status_self_publish(sip
, time(NULL
));
7765 static void send_presence_status(struct sipe_account_data
*sip
)
7767 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
7769 if (!status
) return;
7771 purple_debug_info("sipe", "send_presence_status: status: %s (%s)\n",
7772 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
7773 sipe_is_user_state(sip
) ? "USER" : "MACHINE");
7776 send_presence_category_publish(sip
);
7778 send_presence_soap(sip
, FALSE
);
7782 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
7784 gboolean found
= FALSE
;
7785 const char *method
= msg
->method
? msg
->method
: "NOT FOUND";
7786 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,method
);
7787 if (msg
->response
== 0) { /* request */
7788 if (sipe_strequal(method
, "MESSAGE")) {
7789 process_incoming_message(sip
, msg
);
7791 } else if (sipe_strequal(method
, "NOTIFY")) {
7792 purple_debug_info("sipe","send->process_incoming_notify\n");
7793 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
7795 } else if (sipe_strequal(method
, "BENOTIFY")) {
7796 purple_debug_info("sipe","send->process_incoming_benotify\n");
7797 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
7799 } else if (sipe_strequal(method
, "INVITE")) {
7800 process_incoming_invite(sip
, msg
);
7802 } else if (sipe_strequal(method
, "REFER")) {
7803 process_incoming_refer(sip
, msg
);
7805 } else if (sipe_strequal(method
, "OPTIONS")) {
7806 process_incoming_options(sip
, msg
);
7808 } else if (sipe_strequal(method
, "INFO")) {
7809 process_incoming_info(sip
, msg
);
7811 } else if (sipe_strequal(method
, "ACK")) {
7812 // ACK's don't need any response
7814 } else if (sipe_strequal(method
, "SUBSCRIBE")) {
7815 // LCS 2005 sends us these - just respond 200 OK
7817 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
7818 } else if (sipe_strequal(method
, "BYE")) {
7819 process_incoming_bye(sip
, msg
);
7822 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
7824 } else { /* response */
7825 struct transaction
*trans
= transactions_find(sip
, msg
);
7827 if (msg
->response
== 407) {
7828 gchar
*resend
, *auth
;
7831 if (sip
->proxy
.retries
> 30) return;
7832 sip
->proxy
.retries
++;
7833 /* do proxy authentication */
7835 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
7837 fill_auth(ptmp
, &sip
->proxy
);
7838 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
7839 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
7840 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
7842 resend
= sipmsg_to_string(trans
->msg
);
7843 /* resend request */
7844 sendout_pkt(sip
->gc
, resend
);
7847 if (msg
->response
< 200) {
7848 /* ignore provisional response */
7849 purple_debug_info("sipe", "got provisional (%d) response, ignoring\n", msg
->response
);
7851 sip
->proxy
.retries
= 0;
7852 if (sipe_strequal(trans
->msg
->method
, "REGISTER")) {
7853 if (msg
->response
== 401)
7855 sip
->registrar
.retries
++;
7859 sip
->registrar
.retries
= 0;
7861 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\n", sip
->cseq
);
7863 if (msg
->response
== 401) {
7864 gchar
*resend
, *auth
, *ptmp
;
7865 const char* auth_scheme
;
7867 if (sip
->registrar
.retries
> 4) return;
7868 sip
->registrar
.retries
++;
7870 auth_scheme
= sipe_get_auth_scheme_name(sip
);
7871 ptmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
7873 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\n", ptmp
? ptmp
: "");
7875 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
7876 sip
->gc
->wants_to_die
= TRUE
;
7877 purple_connection_error(sip
->gc
, tmp2
);
7882 fill_auth(ptmp
, &sip
->registrar
);
7883 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
7884 sipmsg_remove_header_now(trans
->msg
, "Authorization");
7885 sipmsg_add_header_now_pos(trans
->msg
, "Authorization", auth
, 5);
7887 resend
= sipmsg_to_string(trans
->msg
);
7888 /* resend request */
7889 sendout_pkt(sip
->gc
, resend
);
7894 if (trans
->callback
) {
7895 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\n");
7896 /* call the callback to process response*/
7897 (trans
->callback
)(sip
, msg
, trans
);
7900 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\n", sip
->cseq
);
7901 transactions_remove(sip
, trans
);
7907 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
7911 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", method
, msg
->response
);
7915 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
7924 /* according to the RFC remove CRLF at the beginning */
7925 while (*cur
== '\r' || *cur
== '\n') {
7928 if (cur
!= conn
->inbuf
) {
7929 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
7930 conn
->inbufused
= strlen(conn
->inbuf
);
7933 /* Received a full Header? */
7934 sip
->processing_input
= TRUE
;
7935 while (sip
->processing_input
&&
7936 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
7937 time_t currtime
= time(NULL
);
7940 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), tmp
= fix_newlines(conn
->inbuf
));
7942 msg
= sipmsg_parse_header(conn
->inbuf
);
7945 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
7946 if (msg
&& restlen
>= msg
->bodylen
) {
7947 dummy
= g_malloc(msg
->bodylen
+ 1);
7948 memcpy(dummy
, cur
, msg
->bodylen
);
7949 dummy
[msg
->bodylen
] = '\0';
7951 cur
+= msg
->bodylen
;
7952 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
7953 conn
->inbufused
= strlen(conn
->inbuf
);
7956 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n", restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
7963 purple_debug_info("sipe", "body:\n%s", msg->body);
7966 // Verify the signature before processing it
7967 if (sip
->registrar
.gssapi_context
) {
7968 struct sipmsg_breakdown msgbd
;
7969 gchar
*signature_input_str
;
7972 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
7973 signature_input_str
= sipmsg_breakdown_get_string(sip
->registrar
.version
, &msgbd
);
7975 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
7977 if (rspauth
!= NULL
) {
7978 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
7979 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
7980 process_input_message(sip
, msg
);
7982 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
7983 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
7984 sip
->gc
->wants_to_die
= TRUE
;
7986 } else if (msg
->response
== 401) {
7987 purple_connection_error(sip
->gc
, _("Wrong password"));
7988 sip
->gc
->wants_to_die
= TRUE
;
7990 g_free(signature_input_str
);
7993 sipmsg_breakdown_free(&msgbd
);
7995 process_input_message(sip
, msg
);
8002 static void sipe_udp_process(gpointer data
, gint source
,
8003 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
8005 PurpleConnection
*gc
= data
;
8006 struct sipe_account_data
*sip
= gc
->proto_data
;
8009 static char buffer
[65536];
8010 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
8011 time_t currtime
= time(NULL
);
8014 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), buffer
);
8015 msg
= sipmsg_parse_msg(buffer
);
8016 if (msg
) process_input_message(sip
, msg
);
8020 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
8022 struct sipe_account_data
*sip
= gc
->proto_data
;
8023 PurpleSslConnection
*gsc
= sip
->gsc
;
8025 purple_debug_error("sipe", "%s",debug
);
8026 purple_connection_error(gc
, msg
);
8028 /* Invalidate this connection. Next send will open a new one */
8030 connection_remove(sip
, gsc
->fd
);
8031 purple_ssl_close(gsc
);
8037 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
8038 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8040 PurpleConnection
*gc
= data
;
8041 struct sipe_account_data
*sip
;
8042 struct sip_connection
*conn
;
8044 gboolean firstread
= TRUE
;
8046 /* NOTE: This check *IS* necessary */
8047 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
8048 purple_ssl_close(gsc
);
8052 sip
= gc
->proto_data
;
8053 conn
= connection_find(sip
, gsc
->fd
);
8055 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
8056 gc
->wants_to_die
= TRUE
;
8057 purple_connection_error(gc
, _("Connection not found. Please try to connect again"));
8061 /* Read all available data from the SSL connection */
8063 /* Increase input buffer size as needed */
8064 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
8065 conn
->inbuflen
+= SIMPLE_BUF_INC
;
8066 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
8067 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
8070 /* Try to read as much as there is space left in the buffer */
8071 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
8072 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
8074 if (len
< 0 && errno
== EAGAIN
) {
8075 /* Try again later */
8077 } else if (len
< 0) {
8078 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
8080 } else if (firstread
&& (len
== 0)) {
8081 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
8085 conn
->inbufused
+= len
;
8088 /* Equivalence indicates that there is possibly more data to read */
8089 } while (len
== readlen
);
8091 conn
->inbuf
[conn
->inbufused
] = '\0';
8092 process_input(sip
, conn
);
8096 static void sipe_input_cb(gpointer data
, gint source
,
8097 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8099 PurpleConnection
*gc
= data
;
8100 struct sipe_account_data
*sip
= gc
->proto_data
;
8102 struct sip_connection
*conn
= connection_find(sip
, source
);
8104 purple_debug_error("sipe", "Connection not found!\n");
8108 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
8109 conn
->inbuflen
+= SIMPLE_BUF_INC
;
8110 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
8113 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
8115 if (len
< 0 && errno
== EAGAIN
)
8117 else if (len
<= 0) {
8118 purple_debug_info("sipe", "sipe_input_cb: read error\n");
8119 connection_remove(sip
, source
);
8120 if (sip
->fd
== source
) sip
->fd
= -1;
8124 conn
->inbufused
+= len
;
8125 conn
->inbuf
[conn
->inbufused
] = '\0';
8127 process_input(sip
, conn
);
8130 /* Callback for new connections on incoming TCP port */
8131 static void sipe_newconn_cb(gpointer data
, gint source
,
8132 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8134 PurpleConnection
*gc
= data
;
8135 struct sipe_account_data
*sip
= gc
->proto_data
;
8136 struct sip_connection
*conn
;
8138 int newfd
= accept(source
, NULL
, NULL
);
8140 conn
= connection_create(sip
, newfd
);
8142 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8145 static void login_cb(gpointer data
, gint source
,
8146 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
8148 PurpleConnection
*gc
= data
;
8149 struct sipe_account_data
*sip
;
8150 struct sip_connection
*conn
;
8152 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8160 purple_connection_error(gc
, _("Could not connect"));
8164 sip
= gc
->proto_data
;
8166 sip
->last_keepalive
= time(NULL
);
8168 conn
= connection_create(sip
, source
);
8172 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8175 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
8176 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8178 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
8179 if (sip
== NULL
) return;
8184 static guint
sipe_ht_hash_nick(const char *nick
)
8186 char *lc
= g_utf8_strdown(nick
, -1);
8187 guint bucket
= g_str_hash(lc
);
8193 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
8195 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
8198 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
8200 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8202 sip
->listen_data
= NULL
;
8204 if (listenfd
== -1) {
8205 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8211 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
8212 sip
->listenfd
= sip
->fd
;
8214 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
8216 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
8220 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
8221 SIPE_UNUSED_PARAMETER
const char *error_message
)
8223 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8225 sip
->query_data
= NULL
;
8227 if (!hosts
|| !hosts
->data
) {
8228 purple_connection_error(sip
->gc
, _("Could not resolve hostname"));
8232 hosts
= g_slist_remove(hosts
, hosts
->data
);
8233 g_free(sip
->serveraddr
);
8234 sip
->serveraddr
= hosts
->data
;
8235 hosts
= g_slist_remove(hosts
, hosts
->data
);
8237 void *tmp
= hosts
->data
;
8238 hosts
= g_slist_remove(hosts
, tmp
);
8239 hosts
= g_slist_remove(hosts
, tmp
);
8243 /* create socket for incoming connections */
8244 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
8245 sipe_udp_host_resolved_listen_cb
, sip
);
8246 if (sip
->listen_data
== NULL
) {
8247 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8252 static const struct sipe_service_data
*current_service
= NULL
;
8254 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
8255 PurpleSslErrorType error
,
8258 PurpleConnection
*gc
= data
;
8259 struct sipe_account_data
*sip
;
8261 /* If the connection is already disconnected, we don't need to do anything else */
8262 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8265 sip
= gc
->proto_data
;
8266 current_service
= sip
->service_data
;
8267 if (current_service
) {
8268 purple_debug_info("sipe", "current_service: transport '%s' service '%s'\n",
8269 current_service
->transport
? current_service
->transport
: "NULL",
8270 current_service
->service
? current_service
->service
: "NULL");
8277 case PURPLE_SSL_CONNECT_FAILED
:
8278 purple_connection_error(gc
, _("Connection failed"));
8280 case PURPLE_SSL_HANDSHAKE_FAILED
:
8281 purple_connection_error(gc
, _("SSL handshake failed"));
8283 case PURPLE_SSL_CERTIFICATE_INVALID
:
8284 purple_connection_error(gc
, _("SSL certificate invalid"));
8290 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
8292 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8293 PurpleProxyConnectData
*connect_data
;
8295 sip
->listen_data
= NULL
;
8297 sip
->listenfd
= listenfd
;
8298 if (sip
->listenfd
== -1) {
8299 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8303 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
8304 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
8305 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
8306 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
8307 sipe_newconn_cb
, sip
->gc
);
8308 purple_debug_info("sipe", "connecting to %s port %d\n",
8309 sip
->realhostname
, sip
->realport
);
8310 /* open tcp connection to the server */
8311 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
8312 sip
->realport
, login_cb
, sip
->gc
);
8314 if (connect_data
== NULL
) {
8315 purple_connection_error(sip
->gc
, _("Could not create socket"));
8319 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
8321 PurpleAccount
*account
= sip
->account
;
8322 PurpleConnection
*gc
= sip
->gc
;
8325 port
= (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
8328 sip
->realhostname
= hostname
;
8329 sip
->realport
= port
;
8331 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
8334 /* TODO: is there a good default grow size? */
8335 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
8336 sip
->txbuf
= purple_circ_buffer_new(0);
8338 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
8340 if (!purple_ssl_is_supported()) {
8341 gc
->wants_to_die
= TRUE
;
8342 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor"));
8346 purple_debug_info("sipe", "using SSL\n");
8348 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
8349 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
8350 if (sip
->gsc
== NULL
) {
8351 purple_connection_error(gc
, _("Could not create SSL context"));
8354 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
8356 purple_debug_info("sipe", "using UDP\n");
8358 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
8359 if (sip
->query_data
== NULL
) {
8360 purple_connection_error(gc
, _("Could not resolve hostname"));
8364 purple_debug_info("sipe", "using TCP\n");
8365 /* create socket for incoming connections */
8366 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
8367 sipe_tcp_connect_listen_cb
, sip
);
8368 if (sip
->listen_data
== NULL
) {
8369 purple_connection_error(gc
, _("Could not create listen socket"));
8375 /* Service list for autodection */
8376 static const struct sipe_service_data service_autodetect
[] = {
8377 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8378 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8379 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8380 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8384 /* Service list for SSL/TLS */
8385 static const struct sipe_service_data service_tls
[] = {
8386 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8387 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8391 /* Service list for TCP */
8392 static const struct sipe_service_data service_tcp
[] = {
8393 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8394 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8398 /* Service list for UDP */
8399 static const struct sipe_service_data service_udp
[] = {
8400 { "sip", "udp", SIPE_TRANSPORT_UDP
},
8404 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
8405 static void resolve_next_service(struct sipe_account_data
*sip
,
8406 const struct sipe_service_data
*start
)
8409 sip
->service_data
= start
;
8411 sip
->service_data
++;
8412 if (sip
->service_data
->service
== NULL
) {
8414 /* Try connecting to the SIP hostname directly */
8415 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
8416 if (sip
->auto_transport
) {
8417 // If SSL is supported, default to using it; OCS servers aren't configured
8418 // by default to accept TCP
8419 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
8420 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8421 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
8424 hostname
= g_strdup(sip
->sipdomain
);
8425 create_connection(sip
, hostname
, 0);
8430 /* Try to resolve next service */
8431 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
8432 sip
->service_data
->transport
,
8437 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
8439 struct sipe_account_data
*sip
= data
;
8441 sip
->srv_query_data
= NULL
;
8443 /* find the host to connect to */
8445 gchar
*hostname
= g_strdup(resp
->hostname
);
8446 int port
= resp
->port
;
8447 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
8451 sip
->transport
= sip
->service_data
->type
;
8453 create_connection(sip
, hostname
, port
);
8455 resolve_next_service(sip
, NULL
);
8459 static void sipe_login(PurpleAccount
*account
)
8461 PurpleConnection
*gc
;
8462 struct sipe_account_data
*sip
;
8463 gchar
**signinname_login
, **userserver
;
8464 const char *transport
;
8467 const char *username
= purple_account_get_username(account
);
8468 gc
= purple_account_get_connection(account
);
8470 purple_debug_info("sipe", "sipe_login: username '%s'\n", username
);
8472 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
8473 gc
->wants_to_die
= TRUE
;
8474 purple_connection_error(gc
, _("SIP Exchange user name contains invalid characters"));
8478 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
8479 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
8480 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
8482 sip
->account
= account
;
8483 sip
->reregister_set
= FALSE
;
8484 sip
->reauthenticate_set
= FALSE
;
8485 sip
->subscribed
= FALSE
;
8486 sip
->subscribed_buddies
= FALSE
;
8487 sip
->initial_state_published
= FALSE
;
8489 /* username format: <username>,[<optional login>] */
8490 signinname_login
= g_strsplit(username
, ",", 2);
8491 purple_debug_info("sipe", "sipe_login: signinname[0] '%s'\n", signinname_login
[0]);
8493 /* ensure that username format is name@domain */
8494 if (!strchr(signinname_login
[0], '@') || g_str_has_prefix(signinname_login
[0], "@") || g_str_has_suffix(signinname_login
[0], "@")) {
8495 g_strfreev(signinname_login
);
8496 gc
->wants_to_die
= TRUE
;
8497 purple_connection_error(gc
, _("User name should be a valid SIP URI\nExample: user@company.com"));
8500 sip
->username
= g_strdup(signinname_login
[0]);
8502 /* ensure that email format is name@domain if provided */
8503 email
= purple_account_get_string(sip
->account
, "email", NULL
);
8504 if (!is_empty(email
) &&
8505 (!strchr(email
, '@') || g_str_has_prefix(email
, "@") || g_str_has_suffix(email
, "@")))
8507 gc
->wants_to_die
= TRUE
;
8508 purple_connection_error(gc
, _("Email address should be valid if provided\nExample: user@company.com"));
8511 sip
->email
= !is_empty(email
) ? g_strdup(email
) : g_strdup(sip
->username
);
8513 /* login name specified? */
8514 if (signinname_login
[1] && strlen(signinname_login
[1])) {
8515 gchar
**domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
8516 gboolean has_domain
= domain_user
[1] != NULL
;
8517 purple_debug_info("sipe", "sipe_login: signinname[1] '%s'\n", signinname_login
[1]);
8518 sip
->authdomain
= has_domain
? g_strdup(domain_user
[0]) : NULL
;
8519 sip
->authuser
= g_strdup(domain_user
[has_domain
? 1 : 0]);
8520 purple_debug_info("sipe", "sipe_login: auth domain '%s' user '%s'\n",
8521 sip
->authdomain
? sip
->authdomain
: "", sip
->authuser
);
8522 g_strfreev(domain_user
);
8525 userserver
= g_strsplit(signinname_login
[0], "@", 2);
8526 purple_debug_info("sipe", "sipe_login: user '%s' server '%s'\n", userserver
[0], userserver
[1]);
8527 purple_connection_set_display_name(gc
, userserver
[0]);
8528 sip
->sipdomain
= g_strdup(userserver
[1]);
8529 g_strfreev(userserver
);
8530 g_strfreev(signinname_login
);
8532 if (strchr(sip
->username
, ' ') != NULL
) {
8533 gc
->wants_to_die
= TRUE
;
8534 purple_connection_error(gc
, _("SIP Exchange user name contains whitespace"));
8538 sip
->password
= g_strdup(purple_connection_get_password(gc
));
8540 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
8541 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8542 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
8543 sip
->subscriptions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8544 g_free
, (GDestroyNotify
)sipe_subscription_free
);
8546 sip
->filetransfers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,g_free
,NULL
);
8548 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
8550 g_free(sip
->status
);
8551 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
8553 sip
->auto_transport
= FALSE
;
8554 transport
= purple_account_get_string(account
, "transport", "auto");
8555 userserver
= g_strsplit(purple_account_get_string(account
, "server", ""), ":", 2);
8556 if (userserver
[0]) {
8557 /* Use user specified server[:port] */
8561 port
= atoi(userserver
[1]);
8563 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login: user specified SIP server %s:%d\n",
8564 userserver
[0], port
);
8566 if (sipe_strequal(transport
, "auto")) {
8567 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8568 } else if (sipe_strequal(transport
, "tls")) {
8569 sip
->transport
= SIPE_TRANSPORT_TLS
;
8570 } else if (sipe_strequal(transport
, "tcp")) {
8571 sip
->transport
= SIPE_TRANSPORT_TCP
;
8573 sip
->transport
= SIPE_TRANSPORT_UDP
;
8576 create_connection(sip
, g_strdup(userserver
[0]), port
);
8578 /* Server auto-discovery */
8579 if (sipe_strequal(transport
, "auto")) {
8580 sip
->auto_transport
= TRUE
;
8581 if (current_service
&& current_service
->transport
!= NULL
&& current_service
->service
!= NULL
){
8583 resolve_next_service(sip
, current_service
);
8585 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
8587 } else if (sipe_strequal(transport
, "tls")) {
8588 resolve_next_service(sip
, service_tls
);
8589 } else if (sipe_strequal(transport
, "tcp")) {
8590 resolve_next_service(sip
, service_tcp
);
8592 resolve_next_service(sip
, service_udp
);
8595 g_strfreev(userserver
);
8598 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
8600 connection_free_all(sip
);
8605 if (sip
->query_data
!= NULL
)
8606 purple_dnsquery_destroy(sip
->query_data
);
8607 sip
->query_data
= NULL
;
8609 if (sip
->srv_query_data
!= NULL
)
8610 purple_srv_cancel(sip
->srv_query_data
);
8611 sip
->srv_query_data
= NULL
;
8613 if (sip
->listen_data
!= NULL
)
8614 purple_network_listen_cancel(sip
->listen_data
);
8615 sip
->listen_data
= NULL
;
8617 if (sip
->gsc
!= NULL
)
8618 purple_ssl_close(sip
->gsc
);
8621 sipe_auth_free(&sip
->registrar
);
8622 sipe_auth_free(&sip
->proxy
);
8625 purple_circ_buffer_destroy(sip
->txbuf
);
8628 g_free(sip
->realhostname
);
8629 sip
->realhostname
= NULL
;
8631 g_free(sip
->server_version
);
8632 sip
->server_version
= NULL
;
8635 purple_input_remove(sip
->listenpa
);
8637 if (sip
->tx_handler
)
8638 purple_input_remove(sip
->tx_handler
);
8639 sip
->tx_handler
= 0;
8640 if (sip
->resendtimeout
)
8641 purple_timeout_remove(sip
->resendtimeout
);
8642 sip
->resendtimeout
= 0;
8643 if (sip
->timeouts
) {
8644 GSList
*entry
= sip
->timeouts
;
8646 struct scheduled_action
*sched_action
= entry
->data
;
8647 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
8648 purple_timeout_remove(sched_action
->timeout_handler
);
8649 if (sched_action
->destroy
) {
8650 (*sched_action
->destroy
)(sched_action
->payload
);
8652 g_free(sched_action
->name
);
8653 g_free(sched_action
);
8654 entry
= entry
->next
;
8657 g_slist_free(sip
->timeouts
);
8659 if (sip
->allow_events
) {
8660 GSList
*entry
= sip
->allow_events
;
8662 g_free(entry
->data
);
8663 entry
= entry
->next
;
8666 g_slist_free(sip
->allow_events
);
8668 if (sip
->containers
) {
8669 GSList
*entry
= sip
->containers
;
8671 free_container((struct sipe_container
*)entry
->data
);
8672 entry
= entry
->next
;
8675 g_slist_free(sip
->containers
);
8678 g_free(sip
->contact
);
8679 sip
->contact
= NULL
;
8681 g_free(sip
->regcallid
);
8682 sip
->regcallid
= NULL
;
8684 if (sip
->serveraddr
)
8685 g_free(sip
->serveraddr
);
8686 sip
->serveraddr
= NULL
;
8688 if (sip
->focus_factory_uri
)
8689 g_free(sip
->focus_factory_uri
);
8690 sip
->focus_factory_uri
= NULL
;
8693 sip
->processing_input
= FALSE
;
8696 sipe_ews_free(sip
->ews
);
8702 * A callback for g_hash_table_foreach_remove
8704 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
8705 SIPE_UNUSED_PARAMETER gpointer user_data
)
8707 sipe_free_buddy((struct sipe_buddy
*) buddy
);
8709 /* We must return TRUE as the key/value have already been deleted */
8713 static void sipe_close(PurpleConnection
*gc
)
8715 struct sipe_account_data
*sip
= gc
->proto_data
;
8718 /* leave all conversations */
8719 sipe_session_close_all(sip
);
8720 sipe_session_remove_all(sip
);
8723 sip_csta_close(sip
);
8726 if (PURPLE_CONNECTION_IS_CONNECTED(sip
->gc
)) {
8727 /* unsubscribe all */
8728 g_hash_table_foreach(sip
->subscriptions
, sipe_unsubscribe_cb
, sip
);
8731 do_register_exp(sip
, 0);
8734 sipe_connection_cleanup(sip
);
8735 g_free(sip
->sipdomain
);
8736 g_free(sip
->username
);
8738 g_free(sip
->password
);
8739 g_free(sip
->authdomain
);
8740 g_free(sip
->authuser
);
8741 g_free(sip
->status
);
8744 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
8745 g_hash_table_destroy(sip
->buddies
);
8746 g_hash_table_destroy(sip
->our_publications
);
8747 g_hash_table_destroy(sip
->user_state_publications
);
8748 g_hash_table_destroy(sip
->subscriptions
);
8749 g_hash_table_destroy(sip
->filetransfers
);
8752 GSList
*entry
= sip
->groups
;
8754 struct sipe_group
*group
= entry
->data
;
8755 g_free(group
->name
);
8757 entry
= entry
->next
;
8760 g_slist_free(sip
->groups
);
8762 if (sip
->our_publication_keys
) {
8763 GSList
*entry
= sip
->our_publication_keys
;
8765 g_free(entry
->data
);
8766 entry
= entry
->next
;
8769 g_slist_free(sip
->our_publication_keys
);
8771 while (sip
->transactions
)
8772 transactions_remove(sip
, sip
->transactions
->data
);
8774 g_free(gc
->proto_data
);
8775 gc
->proto_data
= NULL
;
8778 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
8779 SIPE_UNUSED_PARAMETER
void *user_data
)
8781 PurpleAccount
*acct
= purple_connection_get_account(gc
);
8782 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
8783 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
8785 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
8786 purple_conversation_present(conv
);
8790 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
8791 SIPE_UNUSED_PARAMETER
void *user_data
)
8794 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
8795 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
8798 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
8799 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
8801 PurpleNotifySearchResults
*results
;
8802 PurpleNotifySearchColumn
*column
;
8803 xmlnode
*searchResults
;
8805 int match_count
= 0;
8806 gboolean more
= FALSE
;
8809 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
8811 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
8812 if (!searchResults
) {
8813 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
8817 results
= purple_notify_searchresults_new();
8819 if (results
== NULL
) {
8820 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
8821 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
8823 xmlnode_free(searchResults
);
8827 column
= purple_notify_searchresults_column_new(_("User name"));
8828 purple_notify_searchresults_column_add(results
, column
);
8830 column
= purple_notify_searchresults_column_new(_("Name"));
8831 purple_notify_searchresults_column_add(results
, column
);
8833 column
= purple_notify_searchresults_column_new(_("Company"));
8834 purple_notify_searchresults_column_add(results
, column
);
8836 column
= purple_notify_searchresults_column_new(_("Country"));
8837 purple_notify_searchresults_column_add(results
, column
);
8839 column
= purple_notify_searchresults_column_new(_("Email"));
8840 purple_notify_searchresults_column_add(results
, column
);
8842 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
8845 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
8846 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
8847 g_strfreev(uri_parts
);
8849 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
8850 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
8851 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
8852 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
8854 purple_notify_searchresults_row_add(results
, row
);
8858 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
8859 char *data
= xmlnode_get_data_unescaped(mrow
);
8860 more
= (g_strcasecmp(data
, "true") == 0);
8864 secondary
= g_strdup_printf(
8865 dngettext(GETTEXT_PACKAGE
,
8866 "Found %d contact%s:",
8867 "Found %d contacts%s:", match_count
),
8868 match_count
, more
? _(" (more matched your query)") : "");
8870 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
8871 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
8872 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
8875 xmlnode_free(searchResults
);
8879 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
8881 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
8882 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
8888 PurpleRequestField
*field
= entries
->data
;
8889 const char *id
= purple_request_field_get_id(field
);
8890 const char *value
= purple_request_field_string_get_value(field
);
8892 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
8894 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
8895 } while ((entries
= g_list_next(entries
)) != NULL
);
8899 struct sipe_account_data
*sip
= gc
->proto_data
;
8900 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
8901 gchar
*query
= g_strjoinv(NULL
, attrs
);
8902 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
8903 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
8904 send_soap_request_with_cb(sip
, domain_uri
, body
,
8905 (TransCallback
) process_search_contact_response
, NULL
);
8914 static void sipe_show_find_contact(PurplePluginAction
*action
)
8916 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8917 PurpleRequestFields
*fields
;
8918 PurpleRequestFieldGroup
*group
;
8919 PurpleRequestField
*field
;
8921 fields
= purple_request_fields_new();
8922 group
= purple_request_field_group_new(NULL
);
8923 purple_request_fields_add_group(fields
, group
);
8925 field
= purple_request_field_string_new("givenName", _("First name"), NULL
, FALSE
);
8926 purple_request_field_group_add_field(group
, field
);
8927 field
= purple_request_field_string_new("sn", _("Last name"), NULL
, FALSE
);
8928 purple_request_field_group_add_field(group
, field
);
8929 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
8930 purple_request_field_group_add_field(group
, field
);
8931 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
8932 purple_request_field_group_add_field(group
, field
);
8934 purple_request_fields(gc
,
8936 _("Search for a contact"),
8937 _("Enter the information for the person you wish to find. Empty fields will be ignored."),
8939 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
8941 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
8944 static void sipe_show_about_plugin(PurplePluginAction
*action
)
8946 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8947 char *tmp
= g_strdup_printf(
8949 * Non-translatable parts, like markup, are hard-coded
8950 * into the format string. This requires more translatable
8951 * texts but it makes the translations less error prone.
8953 "<b><font size=\"+1\">SIPE " SIPE_VERSION
" </font></b><br/>"
8956 "<li> - MS Office Communications Server 2007 R2</li><br/>"
8957 "<li> - MS Office Communications Server 2007</li><br/>"
8958 "<li> - MS Live Communications Server 2005</li><br/>"
8959 "<li> - MS Live Communications Server 2003</li><br/>"
8960 "<li> - Reuters Messaging</li><br/>"
8962 /* 2 */ "%s: <a href=\"http://sipe.sourceforge.net\">http://sipe.sourceforge.net</a><br/>"
8963 /* 3,4 */ "%s: <a href=\"http://sourceforge.net/projects/sipe/forums/forum/688534\">%s</a><br/>"
8964 /* 5 */ "%s: <a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a><br/>"
8965 /* 6 */ "%s: GPLv2+<br/>"
8969 " - Reuters Messaging network<br/>"
8970 " - Deutsche Bank<br/>"
8971 " - Merrill Lynch<br/>"
8979 " - Alcatel-Lucent<br/>"
8982 /* 8,9 */ "%s<a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a>%s.<br/>"
8984 /* 10 */ "<b>%s:</b><br/>"
8985 " - Anibal Avelar<br/>"
8986 " - Gabriel Burt<br/>"
8987 " - Stefan Becker<br/>"
8989 " - Jakub Adam<br/>"
8990 " - Tomáš Hrabčík<br/>"
8994 /* The next 11 texts make up the SIPE about note text */
8995 /* About note, part 1/11: introduction */
8996 _("A third-party plugin implementing extended version of SIP/SIMPLE used by various products"),
8997 /* About note, part 2/11: home page URL (label) */
8999 /* About note, part 3/11: support forum URL (label) */
9001 /* About note, part 4/11: support forum name (hyperlink text) */
9003 /* About note, part 5/11: translation service URL (label) */
9005 /* About note, part 6/11: license type (label) */
9007 /* About note, part 7/11: known users */
9008 _("We support users in such organizations as"),
9009 /* About note, part 8/11: translation request, text before Transifex.net URL */
9010 /* append a space if text is not empty */
9011 _("Please help us to translate SIPE to your native language here at "),
9012 /* About note, part 9/11: translation request, text after Transifex.net URL */
9013 /* start with a space if text is not empty */
9014 _(" using convenient web interface"),
9015 /* About note, part 10/11: author list (header) */
9017 /* About note, part 11/11: Localization credit */
9018 /* PLEASE NOTE: do *NOT* simply translate the english original */
9019 /* but write something similar to the following sentence: */
9020 /* "Localization for <language name> (<language code>): <name>" */
9021 _("Original texts in English (en): SIPE developers")
9023 purple_notify_formatted(gc
, NULL
, " ", NULL
, tmp
, NULL
, NULL
);
9027 static void sipe_republish_calendar(PurplePluginAction
*action
)
9029 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
9030 struct sipe_account_data
*sip
= gc
->proto_data
;
9032 sipe_update_calendar(sip
);
9035 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
9039 struct sipe_publication
*publication
= value
;
9041 g_string_append_printf( str
,
9042 SIPE_PUB_XML_PUBLICATION_CLEAR
,
9043 publication
->category
,
9044 publication
->instance
,
9045 publication
->container
,
9046 publication
->version
,
9050 static void sipe_reset_status(PurplePluginAction
*action
)
9052 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
9053 struct sipe_account_data
*sip
= gc
->proto_data
;
9055 if (sip
->ocs2007
) /* 2007+ */
9057 GString
* str
= g_string_new(NULL
);
9058 gchar
*publications
;
9060 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
9061 purple_debug_info("sipe", "sipe_reset_status: no userState publications, exiting.\n");
9065 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
9066 publications
= g_string_free(str
, FALSE
);
9068 send_presence_publish(sip
, publications
);
9069 g_free(publications
);
9073 send_presence_soap0(sip
, FALSE
, TRUE
);
9077 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
9080 PurpleConnection
*gc
= (PurpleConnection
*)context
;
9081 struct sipe_account_data
*sip
= gc
->proto_data
;
9083 PurplePluginAction
*act
;
9084 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
9086 act
= purple_plugin_action_new(_("About SIPE plugin..."), sipe_show_about_plugin
);
9087 menu
= g_list_prepend(menu
, act
);
9089 act
= purple_plugin_action_new(_("Contact search..."), sipe_show_find_contact
);
9090 menu
= g_list_prepend(menu
, act
);
9092 if (sipe_strequal(calendar
, "EXCH")) {
9093 act
= purple_plugin_action_new(_("Republish Calendar"), sipe_republish_calendar
);
9094 menu
= g_list_prepend(menu
, act
);
9097 act
= purple_plugin_action_new(_("Reset status"), sipe_reset_status
);
9098 menu
= g_list_prepend(menu
, act
);
9100 menu
= g_list_reverse(menu
);
9105 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
9109 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9115 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9121 static char *sipe_status_text(PurpleBuddy
*buddy
)
9123 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9124 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9125 const char *status_id
= purple_status_get_id(status
);
9126 struct sipe_account_data
*sip
= (struct sipe_account_data
*)buddy
->account
->gc
->proto_data
;
9127 struct sipe_buddy
*sbuddy
;
9130 if (!sip
) return NULL
; /* happens on pidgin exit */
9132 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9134 const char *activity_str
= sbuddy
->activity
?
9136 sipe_strequal(status_id
, SIPE_STATUS_ID_BUSY
) || sipe_strequal(status_id
, SIPE_STATUS_ID_BRB
) ?
9137 purple_status_get_name(status
) : NULL
;
9139 if (activity_str
&& sbuddy
->note
)
9141 text
= g_strdup_printf("%s - <i>%s</i>", activity_str
, sbuddy
->note
);
9143 else if (activity_str
)
9145 text
= g_strdup(activity_str
);
9147 else if (sbuddy
->note
)
9149 text
= g_strdup_printf("<i>%s</i>", sbuddy
->note
);
9156 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
9158 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9159 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9160 struct sipe_account_data
*sip
;
9161 struct sipe_buddy
*sbuddy
;
9163 gboolean is_oof_note
= FALSE
;
9164 char *activity
= NULL
;
9165 char *calendar
= NULL
;
9166 char *meeting_subject
= NULL
;
9167 char *meeting_location
= NULL
;
9169 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
9170 if (sip
) //happens on pidgin exit
9172 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9175 note
= sbuddy
->note
;
9176 is_oof_note
= sbuddy
->is_oof_note
;
9177 activity
= sbuddy
->activity
;
9178 calendar
= sipe_cal_get_description(sbuddy
);
9179 meeting_subject
= sbuddy
->meeting_subject
;
9180 meeting_location
= sbuddy
->meeting_location
;
9185 if (purple_presence_is_online(presence
))
9187 const char *status_str
= activity
? activity
: purple_status_get_name(status
);
9189 purple_notify_user_info_add_pair(user_info
, _("Status"), status_str
);
9191 if (purple_presence_is_online(presence
) &&
9192 !is_empty(calendar
))
9194 purple_notify_user_info_add_pair(user_info
, _("Calendar"), calendar
);
9197 if (!is_empty(meeting_location
))
9199 purple_notify_user_info_add_pair(user_info
, _("Meeting in"), meeting_location
);
9201 if (!is_empty(meeting_subject
))
9203 purple_notify_user_info_add_pair(user_info
, _("Meeting about"), meeting_subject
);
9208 char *tmp
= g_strdup_printf("<i>%s</i>", note
);
9209 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, note
);
9211 purple_notify_user_info_add_pair(user_info
, is_oof_note
? _("Out of office note") : _("Note"), tmp
);
9217 #if PURPLE_VERSION_CHECK(2,5,0)
9219 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
9222 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
9223 g_hash_table_insert(table
, "login_label", (gpointer
)_("user@company.com"));
9228 static PurpleBuddy
*
9229 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
9232 const gchar
*server_alias
, *email
;
9233 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
9235 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
9237 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
9239 server_alias
= purple_buddy_get_server_alias(buddy
);
9241 purple_blist_server_alias_buddy(clone
, server_alias
);
9244 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9246 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
9249 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
9251 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
9256 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
9258 PurpleBuddy
*buddy
, *b
;
9259 PurpleConnection
*gc
;
9260 PurpleGroup
* group
= purple_find_group(group_name
);
9262 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
9264 buddy
= (PurpleBuddy
*)node
;
9266 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
9267 gc
= purple_account_get_connection(buddy
->account
);
9269 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
9271 purple_blist_add_buddy_clone(group
, buddy
);
9274 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
9278 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
9280 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9282 purple_debug_info("sipe", "sipe_buddy_menu_chat_new_cb: buddy->name=%s\n", buddy
->name
);
9284 /* 2007+ conference */
9287 sipe_conf_add(sip
, buddy
->name
);
9289 else /* 2005- multiparty chat */
9291 gchar
*self
= sip_uri_self(sip
);
9292 struct sip_session
*session
;
9294 session
= sipe_session_add_chat(sip
);
9295 session
->chat_title
= sipe_chat_get_name(session
->callid
);
9296 session
->roster_manager
= g_strdup(self
);
9298 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, session
->chat_title
);
9299 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
9300 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
9301 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
9308 sipe_is_election_finished(struct sip_session
*session
)
9310 gboolean res
= TRUE
;
9312 SIPE_DIALOG_FOREACH
{
9313 if (dialog
->election_vote
== 0) {
9317 } SIPE_DIALOG_FOREACH_END
;
9320 session
->is_voting_in_progress
= FALSE
;
9326 sipe_election_start(struct sipe_account_data
*sip
,
9327 struct sip_session
*session
)
9329 int election_timeout
;
9331 if (session
->is_voting_in_progress
) {
9332 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
9335 session
->is_voting_in_progress
= TRUE
;
9337 session
->bid
= rand();
9339 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
9341 SIPE_DIALOG_FOREACH
{
9342 /* reset election_vote for each chat participant */
9343 dialog
->election_vote
= 0;
9345 /* send RequestRM to each chat participant*/
9346 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
9347 } SIPE_DIALOG_FOREACH_END
;
9349 election_timeout
= 15; /* sec */
9350 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
9354 * @param who a URI to whom to invite to chat
9357 sipe_invite_to_chat(struct sipe_account_data
*sip
,
9358 struct sip_session
*session
,
9362 if (session
->focus_uri
)
9364 sipe_invite_conf(sip
, session
, who
);
9366 else /* a multi-party chat */
9368 gchar
*self
= sip_uri_self(sip
);
9369 if (session
->roster_manager
) {
9370 if (sipe_strequal(session
->roster_manager
, self
)) {
9371 sipe_invite(sip
, session
, who
, NULL
, NULL
, NULL
, FALSE
);
9373 sipe_refer(sip
, session
, who
);
9376 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite: no RM available\n");
9378 session
->pending_invite_queue
= slist_insert_unique_sorted(
9379 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
9381 sipe_election_start(sip
, session
);
9388 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
9389 struct sip_session
*session
)
9392 GSList
*entry
= session
->pending_invite_queue
;
9395 invitee
= entry
->data
;
9396 sipe_invite_to_chat(sip
, session
, invitee
);
9397 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
9403 sipe_election_result(struct sipe_account_data
*sip
,
9406 struct sip_session
*session
= (struct sip_session
*)sess
;
9408 gboolean has_won
= TRUE
;
9410 if (session
->roster_manager
) {
9411 purple_debug_info("sipe",
9412 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
9416 session
->is_voting_in_progress
= FALSE
;
9418 SIPE_DIALOG_FOREACH
{
9419 if (dialog
->election_vote
< 0) {
9421 rival
= dialog
->with
;
9424 } SIPE_DIALOG_FOREACH_END
;
9427 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
9429 session
->roster_manager
= sip_uri_self(sip
);
9431 SIPE_DIALOG_FOREACH
{
9432 /* send SetRM to each chat participant*/
9433 sipe_send_election_set_rm(sip
, dialog
);
9434 } SIPE_DIALOG_FOREACH_END
;
9436 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
9440 sipe_process_pending_invite_queue(sip
, session
);
9444 * For 2007+ conference only.
9447 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9449 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9450 struct sip_session
*session
;
9452 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
9453 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_title=%s\n", chat_title
);
9455 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9457 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
9461 * For 2007+ conference only.
9464 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9466 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9467 struct sip_session
*session
;
9469 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: buddy->name=%s\n", buddy
->name
);
9470 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: chat_title=%s\n", chat_title
);
9472 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9474 sipe_conf_delete_user(sip
, session
, buddy
->name
);
9478 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, char *chat_title
)
9480 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9481 struct sip_session
*session
;
9483 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: buddy->name=%s\n", buddy
->name
);
9484 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: chat_title=%s\n", chat_title
);
9486 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9488 sipe_invite_to_chat(sip
, session
, buddy
->name
);
9492 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
9494 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9496 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: buddy->name=%s\n", buddy
->name
);
9498 char *tel_uri
= sip_to_tel_uri(phone
);
9500 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: going to call number: %s\n", tel_uri
? tel_uri
: "");
9501 sip_csta_make_call(sip
, tel_uri
);
9508 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
9511 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
9513 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9516 char *mailto
= g_strdup_printf("mailto:%s", email
);
9517 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
9521 char *const parmList
[] = {"xdg-email", mailto
, NULL
};
9522 if ((pid
= fork()) == -1)
9524 purple_debug_info("sipe", "fork() error\n");
9528 execvp(parmList
[0], parmList
);
9529 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
9537 //@TODO resolve env variable %WINDIR% first
9538 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
9541 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
9550 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
9555 * A menu which appear when right-clicking on buddy in contact list.
9558 sipe_buddy_menu(PurpleBuddy
*buddy
)
9560 PurpleBlistNode
*g_node
;
9561 PurpleGroup
*group
, *gr_parent
;
9562 PurpleMenuAction
*act
;
9564 GList
*menu_groups
= NULL
;
9565 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9568 const char *phone_disp_str
;
9569 gchar
*self
= sip_uri_self(sip
);
9571 SIPE_SESSION_FOREACH
{
9572 if (g_ascii_strcasecmp(self
, buddy
->name
) && session
->chat_title
&& session
->conv
)
9574 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
9576 PurpleConvChatBuddyFlags flags
;
9577 PurpleConvChatBuddyFlags flags_us
;
9579 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
9580 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9581 if (session
->focus_uri
9582 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
9583 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9585 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"), session
->chat_title
);
9586 act
= purple_menu_action_new(label
,
9587 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
9588 session
->chat_title
, NULL
);
9590 menu
= g_list_prepend(menu
, act
);
9593 if (session
->focus_uri
9594 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9596 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_title
);
9597 act
= purple_menu_action_new(label
,
9598 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
9599 session
->chat_title
, NULL
);
9601 menu
= g_list_prepend(menu
, act
);
9606 if (!session
->focus_uri
9607 || (session
->focus_uri
&& !session
->locked
))
9609 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_title
);
9610 act
= purple_menu_action_new(label
,
9611 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
9612 session
->chat_title
, NULL
);
9614 menu
= g_list_prepend(menu
, act
);
9618 } SIPE_SESSION_FOREACH_END
;
9620 act
= purple_menu_action_new(_("New chat"),
9621 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
9623 menu
= g_list_prepend(menu
, act
);
9625 if (sip
->csta
&& !sip
->csta
->line_status
) {
9628 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
9629 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
9631 gchar
*label
= g_strdup_printf(_("Work %s"),
9632 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9633 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9637 menu
= g_list_prepend(menu
, act
);
9641 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
9642 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
9644 gchar
*label
= g_strdup_printf(_("Mobile %s"),
9645 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9646 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9650 menu
= g_list_prepend(menu
, act
);
9654 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
9655 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
9657 gchar
*label
= g_strdup_printf(_("Home %s"),
9658 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9659 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9663 menu
= g_list_prepend(menu
, act
);
9667 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
9668 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
9670 gchar
*label
= g_strdup_printf(_("Other %s"),
9671 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9672 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9676 menu
= g_list_prepend(menu
, act
);
9680 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
9681 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
9683 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
9684 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9685 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9689 menu
= g_list_prepend(menu
, act
);
9693 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9695 act
= purple_menu_action_new(_("Send email..."),
9696 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
9698 menu
= g_list_prepend(menu
, act
);
9701 gr_parent
= purple_buddy_get_group(buddy
);
9702 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
9703 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
9706 group
= (PurpleGroup
*)g_node
;
9707 if (group
== gr_parent
)
9710 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
9713 act
= purple_menu_action_new(purple_group_get_name(group
),
9714 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
9716 menu_groups
= g_list_prepend(menu_groups
, act
);
9718 menu_groups
= g_list_reverse(menu_groups
);
9720 act
= purple_menu_action_new(_("Copy to"),
9723 menu
= g_list_prepend(menu
, act
);
9724 menu
= g_list_reverse(menu
);
9731 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
9733 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9734 struct sip_session
*session
;
9736 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9737 sipe_conf_modify_conference_lock(sip
, session
, locked
);
9741 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
9743 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
9744 sipe_conf_modify_lock(chat
, FALSE
);
9748 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
9750 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
9751 sipe_conf_modify_lock(chat
, TRUE
);
9755 sipe_chat_menu(PurpleChat
*chat
)
9757 PurpleMenuAction
*act
;
9758 PurpleConvChatBuddyFlags flags_us
;
9760 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9761 struct sip_session
*session
;
9764 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9765 if (!session
) return NULL
;
9767 self
= sip_uri_self(sip
);
9768 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9770 if (session
->focus_uri
9771 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9773 if (session
->locked
) {
9774 act
= purple_menu_action_new(_("Unlock"),
9775 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
9777 menu
= g_list_prepend(menu
, act
);
9779 act
= purple_menu_action_new(_("Lock"),
9780 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
9782 menu
= g_list_prepend(menu
, act
);
9786 menu
= g_list_reverse(menu
);
9793 sipe_blist_node_menu(PurpleBlistNode
*node
)
9795 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
9796 return sipe_buddy_menu((PurpleBuddy
*) node
);
9797 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
9798 return sipe_chat_menu((PurpleChat
*)node
);
9805 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
9807 char *uri
= trans
->payload
->data
;
9809 PurpleNotifyUserInfo
*info
;
9810 PurpleBuddy
*pbuddy
= NULL
;
9811 struct sipe_buddy
*sbuddy
;
9812 const char *alias
= NULL
;
9813 char *device_name
= NULL
;
9814 char *server_alias
= NULL
;
9815 char *phone_number
= NULL
;
9818 char *first_name
= NULL
;
9819 char *last_name
= NULL
;
9821 if (!sip
) return FALSE
;
9823 purple_debug_info("sipe", "Fetching %s's user info for %s\n", uri
, sip
->username
);
9825 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
9826 alias
= purple_buddy_get_local_alias(pbuddy
);
9828 //will query buddy UA's capabilities and send answer to log
9829 sipe_options_request(sip
, uri
);
9831 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
9833 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
9836 info
= purple_notify_user_info_new();
9838 if (msg
->response
!= 200) {
9839 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
9841 xmlnode
*searchResults
;
9844 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
9845 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
9846 if (!searchResults
) {
9847 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
9848 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
9850 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
9851 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
9852 phone_number
= g_strdup(xmlnode_get_attrib(mrow
, "phone"));
9854 /* For 2007 system we will take this from ContactCard -
9855 * it has cleaner tel: URIs at least
9857 if (!sip
->ocs2007
) {
9858 char *tel_uri
= sip_to_tel_uri(phone_number
);
9859 /* trims its parameters, so call first */
9860 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, server_alias
);
9861 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
9862 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
9863 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, phone_number
);
9867 if (server_alias
&& strlen(server_alias
) > 0) {
9868 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9870 if ((value
= xmlnode_get_attrib(mrow
, "title")) && strlen(value
) > 0) {
9871 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
9873 if ((value
= xmlnode_get_attrib(mrow
, "office")) && strlen(value
) > 0) {
9874 purple_notify_user_info_add_pair(info
, _("Office"), value
);
9876 if (phone_number
&& strlen(phone_number
) > 0) {
9877 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
9879 if ((value
= xmlnode_get_attrib(mrow
, "company")) && strlen(value
) > 0) {
9880 purple_notify_user_info_add_pair(info
, _("Company"), value
);
9882 if ((value
= xmlnode_get_attrib(mrow
, "city")) && strlen(value
) > 0) {
9883 purple_notify_user_info_add_pair(info
, _("City"), value
);
9885 if ((value
= xmlnode_get_attrib(mrow
, "state")) && strlen(value
) > 0) {
9886 purple_notify_user_info_add_pair(info
, _("State"), value
);
9888 if ((value
= xmlnode_get_attrib(mrow
, "country")) && strlen(value
) > 0) {
9889 purple_notify_user_info_add_pair(info
, _("Country"), value
);
9891 if (email
&& strlen(email
) > 0) {
9892 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9896 xmlnode_free(searchResults
);
9899 purple_notify_user_info_add_section_break(info
);
9901 if (is_empty(server_alias
)) {
9902 g_free(server_alias
);
9903 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
9905 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9909 /* present alias if it differs from server alias */
9910 if (alias
&& !sipe_strequal(alias
, server_alias
))
9912 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
9915 if (is_empty(email
)) {
9917 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
9919 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9923 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
9925 purple_notify_user_info_add_pair(info
, _("Site"), site
);
9928 sipe_get_first_last_names(sip
, uri
, &first_name
, &last_name
);
9929 if (first_name
&& last_name
) {
9930 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
9932 purple_notify_user_info_add_pair(info
, _("Find on LinkedIn"), link
);
9939 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
9942 /* show a buddy's user info in a nice dialog box */
9943 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
9944 uri
, /* buddy's URI */
9946 NULL
, /* callback called when dialog closed */
9947 NULL
); /* userdata for callback */
9949 g_free(phone_number
);
9950 g_free(server_alias
);
9952 g_free(device_name
);
9958 * AD search first, LDAP based
9960 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
9962 struct sipe_account_data
*sip
= gc
->proto_data
;
9963 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
9964 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
9965 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
9966 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
9968 payload
->destroy
= g_free
;
9969 payload
->data
= g_strdup(username
);
9971 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
9972 send_soap_request_with_cb(sip
, domain_uri
, body
,
9973 (TransCallback
) process_get_info_response
, payload
);
9979 static PurplePlugin
*my_protocol
= NULL
;
9981 static PurplePluginProtocolInfo prpl_info
=
9983 OPT_PROTO_CHAT_TOPIC
,
9984 NULL
, /* user_splits */
9985 NULL
, /* protocol_options */
9986 NO_BUDDY_ICONS
, /* icon_spec */
9987 sipe_list_icon
, /* list_icon */
9988 NULL
, /* list_emblems */
9989 sipe_status_text
, /* status_text */
9990 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
9991 sipe_status_types
, /* away_states */
9992 sipe_blist_node_menu
, /* blist_node_menu */
9993 NULL
, /* chat_info */
9994 NULL
, /* chat_info_defaults */
9995 sipe_login
, /* login */
9996 sipe_close
, /* close */
9997 sipe_im_send
, /* send_im */
9998 NULL
, /* set_info */ // TODO maybe
9999 sipe_send_typing
, /* send_typing */
10000 sipe_get_info
, /* get_info */
10001 sipe_set_status
, /* set_status */
10002 sipe_set_idle
, /* set_idle */
10003 NULL
, /* change_passwd */
10004 sipe_add_buddy
, /* add_buddy */
10005 NULL
, /* add_buddies */
10006 sipe_remove_buddy
, /* remove_buddy */
10007 NULL
, /* remove_buddies */
10008 sipe_add_permit
, /* add_permit */
10009 sipe_add_deny
, /* add_deny */
10010 sipe_add_deny
, /* rem_permit */
10011 sipe_add_permit
, /* rem_deny */
10012 dummy_permit_deny
, /* set_permit_deny */
10013 NULL
, /* join_chat */
10014 NULL
, /* reject_chat */
10015 NULL
, /* get_chat_name */
10016 sipe_chat_invite
, /* chat_invite */
10017 sipe_chat_leave
, /* chat_leave */
10018 NULL
, /* chat_whisper */
10019 sipe_chat_send
, /* chat_send */
10020 sipe_keep_alive
, /* keepalive */
10021 NULL
, /* register_user */
10022 NULL
, /* get_cb_info */ // deprecated
10023 NULL
, /* get_cb_away */ // deprecated
10024 sipe_alias_buddy
, /* alias_buddy */
10025 sipe_group_buddy
, /* group_buddy */
10026 sipe_rename_group
, /* rename_group */
10027 NULL
, /* buddy_free */
10028 sipe_convo_closed
, /* convo_closed */
10029 purple_normalize_nocase
, /* normalize */
10030 NULL
, /* set_buddy_icon */
10031 sipe_remove_group
, /* remove_group */
10032 NULL
, /* get_cb_real_name */ // TODO?
10033 NULL
, /* set_chat_topic */
10034 NULL
, /* find_blist_chat */
10035 NULL
, /* roomlist_get_list */
10036 NULL
, /* roomlist_cancel */
10037 NULL
, /* roomlist_expand_category */
10038 NULL
, /* can_receive_file */
10039 sipe_ft_send_file
, /* send_file */
10040 sipe_ft_new_xfer
, /* new_xfer */
10041 NULL
, /* offline_message */
10042 NULL
, /* whiteboard_prpl_ops */
10043 sipe_send_raw
, /* send_raw */
10044 NULL
, /* roomlist_room_serialize */
10045 NULL
, /* unregister_user */
10046 NULL
, /* send_attention */
10047 NULL
, /* get_attention_types */
10048 #if !PURPLE_VERSION_CHECK(2,5,0)
10049 /* Backward compatibility when compiling against 2.4.x API */
10050 (void (*)(void)) /* _purple_reserved4 */
10052 sizeof(PurplePluginProtocolInfo
), /* struct_size */
10053 #if PURPLE_VERSION_CHECK(2,5,0)
10054 sipe_get_account_text_table
, /* get_account_text_table */
10055 #if PURPLE_VERSION_CHECK(2,6,0)
10056 NULL
, /* initiate_media */
10057 NULL
, /* get_media_caps */
10063 static PurplePluginInfo info
= {
10064 PURPLE_PLUGIN_MAGIC
,
10065 PURPLE_MAJOR_VERSION
,
10066 PURPLE_MINOR_VERSION
,
10067 PURPLE_PLUGIN_PROTOCOL
, /**< type */
10068 NULL
, /**< ui_requirement */
10070 NULL
, /**< dependencies */
10071 PURPLE_PRIORITY_DEFAULT
, /**< priority */
10072 "prpl-sipe", /**< id */
10073 "Office Communicator", /**< name */
10074 SIPE_VERSION
, /**< version */
10075 "Microsoft Office Communicator Protocol Plugin", /**< summary */
10076 "A plugin for the extended SIP/SIMPLE protocol used by " /**< description */
10077 "Microsoft Live/Office Communications Server (LCS2005/OCS2007+)", /**< description */
10078 "Anibal Avelar <avelar@gmail.com>, " /**< author */
10079 "Gabriel Burt <gburt@novell.com>, " /**< author */
10080 "Stefan Becker <stefan.becker@nokia.com>, " /**< author */
10081 "pier11 <pier11@operamail.com>", /**< author */
10082 "http://sipe.sourceforge.net/", /**< homepage */
10083 sipe_plugin_load
, /**< load */
10084 sipe_plugin_unload
, /**< unload */
10085 sipe_plugin_destroy
, /**< destroy */
10086 NULL
, /**< ui_info */
10087 &prpl_info
, /**< extra_info */
10096 static void sipe_plugin_destroy(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
10102 entry
= prpl_info
.protocol_options
;
10104 purple_account_option_destroy(entry
->data
);
10105 entry
= g_list_delete_link(entry
, entry
);
10107 prpl_info
.protocol_options
= NULL
;
10109 entry
= prpl_info
.user_splits
;
10111 purple_account_user_split_destroy(entry
->data
);
10112 entry
= g_list_delete_link(entry
, entry
);
10114 prpl_info
.user_splits
= NULL
;
10117 static void init_plugin(PurplePlugin
*plugin
)
10119 PurpleAccountUserSplit
*split
;
10120 PurpleAccountOption
*option
;
10126 purple_debug_info(PACKAGE
, "bindtextdomain = %s\n", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
10127 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s\n",
10128 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
10129 textdomain(GETTEXT_PACKAGE
);
10132 purple_plugin_register(plugin
);
10134 split
= purple_account_user_split_new(_("Login\n user or DOMAIN\\user or\n user@company.com"), NULL
, ',');
10135 purple_account_user_split_set_reverse(split
, FALSE
);
10136 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
10138 option
= purple_account_option_string_new(_("Server[:Port]\n(leave empty for auto-discovery)"), "server", "");
10139 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10141 option
= purple_account_option_list_new(_("Connection type"), "transport", NULL
);
10142 purple_account_option_add_list_item(option
, _("Auto"), "auto");
10143 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
10144 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
10145 purple_account_option_add_list_item(option
, _("UDP"), "udp");
10146 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10148 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
10149 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
10151 option
= purple_account_option_string_new(_("User Agent"), "useragent", "");
10152 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10154 #ifdef USE_KERBEROS
10155 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
10156 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10158 /* Suitable for sspi/NTLM, sspi/Kerberos and krb5 security mechanisms
10159 * No login/password is taken into account if this option present,
10160 * instead used default credentials stored in OS.
10162 option
= purple_account_option_bool_new(_("Use Single Sign-On"), "sso", TRUE
);
10163 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10166 option
= purple_account_option_list_new(_("Calendar source"), "calendar", NULL
);
10167 purple_account_option_add_list_item(option
, _("Exchange 2007/2010"), "EXCH");
10168 purple_account_option_add_list_item(option
, _("None"), "NONE");
10169 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10171 /** Example: https://server.company.com/EWS/Exchange.asmx */
10172 option
= purple_account_option_string_new(_("Email services URL\n(leave empty for auto-discovery)"), "email_url", "");
10173 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10175 option
= purple_account_option_string_new(_("Email address\n(if different from Username)"), "email", "");
10176 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10178 /** Example: DOMAIN\user or user@company.com */
10179 option
= purple_account_option_string_new(_("Email login\n(if different from Login)"), "email_login", "");
10180 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10182 option
= purple_account_option_string_new(_("Email password\n(if different from Password)"), "email_password", "");
10183 purple_account_option_set_masked(option
, TRUE
);
10184 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10186 my_protocol
= plugin
;
10189 PURPLE_INIT_PLUGIN(sipe
, init_plugin
, info
);
10194 c-file-style: "bsd"
10195 indent-tabs-mode: t