6 * Copyright (C) 2010 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2010 pier11 <pier11@operamail.com>
8 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
9 * Copyright (C) 2009 pier11 <pier11@operamail.com>
10 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
11 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
12 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
15 * Thanks to Google's Summer of Code Program and the helpful mentors
18 * Session-based SIP MESSAGE documentation:
19 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
44 #define _LIBC_INTERNAL_
47 #include "libc_interface.h"
49 #include <sys/types.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
64 #include "connection.h"
65 #include "conversation.h"
68 #include "circbuffer.h"
79 #include "savedstatuses.h"
85 #include "core-depurple.h" /* Temporary for the core de-purple transition */
87 #include "sipe-common.h"
92 #include "sipe-chat.h"
93 #include "sipe-conf.h"
94 #include "sipe-dialog.h"
98 #include "sipe-session.h"
99 #include "sipe-sign.h"
100 #include "sipe-utils.h"
101 #include "sipe-xml.h"
102 #include "http-conn.h"
106 /* Backward compatibility when compiling against 2.4.x API */
107 #if !PURPLE_VERSION_CHECK(2,5,0)
108 #define PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY 0x0100
111 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
113 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
114 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
116 /* Keep in sync with sipe_transport_type! */
117 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
118 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
120 /* Status identifiers (see also: sipe_status_types()) */
121 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
122 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
123 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
124 /* PURPLE_STATUS_UNAVAILABLE: */
125 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
126 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
127 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
128 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
129 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
130 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
131 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
132 /* PURPLE_STATUS_AWAY: */
133 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
134 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
135 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
136 /** Reuters status (user settable) */
137 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
138 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
139 /* ??? PURPLE_STATUS_MOBILE */
140 /* ??? PURPLE_STATUS_TUNE */
142 /* Status attributes (see also sipe_status_types() */
143 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
145 #define SDP_ACCEPT_TYPES "text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml text/x-msmsgsinvite"
147 static struct sipe_activity_map_struct
152 const char *status_id
;
154 } const sipe_activity_map
[] =
156 /* This has nothing to do with Availability numbers, like 3500 (online).
157 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
159 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
160 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
161 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , NULL
},
162 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
163 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("Busy-Idle") , NULL
},
164 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
165 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
166 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
167 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , NULL
},
168 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
169 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , NULL
},
170 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , NULL
},
171 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , NULL
},
172 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
173 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
175 /** @param x is sipe_activity */
176 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
179 /* Action name templates */
180 #define ACTION_NAME_PRESENCE "<presence><%s>"
183 sipe_get_activity_by_token(const char *token
)
187 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
189 if (sipe_strequal(token
, sipe_activity_map
[i
].token
))
190 return sipe_activity_map
[i
].type
;
193 return sipe_activity_map
[0].type
;
197 sipe_get_activity_desc_by_token(const char *token
)
199 if (!token
) return NULL
;
201 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token
));
204 /** Allows to send typed messages from chat window again after account reinstantiation. */
206 sipe_rejoin_chat(PurpleConversation
*conv
)
208 if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
&&
209 PURPLE_CONV_CHAT(conv
)->left
)
211 PURPLE_CONV_CHAT(conv
)->left
= FALSE
;
212 purple_conversation_update(conv
, PURPLE_CONV_UPDATE_CHATLEFT
);
216 static char *genbranch()
218 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
219 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
220 rand() & 0xFFFF, rand() & 0xFFFF);
224 static char *default_ua
= NULL
;
226 sipe_get_useragent(struct sipe_account_data
*sip
)
228 const char *useragent
= purple_account_get_string(sip
->account
, "useragent", "");
229 if (is_empty(useragent
)) {
231 /*@TODO: better approach to define _user_ OS, it's version and host architecture */
233 #if defined(__linux__) || defined(__linux) || defined(__LINUX__)
234 #define SIPE_TARGET_PLATFORM "linux"
235 #elif defined(__NetBSD__) ||defined( __OpenBSD__) || defined(__FreeBSD__)
236 #define SIPE_TARGET_PLATFORM "bsd"
237 #elif defined(__APPLE__) || defined(__MACOS__)
238 #define SIPE_TARGET_PLATFORM "macosx"
239 #elif defined(_AIX) || defined(__AIX__) || defined(__aix__)
240 #define SIPE_TARGET_PLATFORM "aix"
241 #elif defined(__solaris__) || defined(__sun)
242 #define SIPE_TARGET_PLATFORM "sun"
243 #elif defined(_WIN32)
244 #define SIPE_TARGET_PLATFORM "win"
245 #elif defined(__CYGWIN__)
246 #define SIPE_TARGET_PLATFORM "cygwin"
247 #elif defined(__hpux__)
248 #define SIPE_TARGET_PLATFORM "hpux"
249 #elif defined(__sgi__)
250 #define SIPE_TARGET_PLATFORM "irix"
252 #define SIPE_TARGET_PLATFORM "unknown"
255 #if defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)
256 #define SIPE_TARGET_ARCH "x86_64"
257 #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386)
258 #define SIPE_TARGET_ARCH "i386"
259 #elif defined(__ppc64__)
260 #define SIPE_TARGET_ARCH "ppc64"
261 #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR)
262 #define SIPE_TARGET_ARCH "ppc"
263 #elif defined(__hppa__) || defined(__hppa)
264 #define SIPE_TARGET_ARCH "hppa"
265 #elif defined(__mips__) || defined(__mips) || defined(_MIPS_ARCH) || defined(_M_MRX000)
266 #define SIPE_TARGET_ARCH "mips"
267 #elif defined(__s390__) || defined(__s390) || defined(__s390x__) || defined(__s390x)
268 #define SIPE_TARGET_ARCH "s390"
269 #elif defined(__sparc__) || defined(__sparc) || defined(__sparcv8)
270 #define SIPE_TARGET_ARCH "sparc"
271 #elif defined(__arm__)
272 #define SIPE_TARGET_ARCH "arm"
274 #define SIPE_TARGET_ARCH "other"
277 default_ua
= g_strdup_printf("Purple/%s Sipe/" PACKAGE_VERSION
" (" SIPE_TARGET_PLATFORM
"-" SIPE_TARGET_ARCH
"; %s)",
278 purple_core_get_version(),
279 sip
->server_version
? sip
->server_version
: "");
281 useragent
= default_ua
;
286 static const char *sipe_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount
*a
,
287 SIPE_UNUSED_PARAMETER PurpleBuddy
*b
)
292 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
);
294 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
295 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
298 static void sipe_close(PurpleConnection
*gc
);
300 static void send_presence_status(struct sipe_account_data
*sip
);
302 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
304 static void sipe_keep_alive(PurpleConnection
*gc
)
306 struct sipe_account_data
*sip
= gc
->proto_data
;
307 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
308 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
309 gchar buf
[2] = {0, 0};
310 purple_debug_info("sipe", "sending keep alive\n");
311 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
313 time_t now
= time(NULL
);
314 if ((sip
->keepalive_timeout
> 0) &&
315 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
) &&
316 ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
318 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
319 sendout_pkt(gc
, "\r\n\r\n");
320 sip
->last_keepalive
= now
;
325 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
327 struct sip_connection
*ret
= NULL
;
328 GSList
*entry
= sip
->openconns
;
331 if (ret
->fd
== fd
) return ret
;
337 static void sipe_auth_free(struct sip_auth
*auth
)
339 g_free(auth
->opaque
);
343 g_free(auth
->target
);
346 auth
->type
= AUTH_TYPE_UNSET
;
349 g_free(auth
->gssapi_data
);
350 auth
->gssapi_data
= NULL
;
351 sip_sec_destroy_context(auth
->gssapi_context
);
352 auth
->gssapi_context
= NULL
;
355 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
357 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
359 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
363 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
365 struct sip_connection
*conn
= connection_find(sip
, fd
);
367 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
368 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
374 static void connection_free_all(struct sipe_account_data
*sip
)
376 struct sip_connection
*ret
= NULL
;
377 GSList
*entry
= sip
->openconns
;
380 connection_remove(sip
, ret
->fd
);
381 entry
= sip
->openconns
;
386 sipe_make_signature(struct sipe_account_data
*sip
,
389 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
392 const char *authuser
= sip
->authuser
;
396 if (!authuser
|| strlen(authuser
) < 1) {
397 authuser
= sip
->username
;
400 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
401 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
404 // If we have a signature for the message, include that
405 if (msg
->signature
) {
406 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
);
409 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
410 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
413 gchar
*sign_str
= NULL
;
415 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
418 purple_account_get_bool(sip
->account
, "sso", TRUE
),
419 sip
->authdomain
? sip
->authdomain
: "",
424 if (!gssapi_data
|| !auth
->gssapi_context
) {
425 sip
->gc
->wants_to_die
= TRUE
;
426 purple_connection_error(sip
->gc
, _("Failed to authenticate to server"));
430 if (auth
->version
> 3) {
431 sipe_make_signature(sip
, msg
);
432 sign_str
= g_strdup_printf(", crand=\"%s\", cnum=\"%s\", response=\"%s\"",
433 msg
->rand
, msg
->num
, msg
->signature
);
435 sign_str
= g_strdup("");
438 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
439 version_str
= auth
->version
> 2 ? g_strdup_printf(", version=%d", auth
->version
) : g_strdup("");
440 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
);
448 version_str
= auth
->version
> 2 ? g_strdup_printf(", version=%d", auth
->version
) : g_strdup("");
449 ret
= g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"%s", auth_protocol
, auth
->realm
, auth
->target
, version_str
);
453 } else { /* Digest */
455 /* Calculate new session key */
457 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
458 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
459 authuser
, auth
->realm
, sip
->password
,
460 auth
->gssapi_data
, NULL
);
463 sprintf(noncecount
, "%08d", auth
->nc
++);
464 response
= purple_cipher_http_digest_calculate_response("md5",
465 msg
->method
, msg
->target
, NULL
, NULL
,
466 auth
->gssapi_data
, noncecount
, NULL
,
468 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
470 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
);
476 static char *parse_attribute(const char *attrname
, const char *source
)
478 const char *tmp
, *tmp2
;
480 int len
= strlen(attrname
);
482 if (g_str_has_prefix(source
, attrname
)) {
484 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
486 retval
= g_strndup(tmp
, tmp2
- tmp
);
488 retval
= g_strdup(tmp
);
494 static void fill_auth(const gchar
*hdr
, struct sip_auth
*auth
)
500 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
504 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
505 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
506 auth
->type
= AUTH_TYPE_NTLM
;
509 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
510 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
511 auth
->type
= AUTH_TYPE_KERBEROS
;
515 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
516 auth
->type
= AUTH_TYPE_DIGEST
;
520 parts
= g_strsplit(hdr
, "\", ", 0);
521 for (i
= 0; parts
[i
]; i
++) {
524 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
526 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
527 g_free(auth
->gssapi_data
);
528 auth
->gssapi_data
= tmp
;
530 if (auth
->type
== AUTH_TYPE_NTLM
) {
531 /* NTLM module extracts nonce from gssapi-data */
535 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
536 /* Only used with AUTH_TYPE_DIGEST */
537 g_free(auth
->gssapi_data
);
538 auth
->gssapi_data
= tmp
;
539 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
540 g_free(auth
->opaque
);
542 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
546 if (auth
->type
== AUTH_TYPE_DIGEST
) {
547 /* Throw away old session key */
548 g_free(auth
->opaque
);
552 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
553 g_free(auth
->target
);
555 } else if ((tmp
= parse_attribute("version=", parts
[i
]))) {
556 auth
->version
= atoi(tmp
);
559 // uncomment to revert to previous functionality if version 3+ does not work.
560 // auth->version = 2;
567 static void sipe_canwrite_cb(gpointer data
,
568 SIPE_UNUSED_PARAMETER gint source
,
569 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
571 PurpleConnection
*gc
= data
;
572 struct sipe_account_data
*sip
= gc
->proto_data
;
576 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
578 if (max_write
== 0) {
579 if (sip
->tx_handler
!= 0){
580 purple_input_remove(sip
->tx_handler
);
586 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
588 if (written
< 0 && errno
== EAGAIN
)
590 else if (written
<= 0) {
591 /*TODO: do we really want to disconnect on a failure to write?*/
592 purple_connection_error(gc
, _("Could not write"));
596 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
599 static void sipe_canwrite_cb_ssl(gpointer data
,
600 SIPE_UNUSED_PARAMETER gint src
,
601 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
603 PurpleConnection
*gc
= data
;
604 struct sipe_account_data
*sip
= gc
->proto_data
;
608 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
610 if (max_write
== 0) {
611 if (sip
->tx_handler
!= 0) {
612 purple_input_remove(sip
->tx_handler
);
618 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
620 if (written
< 0 && errno
== EAGAIN
)
622 else if (written
<= 0) {
623 /*TODO: do we really want to disconnect on a failure to write?*/
624 purple_connection_error(gc
, _("Could not write"));
628 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
631 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
633 static void send_later_cb(gpointer data
, gint source
,
634 SIPE_UNUSED_PARAMETER
const gchar
*error
)
636 PurpleConnection
*gc
= data
;
637 struct sipe_account_data
*sip
;
638 struct sip_connection
*conn
;
640 if (!PURPLE_CONNECTION_IS_VALID(gc
))
648 purple_connection_error(gc
, _("Could not connect"));
652 sip
= gc
->proto_data
;
654 sip
->connecting
= FALSE
;
655 sip
->last_keepalive
= time(NULL
);
657 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
659 /* If there is more to write now, we need to register a handler */
660 if (sip
->txbuf
->bufused
> 0)
661 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
663 conn
= connection_create(sip
, source
);
664 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
667 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
669 struct sipe_account_data
*sip
;
671 if (!PURPLE_CONNECTION_IS_VALID(gc
))
673 if (gsc
) purple_ssl_close(gsc
);
677 sip
= gc
->proto_data
;
680 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
681 sip
->connecting
= FALSE
;
682 sip
->last_keepalive
= time(NULL
);
684 connection_create(sip
, gsc
->fd
);
686 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
691 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
692 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
694 PurpleConnection
*gc
= data
;
695 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
696 if (sip
== NULL
) return;
698 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
700 /* If there is more to write now */
701 if (sip
->txbuf
->bufused
> 0) {
702 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
707 static void sendlater(PurpleConnection
*gc
, const char *buf
)
709 struct sipe_account_data
*sip
= gc
->proto_data
;
711 if (!sip
->connecting
) {
712 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
713 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
714 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
716 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
717 purple_connection_error(gc
, _("Could not create socket"));
720 sip
->connecting
= TRUE
;
723 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
724 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
726 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
729 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
731 struct sipe_account_data
*sip
= gc
->proto_data
;
732 time_t currtime
= time(NULL
);
733 int writelen
= strlen(buf
);
736 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sending - %s######\n%s######\n", ctime(&currtime
), tmp
= fix_newlines(buf
));
738 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
739 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
740 purple_debug_info("sipe", "could not send packet\n");
749 if (sip
->tx_handler
) {
754 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
756 ret
= write(sip
->fd
, buf
, writelen
);
760 if (ret
< 0 && errno
== EAGAIN
)
762 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
767 if (ret
< writelen
) {
768 if (!sip
->tx_handler
){
770 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
773 sip
->tx_handler
= purple_input_add(sip
->fd
,
774 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
779 /* XXX: is it OK to do this? You might get part of a request sent
780 with part of another. */
781 if (sip
->txbuf
->bufused
> 0)
782 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
784 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
790 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
792 sendout_pkt(gc
, buf
);
796 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
798 GSList
*tmp
= msg
->headers
;
801 GString
*outstr
= g_string_new("");
802 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
804 name
= ((struct sipnameval
*) (tmp
->data
))->name
;
805 value
= ((struct sipnameval
*) (tmp
->data
))->value
;
806 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
807 tmp
= g_slist_next(tmp
);
809 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
810 sendout_pkt(sip
->gc
, outstr
->str
);
811 g_string_free(outstr
, TRUE
);
815 sipe_make_signature(struct sipe_account_data
*sip
,
818 if (sip
->registrar
.gssapi_context
) {
819 struct sipmsg_breakdown msgbd
;
820 gchar
*signature_input_str
;
822 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
823 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
824 sip
->registrar
.ntlm_num
++;
825 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
826 signature_input_str
= sipmsg_breakdown_get_string(sip
->registrar
.version
, &msgbd
);
827 if (signature_input_str
!= NULL
) {
828 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
829 msg
->signature
= signature_hex
;
830 msg
->rand
= g_strdup(msgbd
.rand
);
831 msg
->num
= g_strdup(msgbd
.num
);
832 g_free(signature_input_str
);
834 sipmsg_breakdown_free(&msgbd
);
838 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
842 if (sip
->registrar
.type
== AUTH_TYPE_UNSET
) {
846 sipe_make_signature(sip
, msg
);
848 if (sip
->registrar
.type
&& sipe_strequal(method
, "REGISTER")) {
849 buf
= auth_header(sip
, &sip
->registrar
, msg
);
851 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
854 } 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")) {
855 sip
->registrar
.nc
= 3;
856 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
858 if (purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
859 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
864 buf
= auth_header(sip
, &sip
->registrar
, msg
);
865 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
868 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
872 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
873 const char *text
, const char *body
)
877 GString
*outstr
= g_string_new("");
878 struct sipe_account_data
*sip
= gc
->proto_data
;
881 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
883 /* Can return NULL! */
884 contact
= get_contact(sip
);
886 sipmsg_add_header(msg
, "Contact", contact
);
891 gchar
*len
= g_strdup_printf("%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
892 sipmsg_add_header(msg
, "Content-Length", len
);
895 sipmsg_add_header(msg
, "Content-Length", "0");
898 msg
->response
= code
;
900 sipmsg_strip_headers(msg
, keepers
);
901 sipmsg_merge_new_headers(msg
);
902 sign_outgoing_message(msg
, sip
, msg
->method
);
904 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
907 name
= ((struct sipnameval
*) (tmp
->data
))->name
;
908 value
= ((struct sipnameval
*) (tmp
->data
))->value
;
910 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
911 tmp
= g_slist_next(tmp
);
913 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
914 sendout_pkt(gc
, outstr
->str
);
915 g_string_free(outstr
, TRUE
);
918 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
920 if (sip
->transactions
) {
921 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
922 purple_debug_info("sipe", "sip->transactions count:%d after removal\n", g_slist_length(sip
->transactions
));
924 if (trans
->msg
) sipmsg_free(trans
->msg
);
925 if (trans
->payload
) {
926 (*trans
->payload
->destroy
)(trans
->payload
->data
);
927 g_free(trans
->payload
);
934 static struct transaction
*
935 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
937 const gchar
*call_id
;
939 struct transaction
*trans
= g_new0(struct transaction
, 1);
941 trans
->time
= time(NULL
);
942 trans
->msg
= (struct sipmsg
*)msg
;
943 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
944 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
945 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
946 trans
->callback
= callback
;
947 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
948 purple_debug_info("sipe", "sip->transactions count:%d after addition\n", g_slist_length(sip
->transactions
));
952 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
954 struct transaction
*trans
;
955 GSList
*transactions
= sip
->transactions
;
956 const gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
957 const gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
960 if (!call_id
|| !cseq
) {
961 purple_debug(PURPLE_DEBUG_ERROR
, "sipe", "transaction_find: no Call-ID or CSeq!\n");
965 key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
966 while (transactions
) {
967 trans
= transactions
->data
;
968 if (!g_strcasecmp(trans
->key
, key
)) {
972 transactions
= transactions
->next
;
980 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
981 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
982 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
984 struct sipe_account_data
*sip
= gc
->proto_data
;
985 const char *addh
= "";
988 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
989 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
990 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
991 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
992 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
993 gchar
*route
= g_strdup("");
994 gchar
*epid
= get_epid(sip
);
995 int cseq
= dialog
? ++dialog
->cseq
: 1 /* as Call-Id is new in this case */;
996 struct transaction
*trans
= NULL
;
998 if (dialog
&& dialog
->routes
)
1000 GSList
*iter
= dialog
->routes
;
1005 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
1007 iter
= g_slist_next(iter
);
1011 if (!ourtag
&& !dialog
) {
1015 if (sipe_strequal(method
, "REGISTER")) {
1016 if (sip
->regcallid
) {
1018 callid
= g_strdup(sip
->regcallid
);
1020 sip
->regcallid
= g_strdup(callid
);
1025 if (addheaders
) addh
= addheaders
;
1027 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
1028 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
1029 "From: <sip:%s>%s%s;epid=%s\r\n"
1030 "To: <%s>%s%s%s%s\r\n"
1031 "Max-Forwards: 70\r\n"
1033 "User-Agent: %s\r\n"
1036 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
1038 dialog
&& dialog
->request
? dialog
->request
: url
,
1039 TRANSPORT_DESCRIPTOR
,
1040 purple_network_get_my_ip(-1),
1042 branch
? ";branch=" : "",
1043 branch
? branch
: "",
1045 ourtag
? ";tag=" : "",
1046 ourtag
? ourtag
: "",
1049 theirtag
? ";tag=" : "",
1050 theirtag
? theirtag
: "",
1051 theirepid
? ";epid=" : "",
1052 theirepid
? theirepid
: "",
1055 sipe_get_useragent(sip
),
1059 body
? (gsize
) strlen(body
) : 0,
1063 //printf ("parsing msg buf:\n%s\n\n", buf);
1064 msg
= sipmsg_parse_msg(buf
);
1075 sign_outgoing_message (msg
, sip
, method
);
1077 buf
= sipmsg_to_string (msg
);
1079 /* add to ongoing transactions */
1080 /* ACK isn't supposed to be answered ever. So we do not keep transaction for it. */
1081 if (!sipe_strequal(method
, "ACK")) {
1082 trans
= transactions_add_buf(sip
, msg
, tc
);
1086 sendout_pkt(gc
, buf
);
1093 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
1096 send_soap_request_with_cb(struct sipe_account_data
*sip
,
1099 TransCallback callback
,
1100 struct transaction_payload
*payload
)
1102 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sip
);
1103 gchar
*contact
= get_contact(sip
);
1104 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
1105 "Content-Type: application/SOAP+xml\r\n",contact
);
1107 struct transaction
*trans
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1108 trans
->payload
= payload
;
1115 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1117 send_soap_request_with_cb(sip
, NULL
, body
, NULL
, NULL
);
1120 static char *get_contact_register(struct sipe_account_data
*sip
)
1122 char *epid
= get_epid(sip
);
1123 char *uuid
= generateUUIDfromEPID(epid
);
1124 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
);
1130 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1138 if (!sip
->sipdomain
) return;
1140 uri
= sip_uri_from_name(sip
->sipdomain
);
1141 expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1142 to
= sip_uri_self(sip
);
1143 contact
= get_contact_register(sip
);
1144 hdr
= g_strdup_printf("Contact: %s\r\n"
1145 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1146 "Event: registration\r\n"
1147 "Allow-Events: presence\r\n"
1148 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1149 "%s", contact
, expires
);
1153 sip
->registerstatus
= 1;
1155 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1156 process_register_response
);
1163 static void do_register_cb(struct sipe_account_data
*sip
,
1164 SIPE_UNUSED_PARAMETER
void *unused
)
1166 do_register_exp(sip
, -1);
1167 sip
->reregister_set
= FALSE
;
1170 static void do_register(struct sipe_account_data
*sip
)
1172 do_register_exp(sip
, -1);
1176 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1178 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1179 send_soap_request(sip
, body
);
1184 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1187 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1189 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1192 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1196 void sipe_auth_user_cb(void * data
)
1198 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1201 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1206 void sipe_deny_user_cb(void * data
)
1208 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1211 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1216 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1218 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1219 sipe_contact_allow_deny(sip
, name
, TRUE
);
1223 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1225 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1226 sipe_contact_allow_deny(sip
, name
, FALSE
);
1230 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1232 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1233 sipe_contact_set_acl(sip, name, "");
1237 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1241 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1242 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1244 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| sipe_strequal(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1246 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1247 if (!watchers
) return;
1249 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1250 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1251 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1252 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1254 // TODO pull out optional displayName to pass as alias
1256 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1257 job
->who
= remote_user
;
1259 purple_account_request_authorization(
1273 xmlnode_free(watchers
);
1278 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1280 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1281 if (!purple_group
) {
1282 purple_group
= purple_group_new(group
->name
);
1283 purple_blist_add_group(purple_group
, NULL
);
1287 group
->purple_group
= purple_group
;
1288 sip
->groups
= g_slist_append(sip
->groups
, group
);
1289 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1291 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1295 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1297 struct sipe_group
*group
;
1303 entry
= sip
->groups
;
1305 group
= entry
->data
;
1306 if (group
->id
== id
) {
1309 entry
= entry
->next
;
1314 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1316 struct sipe_group
*group
;
1318 if (!sip
|| !name
) {
1322 entry
= sip
->groups
;
1324 group
= entry
->data
;
1325 if (sipe_strequal(group
->name
, name
)) {
1328 entry
= entry
->next
;
1334 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1337 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1338 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1339 send_soap_request(sip
, body
);
1341 g_free(group
->name
);
1342 group
->name
= g_strdup(name
);
1346 * Only appends if no such value already stored.
1349 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1350 GSList
* res
= list
;
1351 if (!g_slist_find_custom(list
, data
, func
)) {
1352 res
= g_slist_insert_sorted(list
, data
, func
);
1358 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1359 return group1
->id
- group2
->id
;
1363 * Returns string like "2 4 7 8" - group ids buddy belong to.
1366 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1369 //creating array from GList, converting int to gchar*
1370 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1371 GSList
*entry
= buddy
->groups
;
1373 if (!ids_arr
) return NULL
;
1376 struct sipe_group
* group
= entry
->data
;
1377 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1378 entry
= entry
->next
;
1382 res
= g_strjoinv(" ", ids_arr
);
1383 g_strfreev(ids_arr
);
1388 * Sends buddy update to server
1391 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1393 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1394 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1396 if (buddy
&& purple_buddy
) {
1397 const char *alias
= purple_buddy_get_alias(purple_buddy
);
1398 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1401 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1403 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1404 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1406 send_soap_request(sip
, body
);
1413 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1415 if (msg
->response
== 200) {
1416 struct sipe_group
*group
;
1417 struct group_user_context
*ctx
= trans
->payload
->data
;
1421 struct sipe_buddy
*buddy
;
1423 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1428 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1434 group_id
= xmlnode_get_data(node
);
1440 group
= g_new0(struct sipe_group
, 1);
1441 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1443 group
->name
= g_strdup(ctx
->group_name
);
1445 sipe_group_add(sip
, group
);
1447 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1449 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1452 sipe_group_set_user(sip
, ctx
->user_name
);
1460 static void sipe_group_context_destroy(gpointer data
)
1462 struct group_user_context
*ctx
= data
;
1463 g_free(ctx
->group_name
);
1464 g_free(ctx
->user_name
);
1468 static void sipe_group_create (struct sipe_account_data
*sip
, const gchar
*name
, const gchar
* who
)
1470 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
1471 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
1473 ctx
->group_name
= g_strdup(name
);
1474 ctx
->user_name
= g_strdup(who
);
1475 payload
->destroy
= sipe_group_context_destroy
;
1476 payload
->data
= ctx
;
1478 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1479 send_soap_request_with_cb(sip
, NULL
, body
, process_add_group_response
, payload
);
1484 * Data structure for scheduled actions
1487 struct scheduled_action
{
1490 * Format is <Event>[<Data>...]
1491 * Example: <presence><sip:user@domain.com> or <registration>
1494 guint timeout_handler
;
1495 gboolean repetitive
;
1497 GDestroyNotify destroy
;
1498 struct sipe_account_data
*sip
;
1504 * Should return FALSE if repetitive action is not needed
1506 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1509 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1510 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1511 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1512 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1513 ret
= sched_action
->repetitive
;
1514 if (sched_action
->destroy
) {
1515 (*sched_action
->destroy
)(sched_action
->payload
);
1517 g_free(sched_action
->name
);
1518 g_free(sched_action
);
1523 * Kills action timer effectively cancelling
1526 * @param name of action
1528 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1532 if (!sip
->timeouts
|| !name
) return;
1534 entry
= sip
->timeouts
;
1536 struct scheduled_action
*sched_action
= entry
->data
;
1537 if(sipe_strequal(sched_action
->name
, name
)) {
1538 GSList
*to_delete
= entry
;
1539 entry
= entry
->next
;
1540 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1541 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1542 purple_timeout_remove(sched_action
->timeout_handler
);
1543 if (sched_action
->destroy
) {
1544 (*sched_action
->destroy
)(sched_action
->payload
);
1546 g_free(sched_action
->name
);
1547 g_free(sched_action
);
1549 entry
= entry
->next
;
1555 sipe_schedule_action0(const gchar
*name
,
1559 GDestroyNotify destroy
,
1560 struct sipe_account_data
*sip
,
1563 struct scheduled_action
*sched_action
;
1565 /* Make sure each action only exists once */
1566 sipe_cancel_scheduled_action(sip
, name
);
1568 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1569 sched_action
= g_new0(struct scheduled_action
, 1);
1570 sched_action
->repetitive
= FALSE
;
1571 sched_action
->name
= g_strdup(name
);
1572 sched_action
->action
= action
;
1573 sched_action
->destroy
= destroy
;
1574 sched_action
->sip
= sip
;
1575 sched_action
->payload
= payload
;
1576 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1577 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1578 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1579 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1583 sipe_schedule_action(const gchar
*name
,
1586 GDestroyNotify destroy
,
1587 struct sipe_account_data
*sip
,
1590 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1594 * Same as sipe_schedule_action() but timeout is in milliseconds.
1597 sipe_schedule_action_msec(const gchar
*name
,
1600 GDestroyNotify destroy
,
1601 struct sipe_account_data
*sip
,
1604 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1608 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1609 time_t calculate_from
);
1612 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
);
1615 sipe_get_status_by_availability(int avail
,
1619 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
1620 const char *status_id
,
1621 const char *message
,
1622 time_t do_not_publish
[]);
1625 sipe_apply_calendar_status(struct sipe_account_data
*sip
,
1626 struct sipe_buddy
*sbuddy
,
1627 const char *status_id
)
1629 time_t cal_avail_since
;
1630 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
1634 if (!sbuddy
) return;
1636 if (cal_status
< SIPE_CAL_NO_DATA
) {
1637 purple_debug_info("sipe", "sipe_apply_calendar_status: cal_status : %d for %s\n", cal_status
, sbuddy
->name
);
1638 purple_debug_info("sipe", "sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
1641 /* scheduled Cal update call */
1643 status_id
= sbuddy
->last_non_cal_status_id
;
1644 g_free(sbuddy
->activity
);
1645 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
1649 purple_debug_info("sipe", "sipe_apply_calendar_status: status_id is NULL for %s, exiting.\n",
1650 sbuddy
->name
? sbuddy
->name
: "" );
1654 /* adjust to calendar status */
1655 if (cal_status
!= SIPE_CAL_NO_DATA
) {
1656 purple_debug_info("sipe", "sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
1658 if (cal_status
== SIPE_CAL_BUSY
1659 && cal_avail_since
> sbuddy
->user_avail_since
1660 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
1662 status_id
= SIPE_STATUS_ID_BUSY
;
1663 g_free(sbuddy
->activity
);
1664 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
));
1666 avail
= sipe_get_availability_by_status(status_id
, NULL
);
1668 purple_debug_info("sipe", "sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
1669 if (cal_avail_since
> sbuddy
->activity_since
) {
1670 if (cal_status
== SIPE_CAL_OOF
1671 && avail
>= 15000) /* 12000 in 2007 */
1673 g_free(sbuddy
->activity
);
1674 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
1679 /* then set status_id actually */
1680 purple_debug_info("sipe", "sipe_apply_calendar_status: to %s for %s\n", status_id
, sbuddy
->name
? sbuddy
->name
: "" );
1681 purple_prpl_got_user_status(sip
->account
, sbuddy
->name
, status_id
, NULL
);
1683 /* set our account state to the one in roaming (including calendar info) */
1684 self_uri
= sip_uri_self(sip
);
1685 if (sip
->initial_state_published
&& sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
1686 if (sipe_strequal(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
1687 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
1690 purple_debug_info("sipe", "sipe_apply_calendar_status: switch to '%s' for the account\n", sip
->status
);
1691 sipe_set_purple_account_status_and_note(sip
->account
, status_id
, sip
->note
, sip
->do_not_publish
);
1697 sipe_got_user_status(struct sipe_account_data
*sip
,
1699 const char *status_id
)
1701 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
1703 if (!sbuddy
) return;
1705 /* Check if on 2005 system contact's calendar,
1706 * then set/preserve it.
1708 if (!sip
->ocs2007
) {
1709 sipe_apply_calendar_status(sip
, sbuddy
, status_id
);
1711 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
1716 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
1717 struct sipe_buddy
*sbuddy
,
1718 struct sipe_account_data
*sip
)
1720 sipe_apply_calendar_status(sip
, sbuddy
, NULL
);
1724 * Updates contact's status
1725 * based on their calendar information.
1727 * Applicability: 2005 systems
1730 update_calendar_status(struct sipe_account_data
*sip
)
1732 purple_debug_info("sipe", "update_calendar_status() started.\n");
1733 g_hash_table_foreach(sip
->buddies
, (GHFunc
)update_calendar_status_cb
, (gpointer
)sip
);
1735 /* repeat scheduling */
1736 sipe_sched_calendar_status_update(sip
, time(NULL
) + 3*60 /* 3 min */);
1740 * Schedules process of contacts' status update
1741 * based on their calendar information.
1742 * Should be scheduled to the beginning of every
1743 * 15 min interval, like:
1744 * 13:00, 13:15, 13:30, 13:45, etc.
1746 * Applicability: 2005 systems
1749 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1750 time_t calculate_from
)
1752 int interval
= 15*60;
1753 /** start of the beginning of closest 15 min interval. */
1754 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1756 purple_debug_info("sipe", "sipe_sched_calendar_status_update: calculate_from time: %s",
1757 asctime(localtime(&calculate_from
)));
1758 purple_debug_info("sipe", "sipe_sched_calendar_status_update: next start time : %s",
1759 asctime(localtime(&next_start
)));
1761 sipe_schedule_action("<+2005-cal-status>",
1762 (int)(next_start
- time(NULL
)),
1763 (Action
)update_calendar_status
,
1770 * Schedules process of self status publish
1771 * based on own calendar information.
1772 * Should be scheduled to the beginning of every
1773 * 15 min interval, like:
1774 * 13:00, 13:15, 13:30, 13:45, etc.
1776 * Applicability: 2007+ systems
1779 sipe_sched_calendar_status_self_publish(struct sipe_account_data
*sip
,
1780 time_t calculate_from
)
1782 int interval
= 5*60;
1783 /** start of the beginning of closest 5 min interval. */
1784 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1786 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: calculate_from time: %s",
1787 asctime(localtime(&calculate_from
)));
1788 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: next start time : %s",
1789 asctime(localtime(&next_start
)));
1791 sipe_schedule_action("<+2007-cal-status>",
1792 (int)(next_start
- time(NULL
)),
1793 (Action
)publish_calendar_status_self
,
1799 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1801 /** Should be g_free()'d
1804 sipe_get_subscription_key(const gchar
*event
,
1809 if (is_empty(event
)) return NULL
;
1811 if (event
&& sipe_strcase_equal(event
, "presence")) {
1812 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1813 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, with
);
1815 /* @TODO drop participated buddies' just_added flag */
1817 /* Subscription is identified by <event> key */
1818 key
= g_strdup_printf("<%s>", event
);
1824 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
1825 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
1827 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
1828 const gchar
*event
= sipmsg_find_header(msg
, "Event");
1831 /* The case with 2005 Public IM Connectivity (PIC) - no Event header */
1833 struct sipmsg
*request_msg
= trans
->msg
;
1834 event
= sipmsg_find_header(request_msg
, "Event");
1837 key
= sipe_get_subscription_key(event
, with
);
1839 /* 200 OK; 481 Call Leg Does Not Exist */
1840 if (key
&& (msg
->response
== 200 || msg
->response
== 481)) {
1841 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
1842 g_hash_table_remove(sip
->subscriptions
, key
);
1843 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
1847 /* create/store subscription dialog if not yet */
1848 if (msg
->response
== 200) {
1849 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1850 gchar
*cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
1853 struct sip_subscription
*subscription
= g_new0(struct sip_subscription
, 1);
1854 g_hash_table_insert(sip
->subscriptions
, g_strdup(key
), subscription
);
1856 subscription
->dialog
.callid
= g_strdup(callid
);
1857 subscription
->dialog
.cseq
= atoi(cseq
);
1858 subscription
->dialog
.with
= g_strdup(with
);
1859 subscription
->event
= g_strdup(event
);
1860 sipe_dialog_parse(&subscription
->dialog
, msg
, TRUE
);
1862 purple_debug_info("sipe", "process_subscribe_response: subscription dialog added for: %s\n", key
);
1871 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1873 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1878 static void sipe_subscribe_resource_uri(const char *name
,
1879 SIPE_UNUSED_PARAMETER gpointer value
,
1880 gchar
**resources_uri
)
1882 gchar
*tmp
= *resources_uri
;
1883 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1887 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1889 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1890 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1891 gchar
*tmp
= *resources_uri
;
1893 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
1895 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
1900 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1901 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1902 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1903 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1904 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1907 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1910 gchar
*contact
= get_contact(sip
);
1913 gchar
*require
= "";
1915 gchar
*autoextend
= "";
1916 gchar
*content_type
;
1917 struct sip_dialog
*dialog
;
1920 require
= ", categoryList";
1921 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1922 content_type
= "application/msrtc-adrl-categorylist+xml";
1923 content
= g_strdup_printf(
1924 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1925 "<action name=\"subscribe\" id=\"63792024\">\n"
1926 "<adhocList>\n%s</adhocList>\n"
1927 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1928 "<category name=\"calendarData\"/>\n"
1929 "<category name=\"contactCard\"/>\n"
1930 "<category name=\"note\"/>\n"
1931 "<category name=\"state\"/>\n"
1934 "</batchSub>", sip
->username
, resources_uri
);
1936 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1937 content_type
= "application/adrl+xml";
1938 content
= g_strdup_printf(
1939 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1940 "<create xmlns=\"\">\n%s</create>\n"
1941 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1943 g_free(resources_uri
);
1945 request
= g_strdup_printf(
1946 "Require: adhoclist%s\r\n"
1947 "Supported: eventlist\r\n"
1948 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1949 "Supported: ms-piggyback-first-notify\r\n"
1950 "%sSupported: ms-benotify\r\n"
1951 "Proxy-Require: ms-benotify\r\n"
1952 "Event: presence\r\n"
1953 "Content-Type: %s\r\n"
1954 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1957 /* subscribe to buddy presence */
1958 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1959 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1960 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1961 purple_debug_info("sipe", "sipe_subscribe_presence_batched_to: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
1963 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
1971 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
,
1972 SIPE_UNUSED_PARAMETER
void *unused
)
1974 gchar
*to
= sip_uri_self(sip
);
1975 gchar
*resources_uri
= g_strdup("");
1977 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1979 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1982 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1985 struct presence_batched_routed
{
1990 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1992 struct presence_batched_routed
*data
= payload
;
1993 GSList
*buddies
= data
->buddies
;
1995 g_free(buddies
->data
);
1996 buddies
= buddies
->next
;
1998 g_slist_free(data
->buddies
);
2003 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
2005 struct presence_batched_routed
*data
= payload
;
2006 GSList
*buddies
= data
->buddies
;
2007 gchar
*resources_uri
= g_strdup("");
2009 gchar
*tmp
= resources_uri
;
2010 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
2012 buddies
= buddies
->next
;
2014 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
2015 g_strdup(data
->host
));
2019 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
2020 * The user sends a single SUBSCRIBE request to the subscribed contact.
2021 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
2025 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
2029 gchar
*to
= sip_uri((char *)buddy_name
);
2030 gchar
*tmp
= get_contact(sip
);
2032 gchar
*content
= NULL
;
2033 gchar
*autoextend
= "";
2034 gchar
*content_type
= "";
2035 struct sip_dialog
*dialog
;
2036 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, to
);
2037 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
2039 if (sbuddy
) sbuddy
->just_added
= FALSE
;
2042 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
2044 autoextend
= "Supported: com.microsoft.autoextend\r\n";
2047 request
= g_strdup_printf(
2048 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
2049 "Supported: ms-piggyback-first-notify\r\n"
2050 "%s%sSupported: ms-benotify\r\n"
2051 "Proxy-Require: ms-benotify\r\n"
2052 "Event: presence\r\n"
2053 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
2056 content
= g_strdup_printf(
2057 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
2058 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
2059 "<resource uri=\"%s\"%s\n"
2061 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
2062 "<category name=\"calendarData\"/>\n"
2063 "<category name=\"contactCard\"/>\n"
2064 "<category name=\"note\"/>\n"
2065 "<category name=\"state\"/>\n"
2068 "</batchSub>", sip
->username
, to
, context
);
2073 /* subscribe to buddy presence */
2074 /* Subscription is identified by ACTION_NAME_PRESENCE key */
2075 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
2076 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2077 purple_debug_info("sipe", "sipe_subscribe_presence_single: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2079 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
2087 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
2089 purple_debug_info("sipe", "sipe_set_status: status=%s\n", purple_status_get_id(status
));
2091 if (!purple_status_is_active(status
))
2095 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
2100 time_t now
= time(NULL
);
2101 const char *status_id
= purple_status_get_id(status
);
2102 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
2103 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
2104 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
2106 /* when other point of presence clears note, but we are keeping
2107 * state if OOF note.
2109 if (do_not_publish
&& !note
&& sip
->ews
&& sip
->ews
->oof_note
) {
2110 purple_debug_info("sipe", "sipe_set_status: enabling publication as OOF note keepers.\n");
2111 do_not_publish
= FALSE
;
2114 purple_debug_info("sipe", "sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d\n",
2115 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
2117 sip
->do_not_publish
[activity
] = 0;
2118 purple_debug_info("sipe", "sipe_set_status: set: sip->do_not_publish[%s]=%d [0]\n",
2119 status_id
, (int)sip
->do_not_publish
[activity
]);
2123 purple_debug_info("sipe", "sipe_set_status: publication was switched off, exiting.\n");
2127 g_free(sip
->status
);
2128 sip
->status
= g_strdup(status_id
);
2130 /* hack to escape apostrof before comparison */
2131 tmp
= note
? purple_strreplace(note
, "'", "'") : NULL
;
2133 /* this will preserve OOF flag as well */
2134 if (!sipe_strequal(tmp
, sip
->note
)) {
2135 sip
->is_oof_note
= FALSE
;
2137 sip
->note
= g_strdup(note
);
2138 sip
->note_since
= time(NULL
);
2142 /* schedule 2 sec to capture idle flag */
2143 action_name
= g_strdup_printf("<%s>", "+set-status");
2144 sipe_schedule_action(action_name
, SIPE_IDLE_SET_DELAY
, (Action
)send_presence_status
, NULL
, sip
, NULL
);
2145 g_free(action_name
);
2150 sipe_set_idle(PurpleConnection
* gc
,
2153 purple_debug_info("sipe", "sipe_set_idle: interval=%d\n", interval
);
2156 struct sipe_account_data
*sip
= gc
->proto_data
;
2159 sip
->idle_switch
= time(NULL
);
2160 purple_debug_info("sipe", "sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
2166 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
,
2167 SIPE_UNUSED_PARAMETER
const char *alias
)
2169 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2170 sipe_group_set_user(sip
, name
);
2174 sipe_group_buddy(PurpleConnection
*gc
,
2176 const char *old_group_name
,
2177 const char *new_group_name
)
2179 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2180 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
2181 struct sipe_group
* old_group
= NULL
;
2182 struct sipe_group
* new_group
;
2184 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
2185 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
2187 if(!buddy
) { // buddy not in roaming list
2191 if (old_group_name
) {
2192 old_group
= sipe_group_find_by_name(sip
, old_group_name
);
2194 new_group
= sipe_group_find_by_name(sip
, new_group_name
);
2197 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
2198 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
2202 sipe_group_create(sip
, new_group_name
, who
);
2204 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
2205 sipe_group_set_user(sip
, who
);
2209 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2211 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2213 /* libpurple can call us with undefined buddy or group */
2214 if (buddy
&& group
) {
2215 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2217 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2218 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
2219 purple_blist_rename_buddy(buddy
, buddy_name
);
2222 /* Prepend sip: if needed */
2223 if (!g_str_has_prefix(buddy
->name
, "sip:")) {
2224 gchar
*buf
= sip_uri_from_name(buddy
->name
);
2225 purple_blist_rename_buddy(buddy
, buf
);
2229 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
2230 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
2231 purple_debug_info("sipe", "sipe_add_buddy: adding %s\n", buddy
->name
);
2232 b
->name
= g_strdup(buddy
->name
);
2233 b
->just_added
= TRUE
;
2234 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
2235 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
2236 /* @TODO should go to callback */
2237 sipe_subscribe_presence_single(sip
, b
->name
);
2239 purple_debug_info("sipe", "sipe_add_buddy: buddy %s already in internal list\n", buddy
->name
);
2244 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
2248 * We are calling g_hash_table_foreach_steal(). That means that no
2249 * key/value deallocation functions are called. Therefore the glib
2250 * hash code does not touch the key (buddy->name) or value (buddy)
2251 * of the to-be-deleted hash node at all. It follows that we
2253 * - MUST free the memory for the key ourselves and
2254 * - ARE allowed to do it in this function
2256 * Conclusion: glib must be broken on the Windows platform if sipe
2257 * crashes with SIGTRAP when closing. You'll have to live
2258 * with the memory leak until this is fixed.
2260 g_free(buddy
->name
);
2262 g_free(buddy
->activity
);
2263 g_free(buddy
->meeting_subject
);
2264 g_free(buddy
->meeting_location
);
2265 g_free(buddy
->note
);
2267 g_free(buddy
->cal_start_time
);
2268 g_free(buddy
->cal_free_busy_base64
);
2269 g_free(buddy
->cal_free_busy
);
2270 g_free(buddy
->last_non_cal_activity
);
2272 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
2274 g_free(buddy
->device_name
);
2275 g_slist_free(buddy
->groups
);
2280 * Unassociates buddy from group first.
2281 * Then see if no groups left, removes buddy completely.
2282 * Otherwise updates buddy groups on server.
2284 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2286 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2287 struct sipe_buddy
*b
;
2288 struct sipe_group
*g
= NULL
;
2290 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2293 b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
2297 g
= sipe_group_find_by_name(sip
, group
->name
);
2301 b
->groups
= g_slist_remove(b
->groups
, g
);
2302 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
2305 if (g_slist_length(b
->groups
) < 1) {
2306 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
2307 sipe_cancel_scheduled_action(sip
, action_name
);
2308 g_free(action_name
);
2310 g_hash_table_remove(sip
->buddies
, buddy
->name
);
2313 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
2314 send_soap_request(sip
, body
);
2320 //updates groups on server
2321 sipe_group_set_user(sip
, b
->name
);
2327 sipe_rename_group(PurpleConnection
*gc
,
2328 const char *old_name
,
2330 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
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
, old_name
);
2335 sipe_group_rename(sip
, s_group
, group
->name
);
2337 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
2342 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
2344 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2345 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
2348 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
2349 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
2350 send_soap_request(sip
, body
);
2353 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
2354 g_free(s_group
->name
);
2357 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
2361 /** All statuses need message attribute to pass Note */
2362 static GList
*sipe_status_types(SIPE_UNUSED_PARAMETER PurpleAccount
*acc
)
2364 PurpleStatusType
*type
;
2365 GList
*types
= NULL
;
2367 /* Macros to reduce code repetition.
2368 Translators: noun */
2369 #define SIPE_ADD_STATUS(prim,id,name,user) type = purple_status_type_new_with_attrs( \
2371 TRUE, user, FALSE, \
2372 SIPE_STATUS_ATTR_ID_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), \
2374 types = g_list_append(types, type);
2377 SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE
,
2383 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2384 sipe_activity_map
[SIPE_ACTIVITY_BUSY
].status_id
,
2385 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSY
),
2388 /* Do Not Disturb */
2389 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2390 sipe_activity_map
[SIPE_ACTIVITY_DND
].status_id
,
2395 /* Goes first in the list as
2396 * purple picks the first status with the AWAY type
2399 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2405 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2406 sipe_activity_map
[SIPE_ACTIVITY_BRB
].status_id
,
2407 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BRB
),
2410 /* Appear Offline */
2411 SIPE_ADD_STATUS(PURPLE_STATUS_INVISIBLE
,
2417 type
= purple_status_type_new(PURPLE_STATUS_OFFLINE
,
2421 types
= g_list_append(types
, type
);
2427 * A callback for g_hash_table_foreach
2430 sipe_buddy_subscribe_cb(char *buddy_name
,
2431 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
2432 struct sipe_account_data
*sip
)
2434 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy_name
);
2435 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
2436 guint time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
2437 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
2439 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy_name
));
2440 g_free(action_name
);
2444 * Removes entries from purple buddy list
2445 * that does not correspond ones in the roaming contact list.
2447 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
2448 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
2449 GSList
*entry
= buddies
;
2450 struct sipe_buddy
*buddy
;
2454 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
2455 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
2458 g
= purple_buddy_get_group(b
);
2459 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
2461 gboolean in_sipe_groups
= FALSE
;
2462 GSList
*entry2
= buddy
->groups
;
2464 struct sipe_group
*group
= entry2
->data
;
2465 if (sipe_strequal(group
->name
, g
->name
)) {
2466 in_sipe_groups
= TRUE
;
2469 entry2
= entry2
->next
;
2471 if(!in_sipe_groups
) {
2472 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
2473 purple_blist_remove_buddy(b
);
2476 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
2477 purple_blist_remove_buddy(b
);
2479 entry
= entry
->next
;
2481 g_slist_free(buddies
);
2484 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2486 int len
= msg
->bodylen
;
2488 const gchar
*tmp
= sipmsg_find_header(msg
, "Event");
2491 const gchar
*contacts_delta
;
2492 xmlnode
*group_node
;
2493 if (!g_str_has_prefix(tmp
, "vnd-microsoft-roaming-contacts")) {
2497 /* Convert the contact from XML to Purple Buddies */
2498 isc
= xmlnode_from_str(msg
->body
, len
);
2503 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
2504 if (contacts_delta
) {
2505 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2508 if (sipe_strequal(isc
->name
, "contactList")) {
2511 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
2512 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2513 const char *name
= xmlnode_get_attrib(group_node
, "name");
2515 if (g_str_has_prefix(name
, "~")) {
2516 name
= _("Other Contacts");
2518 group
->name
= g_strdup(name
);
2519 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
2521 sipe_group_add(sip
, group
);
2524 // Make sure we have at least one group
2525 if (g_slist_length(sip
->groups
) == 0) {
2526 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2527 PurpleGroup
*purple_group
;
2528 group
->name
= g_strdup(_("Other Contacts"));
2530 purple_group
= purple_group_new(group
->name
);
2531 purple_blist_add_group(purple_group
, NULL
);
2532 sip
->groups
= g_slist_append(sip
->groups
, group
);
2535 /* Parse contacts */
2536 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
2537 const gchar
*uri
= xmlnode_get_attrib(item
, "uri");
2538 const gchar
*name
= xmlnode_get_attrib(item
, "name");
2540 struct sipe_buddy
*buddy
= NULL
;
2542 gchar
**item_groups
;
2545 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2546 tmp
= sip_uri_from_name(uri
);
2547 buddy_name
= g_ascii_strdown(tmp
, -1);
2550 /* assign to group Other Contacts if nothing else received */
2551 tmp
= g_strdup(xmlnode_get_attrib(item
, "groups"));
2553 struct sipe_group
*group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
2555 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
2557 item_groups
= g_strsplit(tmp
, " ", 0);
2560 while (item_groups
[i
]) {
2561 struct sipe_group
*group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2563 // If couldn't find the right group for this contact, just put them in the first group we have
2564 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2565 group
= sip
->groups
->data
;
2568 if (group
!= NULL
) {
2569 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2571 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2572 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2574 purple_debug_info("sipe", "Created new buddy %s with alias %s\n", buddy_name
, uri
);
2577 if (sipe_strcase_equal(uri
, purple_buddy_get_alias(b
))) {
2578 if (name
!= NULL
&& strlen(name
) != 0) {
2579 purple_blist_alias_buddy(b
, name
);
2581 purple_debug_info("sipe", "Replaced buddy %s alias with %s\n", buddy_name
, name
);
2586 buddy
= g_new0(struct sipe_buddy
, 1);
2587 buddy
->name
= g_strdup(b
->name
);
2588 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2591 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2593 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2595 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2600 } // while, contact groups
2601 g_strfreev(item_groups
);
2606 sipe_cleanup_local_blist(sip
);
2608 /* Add self-contact if not there yet. 2005 systems. */
2609 /* This will resemble subscription to roaming_self in 2007 systems */
2610 if (!sip
->ocs2007
) {
2611 gchar
*self_uri
= sip_uri_self(sip
);
2612 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, self_uri
);
2615 buddy
= g_new0(struct sipe_buddy
, 1);
2616 buddy
->name
= g_strdup(self_uri
);
2617 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2624 /* subscribe to buddies */
2625 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2626 if (sip
->batched_support
) {
2627 sipe_subscribe_presence_batched(sip
, NULL
);
2629 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2631 sip
->subscribed_buddies
= TRUE
;
2633 /* for 2005 systems schedule contacts' status update
2634 * based on their calendar information
2636 if (!sip
->ocs2007
) {
2637 sipe_sched_calendar_status_update(sip
, time(NULL
));
2644 * Subscribe roaming contacts
2646 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
)
2648 gchar
*to
= sip_uri_self(sip
);
2649 gchar
*tmp
= get_contact(sip
);
2650 gchar
*hdr
= g_strdup_printf(
2651 "Event: vnd-microsoft-roaming-contacts\r\n"
2652 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2653 "Supported: com.microsoft.autoextend\r\n"
2654 "Supported: ms-benotify\r\n"
2655 "Proxy-Require: ms-benotify\r\n"
2656 "Supported: ms-piggyback-first-notify\r\n"
2657 "Contact: %s\r\n", tmp
);
2660 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2665 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
,
2666 SIPE_UNUSED_PARAMETER
void *unused
)
2669 struct sip_dialog
*dialog
;
2670 gchar
*to
= sip_uri_self(sip
);
2671 gchar
*tmp
= get_contact(sip
);
2672 gchar
*hdr
= g_strdup_printf(
2673 "Event: presence.wpending\r\n"
2674 "Accept: text/xml+msrtc.wpending\r\n"
2675 "Supported: com.microsoft.autoextend\r\n"
2676 "Supported: ms-benotify\r\n"
2677 "Proxy-Require: ms-benotify\r\n"
2678 "Supported: ms-piggyback-first-notify\r\n"
2679 "Contact: %s\r\n", tmp
);
2682 /* Subscription is identified by <event> key */
2683 key
= g_strdup_printf("<%s>", "presence.wpending");
2684 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2685 purple_debug_info("sipe", "sipe_subscribe_presence_wpending: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2687 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", dialog
, process_subscribe_response
);
2695 * Fires on deregistration event initiated by server.
2696 * [MS-SIPREGE] SIP extension.
2701 // Content-Type: text/registration-event
2702 // subscription-state: terminated;expires=0
2703 // ms-diagnostics-public: 4141;reason="User disabled"
2705 // deregistered;event=rejected
2707 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2709 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2710 gchar
*event
= NULL
;
2711 gchar
*reason
= NULL
;
2712 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
2715 diagnostics
= diagnostics
? diagnostics
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2716 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2718 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2719 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2720 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2721 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2723 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2727 if (diagnostics
!= NULL
) {
2728 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
2729 } else { // for LCS2005
2731 if (event
&& sipe_strcase_equal(event
, "unregistered")) {
2732 error_id
= 4140; // [MS-SIPREGE]
2733 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2734 reason
= g_strdup(_("you are already signed in at another location"));
2735 } else if (event
&& sipe_strcase_equal(event
, "rejected")) {
2737 reason
= g_strdup(_("user disabled")); // [MS-OCER]
2738 } else if (event
&& sipe_strcase_equal(event
, "deactivated")) {
2740 reason
= g_strdup(_("user moved")); // [MS-OCER]
2744 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
2747 sip
->gc
->wants_to_die
= TRUE
;
2748 purple_connection_error(sip
->gc
, warning
);
2753 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2755 xmlnode
*xn_provision_group_list
;
2758 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2760 /* provisionGroup */
2761 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2762 if (sipe_strequal("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2763 g_free(sip
->focus_factory_uri
);
2764 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2765 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2766 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2770 xmlnode_free(xn_provision_group_list
);
2773 /** for 2005 system */
2775 sipe_process_provisioning(struct sipe_account_data
*sip
,
2778 xmlnode
*xn_provision
;
2781 xn_provision
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2782 if ((node
= xmlnode_get_child(xn_provision
, "user"))) {
2783 purple_debug_info("sipe", "sipe_process_provisioning: uri=%s\n", xmlnode_get_attrib(node
, "uri"));
2784 if ((node
= xmlnode_get_child(node
, "line"))) {
2785 const gchar
*line_uri
= xmlnode_get_attrib(node
, "uri");
2786 const gchar
*server
= xmlnode_get_attrib(node
, "server");
2787 purple_debug_info("sipe", "sipe_process_provisioning: line_uri=%s server=%s\n", line_uri
, server
);
2788 sip_csta_open(sip
, line_uri
, server
);
2791 xmlnode_free(xn_provision
);
2794 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2796 const gchar
*contacts_delta
;
2799 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2805 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2808 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2815 free_container(struct sipe_container
*container
)
2819 if (!container
) return;
2821 entry
= container
->members
;
2823 void *data
= entry
->data
;
2824 entry
= g_slist_remove(entry
, data
);
2831 * Finds locally stored MS-PRES container member
2833 static struct sipe_container_member
*
2834 sipe_find_container_member(struct sipe_container
*container
,
2838 struct sipe_container_member
*member
;
2841 if (container
== NULL
|| type
== NULL
) {
2845 entry
= container
->members
;
2847 member
= entry
->data
;
2848 if (!g_strcasecmp(member
->type
, type
)
2849 && ((!member
->value
&& !value
)
2850 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2854 entry
= entry
->next
;
2860 * Finds locally stored MS-PRES container by id
2862 static struct sipe_container
*
2863 sipe_find_container(struct sipe_account_data
*sip
,
2866 struct sipe_container
*container
;
2873 entry
= sip
->containers
;
2875 container
= entry
->data
;
2876 if (id
== container
->id
) {
2879 entry
= entry
->next
;
2893 sipe_find_access_level(struct sipe_account_data
*sip
,
2897 guint containers
[] = {32000, 400, 300, 200, 100};
2900 for (i
= 0; i
< 5; i
++) {
2901 struct sipe_container_member
*member
;
2902 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2903 if (!container
) continue;
2905 member
= sipe_find_container_member(container
, type
, value
);
2907 return containers
[i
];
2915 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2917 guint container_version
,
2918 const gchar
* action
,
2922 gchar
*self
= sip_uri_self(sip
);
2923 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2926 gchar
*body
= g_strdup_printf(
2927 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2928 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2929 "</setContainerMembers>",
2937 contact
= get_contact(sip
);
2938 hdr
= g_strdup_printf("Contact: %s\r\n"
2939 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2942 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2950 free_publication(struct sipe_publication
*publication
)
2952 g_free(publication
->category
);
2953 g_free(publication
->cal_event_hash
);
2954 g_free(publication
->note
);
2956 g_free(publication
->working_hours_xml_str
);
2957 g_free(publication
->fb_start_str
);
2958 g_free(publication
->free_busy_base64
);
2960 g_free(publication
);
2963 /* key is <category><instance><container> */
2965 sipe_is_our_publication(struct sipe_account_data
*sip
,
2970 /* filling keys for our publications if not yet cached */
2971 if (!sip
->our_publication_keys
) {
2972 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
2973 guint machine_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
2974 guint user_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
);
2975 guint calendar_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
2976 guint cal_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
);
2977 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
2978 guint note_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
);
2980 purple_debug_info("sipe", "* Our Publication Instances *\n");
2981 purple_debug_info("sipe", "\tDevice : %u\t0x%08X\n", device_instance
, device_instance
);
2982 purple_debug_info("sipe", "\tMachine State : %u\t0x%08X\n", machine_instance
, machine_instance
);
2983 purple_debug_info("sipe", "\tUser Stare : %u\t0x%08X\n", user_instance
, user_instance
);
2984 purple_debug_info("sipe", "\tCalendar State : %u\t0x%08X\n", calendar_instance
, calendar_instance
);
2985 purple_debug_info("sipe", "\tCalendar OOF State : %u\t0x%08X\n", cal_oof_instance
, cal_oof_instance
);
2986 purple_debug_info("sipe", "\tCalendar FreeBusy : %u\t0x%08X\n", cal_data_instance
, cal_data_instance
);
2987 purple_debug_info("sipe", "\tOOF Note : %u\t0x%08X\n", note_oof_instance
, note_oof_instance
);
2988 purple_debug_info("sipe", "\tNote : %u\n", 0);
2989 purple_debug_info("sipe", "\tCalendar WorkingHours: %u\n", 0);
2992 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2993 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
2995 /* state:machineState */
2996 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2997 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
2998 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2999 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
3001 /* state:userState */
3002 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3003 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
3004 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3005 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
3007 /* state:calendarState */
3008 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3009 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
3010 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3011 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
3013 /* state:calendarState OOF */
3014 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3015 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
3016 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3017 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
3020 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3021 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
3022 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3023 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
3024 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3025 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
3028 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3029 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
3030 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3031 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
3032 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3033 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
3035 /* calendarData:WorkingHours */
3036 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3037 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
3038 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3039 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
3040 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3041 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
3042 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3043 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
3044 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3045 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
3046 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3047 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
3049 /* calendarData:FreeBusy */
3050 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3051 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
3052 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3053 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
3054 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3055 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
3056 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3057 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
3058 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3059 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
3060 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3061 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
3063 //purple_debug_info("sipe", "sipe_is_our_publication: sip->our_publication_keys length=%d\n",
3064 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
3067 //purple_debug_info("sipe", "sipe_is_our_publication: key=%s\n", key);
3069 entry
= sip
->our_publication_keys
;
3071 //purple_debug_info("sipe", " sipe_is_our_publication: entry->data=%s\n", entry->data);
3072 if (sipe_strequal(entry
->data
, key
)) {
3075 entry
= entry
->next
;
3080 /** Property names to store in blist.xml */
3081 #define ALIAS_PROP "alias"
3082 #define EMAIL_PROP "email"
3083 #define PHONE_PROP "phone"
3084 #define PHONE_DISPLAY_PROP "phone-display"
3085 #define PHONE_MOBILE_PROP "phone-mobile"
3086 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
3087 #define PHONE_HOME_PROP "phone-home"
3088 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
3089 #define PHONE_OTHER_PROP "phone-other"
3090 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
3091 #define PHONE_CUSTOM1_PROP "phone-custom1"
3092 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
3093 #define SITE_PROP "site"
3094 #define COMPANY_PROP "company"
3095 #define DEPARTMENT_PROP "department"
3096 #define TITLE_PROP "title"
3097 #define OFFICE_PROP "office"
3098 /** implies work address */
3099 #define ADDRESS_STREET_PROP "address-street"
3100 #define ADDRESS_CITY_PROP "address-city"
3101 #define ADDRESS_STATE_PROP "address-state"
3102 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
3103 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
3106 * Tries to figure out user first and last name
3107 * based on Display Name and email properties.
3109 * Allocates memory - must be g_free()'d
3111 * Examples to parse:
3113 * First Last - Company Name
3116 * Last, First (C)(STP) (Company)
3117 * first.last@company.com (preprocessed as "first last")
3118 * first.last.company.com@reuters.net (preprocessed as "first last company com")
3120 * Unusable examples:
3121 * user@company.com (preprocessed as "user")
3122 * first.m.last@company.com (preprocessed as "first m last")
3123 * user.company.com@reuters.net (preprocessed as "user company com")
3126 sipe_get_first_last_names(struct sipe_account_data
*sip
,
3131 PurpleBuddy
*p_buddy
;
3134 const char *first
, *last
;
3137 gboolean has_comma
= FALSE
;
3139 if (!sip
|| !uri
) return;
3141 p_buddy
= purple_find_buddy(sip
->account
, uri
);
3143 if (!p_buddy
) return;
3145 display_name
= g_strdup(purple_buddy_get_alias(p_buddy
));
3146 email
= purple_blist_node_get_string(&p_buddy
->node
, EMAIL_PROP
);
3148 if (!display_name
&& !email
) return;
3150 /* if no display name, make "first last anything_else" out of email */
3151 if (email
&& !display_name
) {
3152 display_name
= g_strndup(email
, strstr(email
, "@") - email
);
3153 display_name
= purple_strreplace((tmp
= display_name
), ".", " ");
3158 has_comma
= (strstr(display_name
, ",") != NULL
);
3159 display_name
= purple_strreplace((tmp
= display_name
), ", ", " ");
3161 display_name
= purple_strreplace((tmp
= display_name
), ",", " ");
3165 parts
= g_strsplit(display_name
, " ", 0);
3167 if (!parts
[0] || !parts
[1]) {
3168 g_free(display_name
);
3182 *first_name
= g_strstrip(g_strdup(first
));
3186 *last_name
= g_strstrip(g_strdup(last
));
3189 g_free(display_name
);
3194 * Update user information
3196 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3197 * @param property_name
3198 * @param property_value may be modified to strip white space
3201 sipe_update_user_info(struct sipe_account_data
*sip
,
3203 const char *property_name
,
3204 char *property_value
)
3206 GSList
*buddies
, *entry
;
3208 if (!property_name
|| strlen(property_name
) == 0) return;
3211 property_value
= g_strstrip(property_value
);
3213 entry
= buddies
= purple_find_buddies(sip
->account
, uri
); /* all buddies in different groups */
3215 const char *prop_str
;
3216 const char *server_alias
;
3217 PurpleBuddy
*p_buddy
= entry
->data
;
3219 /* for Display Name */
3220 if (sipe_strequal(property_name
, ALIAS_PROP
)) {
3221 if (property_value
&& sipe_is_bad_alias(uri
, purple_buddy_get_alias(p_buddy
))) {
3222 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, property_value
);
3223 purple_blist_alias_buddy(p_buddy
, property_value
);
3226 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3227 if (!is_empty(property_value
) &&
3228 (!sipe_strequal(property_value
, server_alias
) || is_empty(server_alias
)) )
3230 purple_blist_server_alias_buddy(p_buddy
, property_value
);
3233 /* for other properties */
3235 if (!is_empty(property_value
)) {
3236 prop_str
= purple_blist_node_get_string(&p_buddy
->node
, property_name
);
3237 if (!prop_str
|| !sipe_strcase_equal(prop_str
, property_value
)) {
3238 purple_blist_node_set_string(&p_buddy
->node
, property_name
, property_value
);
3243 entry
= entry
->next
;
3245 g_slist_free(buddies
);
3250 * Suitable for both 2005 and 2007 systems.
3252 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3254 * @param phone may be modified to strip white space
3255 * @param phone_display_string may be modified to strip white space
3258 sipe_update_user_phone(struct sipe_account_data
*sip
,
3260 const gchar
*phone_type
,
3262 gchar
*phone_display_string
)
3264 const char *phone_node
= PHONE_PROP
; /* work phone by default */
3265 const char *phone_display_node
= PHONE_DISPLAY_PROP
; /* work phone by default */
3267 if(!phone
|| strlen(phone
) == 0) return;
3269 if ((sipe_strequal(phone_type
, "mobile") || sipe_strequal(phone_type
, "cell"))) {
3270 phone_node
= PHONE_MOBILE_PROP
;
3271 phone_display_node
= PHONE_MOBILE_DISPLAY_PROP
;
3272 } else if (sipe_strequal(phone_type
, "home")) {
3273 phone_node
= PHONE_HOME_PROP
;
3274 phone_display_node
= PHONE_HOME_DISPLAY_PROP
;
3275 } else if (sipe_strequal(phone_type
, "other")) {
3276 phone_node
= PHONE_OTHER_PROP
;
3277 phone_display_node
= PHONE_OTHER_DISPLAY_PROP
;
3278 } else if (sipe_strequal(phone_type
, "custom1")) {
3279 phone_node
= PHONE_CUSTOM1_PROP
;
3280 phone_display_node
= PHONE_CUSTOM1_DISPLAY_PROP
;
3283 sipe_update_user_info(sip
, uri
, phone_node
, phone
);
3284 if (phone_display_string
) {
3285 sipe_update_user_info(sip
, uri
, phone_display_node
, phone_display_string
);
3290 sipe_update_calendar(struct sipe_account_data
*sip
)
3292 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
3294 purple_debug_info("sipe", "sipe_update_calendar: started.\n");
3296 if (sipe_strequal(calendar
, "EXCH")) {
3297 sipe_ews_update_calendar(sip
);
3300 /* schedule repeat */
3301 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_INTERVAL
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3303 purple_debug_info("sipe", "sipe_update_calendar: finished.\n");
3307 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
3308 * by using standard Purple's means of signals and saved statuses.
3310 * Thus all UI elements get updated: Status Button with Note, docklet.
3311 * This is ablolutely important as both our status and note can come
3312 * inbound (roaming) or be updated programmatically (e.g. based on our
3316 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
3317 const char *status_id
,
3318 const char *message
,
3319 time_t do_not_publish
[])
3321 PurpleStatus
*status
= purple_account_get_active_status(account
);
3322 gboolean changed
= TRUE
;
3324 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
3325 sipe_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
3330 if (purple_savedstatus_is_idleaway()) {
3335 PurpleSavedStatus
*saved_status
;
3336 const PurpleStatusType
*acct_status_type
=
3337 purple_status_type_find_with_id(account
->status_types
, status_id
);
3338 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
3339 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
3341 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
3343 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
3346 /* If this type+message is unique then create a new transient saved status
3347 * Ref: gtkstatusbox.c
3349 if (!saved_status
) {
3351 GList
*active_accts
= purple_accounts_get_all_active();
3353 saved_status
= purple_savedstatus_new(NULL
, primitive
);
3354 purple_savedstatus_set_message(saved_status
, message
);
3356 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
3357 purple_savedstatus_set_substatus(saved_status
,
3358 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
3360 g_list_free(active_accts
);
3363 do_not_publish
[activity
] = time(NULL
);
3364 purple_debug_info("sipe", "sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]\n",
3365 status_id
, (int)do_not_publish
[activity
]);
3367 /* Set the status for each account */
3368 purple_savedstatus_activate(saved_status
);
3372 struct hash_table_delete_payload
{
3373 GHashTable
*hash_table
;
3378 sipe_remove_category_container_publications_cb(const char *name
,
3379 struct sipe_publication
*publication
,
3380 struct hash_table_delete_payload
*payload
)
3382 if (publication
->container
== payload
->container
) {
3383 g_hash_table_remove(payload
->hash_table
, name
);
3387 sipe_remove_category_container_publications(GHashTable
*our_publications
,
3388 const char *category
,
3391 struct hash_table_delete_payload payload
;
3392 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
3394 if (!payload
.hash_table
) return;
3396 payload
.container
= container
;
3397 g_hash_table_foreach(payload
.hash_table
, (GHFunc
)sipe_remove_category_container_publications_cb
, &payload
);
3401 send_publish_category_initial(struct sipe_account_data
*sip
);
3404 * When we receive some self (BE) NOTIFY with a new subscriber
3405 * we sends a setSubscribers request to him [SIP-PRES] 4.8
3408 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3415 char *display_name
= NULL
;
3417 GSList
*category_names
= NULL
;
3418 int aggreg_avail
= 0;
3419 static sipe_activity aggreg_activity
= SIPE_ACTIVITY_UNSET
;
3420 gboolean do_update_status
= FALSE
;
3421 gboolean has_note_cleaned
= FALSE
;
3423 purple_debug_info("sipe", "sipe_process_roaming_self\n");
3425 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3428 contact
= get_contact(sip
);
3429 to
= sip_uri_self(sip
);
3433 /* set list of categories participating in this XML */
3434 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3435 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3436 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
3438 purple_debug_info("sipe", "sipe_process_roaming_self: category_names length=%d\n",
3439 category_names
? (int) g_slist_length(category_names
) : -1);
3440 /* drop category information */
3441 if (category_names
) {
3442 GSList
*entry
= category_names
;
3444 GHashTable
*cat_publications
;
3445 const gchar
*category
= entry
->data
;
3446 entry
= entry
->next
;
3447 purple_debug_info("sipe", "sipe_process_roaming_self: dropping category: %s\n", category
);
3448 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
3449 if (cat_publications
) {
3450 g_hash_table_remove(sip
->our_publications
, category
);
3451 purple_debug_info("sipe", " sipe_process_roaming_self: dropped category: %s\n", category
);
3455 g_slist_free(category_names
);
3456 /* filling our categories reflected in roaming data */
3457 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3459 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3460 guint container
= xmlnode_get_int_attrib(node
, "container", -1);
3461 guint instance
= xmlnode_get_int_attrib(node
, "instance", -1);
3462 guint version
= xmlnode_get_int_attrib(node
, "version", 0);
3463 time_t publish_time
= (tmp
= xmlnode_get_attrib(node
, "publishTime")) ?
3464 sipe_utils_str_to_time(tmp
) : 0;
3466 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
3468 /* Ex. clear note: <category name="note"/> */
3469 if (container
== (guint
)-1) {
3472 do_update_status
= TRUE
;
3476 /* Ex. clear note: <category name="note" container="200"/> */
3477 if (instance
== (guint
)-1) {
3478 if (container
== 200) {
3481 do_update_status
= TRUE
;
3483 purple_debug_info("sipe", "sipe_process_roaming_self: removing publications for: %s/%u\n", name
, container
);
3484 sipe_remove_category_container_publications(
3485 sip
->our_publications
, name
, container
);
3489 /* key is <category><instance><container> */
3490 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
3491 purple_debug_info("sipe", "sipe_process_roaming_self: key=%s version=%d\n", key
, version
);
3493 /* capture all userState publication for later clean up if required */
3494 if (sipe_strequal(name
, "state") && (container
== 2 || container
== 3)) {
3495 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3497 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "userState")) {
3498 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3499 publication
->category
= g_strdup(name
);
3500 publication
->instance
= instance
;
3501 publication
->container
= container
;
3502 publication
->version
= version
;
3504 if (!sip
->user_state_publications
) {
3505 sip
->user_state_publications
= g_hash_table_new_full(
3506 g_str_hash
, g_str_equal
,
3507 g_free
, (GDestroyNotify
)free_publication
);
3509 g_hash_table_insert(sip
->user_state_publications
, g_strdup(key
), publication
);
3510 purple_debug_info("sipe", "sipe_process_roaming_self: added to user_state_publications key=%s version=%d\n",
3515 if (sipe_is_our_publication(sip
, key
)) {
3516 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3518 publication
->category
= g_strdup(name
);
3519 publication
->instance
= instance
;
3520 publication
->container
= container
;
3521 publication
->version
= version
;
3523 /* filling publication->availability */
3524 if (sipe_strequal(name
, "state")) {
3525 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3526 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3529 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3531 publication
->availability
= atoi(avail_str
);
3535 /* for calendarState */
3536 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "calendarState")) {
3537 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3538 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
3540 event
->start_time
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "startTime"));
3542 if (sipe_strequal(xmlnode_get_attrib(xn_activity
, "token"),
3543 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
))
3545 event
->is_meeting
= TRUE
;
3548 event
->subject
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingSubject"));
3549 event
->location
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingLocation"));
3551 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
3552 purple_debug_info("sipe", "sipe_process_roaming_self: hash=%s\n",
3553 publication
->cal_event_hash
);
3554 sipe_cal_event_free(event
);
3557 /* filling publication->note */
3558 if (sipe_strequal(name
, "note")) {
3559 xmlnode
*xn_body
= xmlnode_get_descendant(node
, "note", "body", NULL
);
3561 if (!has_note_cleaned
) {
3562 has_note_cleaned
= TRUE
;
3566 sip
->note_since
= publish_time
;
3568 do_update_status
= TRUE
;
3571 g_free(publication
->note
);
3572 publication
->note
= NULL
;
3576 publication
->note
= g_markup_escape_text((tmp
= xmlnode_get_data(xn_body
)), -1);
3578 if (publish_time
>= sip
->note_since
) {
3580 sip
->note
= g_strdup(publication
->note
);
3581 sip
->note_since
= publish_time
;
3582 sip
->is_oof_note
= sipe_strequal(xmlnode_get_attrib(xn_body
, "type"), "OOF");
3584 do_update_status
= TRUE
;
3589 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
3590 if (sipe_strequal(name
, "calendarData") && (publication
->container
== 300)) {
3591 xmlnode
*xn_free_busy
= xmlnode_get_descendant(node
, "calendarData", "freeBusy", NULL
);
3592 xmlnode
*xn_working_hours
= xmlnode_get_descendant(node
, "calendarData", "WorkingHours", NULL
);
3594 publication
->fb_start_str
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
3595 publication
->free_busy_base64
= xmlnode_get_data(xn_free_busy
);
3597 if (xn_working_hours
) {
3598 publication
->working_hours_xml_str
= xmlnode_to_str(xn_working_hours
, NULL
);
3602 if (!cat_publications
) {
3603 cat_publications
= g_hash_table_new_full(
3604 g_str_hash
, g_str_equal
,
3605 g_free
, (GDestroyNotify
)free_publication
);
3606 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
3607 purple_debug_info("sipe", "sipe_process_roaming_self: added GHashTable cat=%s\n", name
);
3609 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
3610 purple_debug_info("sipe", "sipe_process_roaming_self: added key=%s version=%d\n", key
, version
);
3614 /* aggregateState (not an our publication) from 2-nd container */
3615 if (sipe_strequal(name
, "state") && container
== 2) {
3616 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3618 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "aggregateState")) {
3619 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3620 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3623 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3625 aggreg_avail
= atoi(avail_str
);
3631 const char *activity_token
= xmlnode_get_attrib(xn_activity
, "token");
3633 aggreg_activity
= sipe_get_activity_by_token(activity_token
);
3636 do_update_status
= TRUE
;
3640 /* userProperties published by server from AD */
3641 if (!sip
->csta
&& sipe_strequal(name
, "userProperties")) {
3643 /* line, for Remote Call Control (RCC) */
3644 for (line
= xmlnode_get_descendant(node
, "userProperties", "lines", "line", NULL
); line
; line
= xmlnode_get_next_twin(line
)) {
3645 const gchar
*line_server
= xmlnode_get_attrib(line
, "lineServer");
3646 const gchar
*line_type
= xmlnode_get_attrib(line
, "lineType");
3649 if (!line_server
|| !(sipe_strequal(line_type
, "Rcc") || sipe_strequal(line_type
, "Dual"))) continue;
3651 line_uri
= xmlnode_get_data(line
);
3653 purple_debug_info("sipe", "sipe_process_roaming_self: line_uri=%s server=%s\n", line_uri
, line_server
);
3654 sip_csta_open(sip
, line_uri
, line_server
);
3662 purple_debug_info("sipe", "sipe_process_roaming_self: sip->our_publications size=%d\n",
3663 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
3666 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3667 guint id
= xmlnode_get_int_attrib(node
, "id", 0);
3668 struct sipe_container
*container
= sipe_find_container(sip
, id
);
3671 sip
->containers
= g_slist_remove(sip
->containers
, container
);
3672 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
3673 free_container(container
);
3675 container
= g_new0(struct sipe_container
, 1);
3677 container
->version
= xmlnode_get_int_attrib(node
, "version", 0);
3678 sip
->containers
= g_slist_append(sip
->containers
, container
);
3679 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
3681 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
3682 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
3683 member
->type
= xmlnode_get_attrib(node2
, "type");
3684 member
->value
= xmlnode_get_attrib(node2
, "value");
3685 container
->members
= g_slist_append(container
->members
, member
);
3686 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
3687 member
->type
, member
->value
? member
->value
: "");
3691 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
3692 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
3693 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
3694 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
3695 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
3696 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
3697 /* initial set-up to let counterparties see your status */
3698 if (sameEnterpriseAL
< 0) {
3699 struct sipe_container
*container
= sipe_find_container(sip
, 200);
3700 guint version
= container
? container
->version
: 0;
3701 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
3703 if (federatedAL
< 0) {
3704 struct sipe_container
*container
= sipe_find_container(sip
, 100);
3705 guint version
= container
? container
->version
: 0;
3706 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
3708 sip
->access_level_set
= TRUE
;
3712 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3714 const char *acknowledged
;
3718 user
= xmlnode_get_attrib(node
, "user"); /* without 'sip:' prefix */
3719 if (!user
) continue;
3720 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
3721 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
3722 uri
= sip_uri_from_name(user
);
3724 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
3726 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
3727 if(sipe_strcase_equal(acknowledged
,"false")){
3728 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
3729 if (!purple_find_buddy(sip
->account
, uri
)) {
3730 purple_account_request_add(sip
->account
, uri
, _("you"), display_name
, NULL
);
3733 hdr
= g_strdup_printf(
3735 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
3737 body
= g_strdup_printf(
3738 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
3739 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
3740 "</setSubscribers>", user
);
3742 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
3746 g_free(display_name
);
3753 /* Publish initial state if not yet.
3754 * Assuming this happens on initial responce to subscription to roaming-self
3755 * so we've already updated our roaming data in full.
3758 if (!sip
->initial_state_published
) {
3759 send_publish_category_initial(sip
);
3760 sip
->initial_state_published
= TRUE
;
3762 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3763 do_update_status
= FALSE
;
3764 } else if (aggreg_avail
) {
3766 g_free(sip
->status
);
3767 if (aggreg_avail
&& aggreg_avail
< 18000) { /* not offline */
3768 sip
->status
= g_strdup(sipe_get_status_by_availability(aggreg_avail
, NULL
));
3770 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
3774 if (do_update_status
) {
3775 purple_debug_info("sipe", "sipe_process_roaming_self: switch to '%s' for the account\n", sip
->status
);
3776 sipe_set_purple_account_status_and_note(sip
->account
, sip
->status
, sip
->note
, sip
->do_not_publish
);
3782 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
)
3784 gchar
*to
= sip_uri_self(sip
);
3785 gchar
*tmp
= get_contact(sip
);
3786 gchar
*hdr
= g_strdup_printf(
3787 "Event: vnd-microsoft-roaming-ACL\r\n"
3788 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
3789 "Supported: com.microsoft.autoextend\r\n"
3790 "Supported: ms-benotify\r\n"
3791 "Proxy-Require: ms-benotify\r\n"
3792 "Supported: ms-piggyback-first-notify\r\n"
3793 "Contact: %s\r\n", tmp
);
3796 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
3802 * To request for presence information about the user, access level settings that have already been configured by the user
3803 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
3804 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
3807 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
)
3809 gchar
*to
= sip_uri_self(sip
);
3810 gchar
*tmp
= get_contact(sip
);
3811 gchar
*hdr
= g_strdup_printf(
3812 "Event: vnd-microsoft-roaming-self\r\n"
3813 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
3814 "Supported: ms-benotify\r\n"
3815 "Proxy-Require: ms-benotify\r\n"
3816 "Supported: ms-piggyback-first-notify\r\n"
3818 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
3820 gchar
*body
=g_strdup(
3821 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
3822 "<roaming type=\"categories\"/>"
3823 "<roaming type=\"containers\"/>"
3824 "<roaming type=\"subscribers\"/></roamingList>");
3827 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3836 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
)
3838 gchar
*to
= sip_uri_self(sip
);
3839 gchar
*tmp
= get_contact(sip
);
3840 gchar
*hdr
= g_strdup_printf(
3841 "Event: vnd-microsoft-provisioning\r\n"
3842 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
3843 "Supported: com.microsoft.autoextend\r\n"
3844 "Supported: ms-benotify\r\n"
3845 "Proxy-Require: ms-benotify\r\n"
3846 "Supported: ms-piggyback-first-notify\r\n"
3848 "Contact: %s\r\n", tmp
);
3851 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
3856 /** Subscription for provisioning information to help with initial
3857 * configuration. This subscription is a one-time query (denoted by the Expires header,
3858 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
3859 * configuration, meeting policies, and policy settings that Communicator must enforce.
3860 * TODO: for what we need this information.
3863 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
)
3865 gchar
*to
= sip_uri_self(sip
);
3866 gchar
*tmp
= get_contact(sip
);
3867 gchar
*hdr
= g_strdup_printf(
3868 "Event: vnd-microsoft-provisioning-v2\r\n"
3869 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
3870 "Supported: com.microsoft.autoextend\r\n"
3871 "Supported: ms-benotify\r\n"
3872 "Proxy-Require: ms-benotify\r\n"
3873 "Supported: ms-piggyback-first-notify\r\n"
3876 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
3877 gchar
*body
= g_strdup(
3878 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
3879 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
3880 "<provisioningGroup name=\"ucPolicy\"/>"
3881 "</provisioningGroupList>");
3884 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3891 sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key
,
3892 gpointer value
, gpointer user_data
)
3894 struct sip_subscription
*subscription
= value
;
3895 struct sip_dialog
*dialog
= &subscription
->dialog
;
3896 struct sipe_account_data
*sip
= user_data
;
3897 gchar
*tmp
= get_contact(sip
);
3898 gchar
*hdr
= g_strdup_printf(
3901 "Contact: %s\r\n", subscription
->event
, tmp
);
3904 /* Rate limit to max. 25 requests per seconds */
3905 g_usleep(1000000 / 25);
3907 send_sip_request(sip
->gc
, "SUBSCRIBE", dialog
->with
, dialog
->with
, hdr
, NULL
, dialog
, NULL
);
3911 /* IM Session (INVITE and MESSAGE methods) */
3913 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
3915 get_end_points (struct sipe_account_data
*sip
,
3916 struct sip_session
*session
)
3920 if (session
== NULL
) {
3924 res
= g_strdup_printf("<sip:%s>", sip
->username
);
3926 SIPE_DIALOG_FOREACH
{
3928 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
3931 if (dialog
->theirepid
) {
3933 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
3936 } SIPE_DIALOG_FOREACH_END
;
3942 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_account_data
*sip
,
3944 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3946 gboolean ret
= TRUE
;
3948 if (msg
->response
!= 200) {
3949 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
3953 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
3959 * Asks UA/proxy about its capabilities.
3961 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
3963 gchar
*to
= sip_uri(who
);
3964 gchar
*contact
= get_contact(sip
);
3965 gchar
*request
= g_strdup_printf(
3966 "Accept: application/sdp\r\n"
3967 "Contact: %s\r\n", contact
);
3970 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
3977 sipe_notify_user(struct sipe_account_data
*sip
,
3978 struct sip_session
*session
,
3979 PurpleMessageFlags flags
,
3980 const gchar
*message
)
3982 PurpleConversation
*conv
;
3984 if (!session
->conv
) {
3985 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
3987 conv
= session
->conv
;
3989 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
3993 sipe_present_info(struct sipe_account_data
*sip
,
3994 struct sip_session
*session
,
3995 const gchar
*message
)
3997 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
4001 sipe_present_err(struct sipe_account_data
*sip
,
4002 struct sip_session
*session
,
4003 const gchar
*message
)
4005 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_ERROR
, message
);
4009 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
4010 struct sip_session
*session
,
4014 const gchar
*message
)
4016 char *msg
, *msg_tmp
, *msg_tmp2
;
4019 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
4020 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
4022 /* Service unavailable; Server Internal Error; Server Time-out */
4023 if (sip_error
== 606 && sip_warning
== 309) { /* Not acceptable all. */ /* Message contents not allowed by policy */
4024 label
= _("Your message or invitation was not delivered, possibly because it contains a hyperlink or other content that the system administrator has blocked.");
4027 } else if (sip_error
== 503 || sip_error
== 500 || sip_error
== 504) {
4028 label
= _("This message was not delivered to %s because the service is not available");
4029 } else if (sip_error
== 486) { /* Busy Here */
4030 label
= _("This message was not delivered to %s because one or more recipients do not want to be disturbed");
4031 } else if (sip_error
== 415) { /* Unsupported media type */
4032 label
= _("This message was not delivered to %s because one or more recipients don't support this type of message");
4034 label
= _("This message was not delivered to %s because one or more recipients are offline");
4037 msg_tmp
= g_strdup_printf( "%s%s\n%s" ,
4038 msg_tmp2
= g_strdup_printf(label
, who
? who
: ""),
4041 sipe_present_err(sip
, session
, msg_tmp
);
4049 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4050 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
4052 gboolean ret
= TRUE
;
4053 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
4054 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
4055 struct sip_dialog
*dialog
;
4058 struct queued_message
*message
;
4061 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
4066 dialog
= sipe_dialog_find(session
, with
);
4068 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
4073 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4074 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
4076 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4078 if (msg
->response
>= 400) {
4079 PurpleBuddy
*pbuddy
;
4080 const char *alias
= with
;
4081 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
4084 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
4087 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
4089 warning
= atoi(parts
[0]);
4094 /* cancel file transfer as rejected by server */
4095 if (msg
->response
== 606 && /* Not acceptable all. */
4096 warning
== 309 && /* Message contents not allowed by policy */
4097 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
4099 GSList
*parsed_body
= sipe_ft_parse_msg_body(msg
->body
);
4100 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4101 sipe_utils_nameval_free(parsed_body
);
4104 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4105 alias
= purple_buddy_get_alias(pbuddy
);
4108 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, warning
, alias
, (message
? message
->body
: NULL
));
4110 /* drop dangling IM sessions: assume that BYE from remote never reached us */
4111 if (msg
->response
== 408 || /* Request timeout */
4112 msg
->response
== 480 || /* Temporarily Unavailable */
4113 msg
->response
== 481) { /* Call/Transaction Does Not Exist */
4114 purple_debug_info("sipe", "process_message_response: assuming dangling IM session, dropping it.\n");
4115 send_sip_request(sip
->gc
, "BYE", with
, with
, NULL
, NULL
, dialog
, NULL
);
4120 const gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
4122 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
->body
));
4123 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
4124 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
4127 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4128 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
4129 key
, g_hash_table_size(session
->unconfirmed_messages
));
4135 if (ret
) sipe_im_process_queue(sip
, session
);
4140 sipe_is_election_finished(struct sip_session
*session
);
4143 sipe_election_result(struct sipe_account_data
*sip
,
4147 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4148 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
4150 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4151 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4152 struct sip_dialog
*dialog
;
4153 struct sip_session
*session
;
4155 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4157 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
4161 if (msg
->response
== 200 && g_str_has_prefix(contenttype
, "application/x-ms-mim")) {
4162 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4163 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
4164 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
4166 if (xn_request_rm_response
) {
4167 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
4168 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
4170 dialog
= sipe_dialog_find(session
, with
);
4172 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
4173 xmlnode_free(xn_action
);
4177 if (allow
&& !g_strcasecmp(allow
, "true")) {
4178 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
4179 dialog
->election_vote
= 1;
4180 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
4181 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
4182 dialog
->election_vote
= -1;
4185 if (sipe_is_election_finished(session
)) {
4186 sipe_election_result(sip
, session
);
4189 } else if (xn_set_rm_response
) {
4192 xmlnode_free(xn_action
);
4199 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
, const char *content_type
)
4203 char *msgtext
= NULL
;
4204 const gchar
*msgr
= "";
4207 if (!g_str_has_prefix(content_type
, "text/x-msmsgsinvite")) {
4211 sipe_parse_html(msg
, &msgformat
, &msgtext
);
4212 purple_debug_info("sipe", "sipe_send_message: msgformat=%s\n", msgformat
);
4214 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4217 msgr
= tmp2
= g_strdup_printf(";msgr=%s", msgr_value
);
4221 msgtext
= g_strdup(msg
);
4224 tmp
= get_contact(sip
);
4225 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
4226 //hdr = g_strdup("Content-Type: text/rtf\r\n");
4227 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
4228 if (content_type
== NULL
)
4229 content_type
= "text/plain";
4231 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp
, content_type
, msgr
);
4235 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
4242 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
4244 GSList
*entry2
= session
->outgoing_message_queue
;
4246 struct queued_message
*msg
= entry2
->data
;
4248 /* for multiparty chat or conference */
4249 if (session
->is_multiparty
|| session
->focus_uri
) {
4250 gchar
*who
= sip_uri_self(sip
);
4251 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
4252 PURPLE_MESSAGE_SEND
, msg
->body
, time(NULL
));
4256 SIPE_DIALOG_FOREACH
{
4258 struct queued_message
*message
;
4260 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
4262 message
= g_new0(struct queued_message
,1);
4263 message
->body
= g_strdup(msg
->body
);
4264 if (msg
->content_type
!= NULL
)
4265 message
->content_type
= g_strdup(msg
->content_type
);
4267 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
4268 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
4269 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
4270 key
, g_hash_table_size(session
->unconfirmed_messages
));
4273 sipe_send_message(sip
, dialog
, msg
->body
, msg
->content_type
);
4274 } SIPE_DIALOG_FOREACH_END
;
4276 entry2
= sipe_session_dequeue_message(session
);
4281 sipe_refer_notify(struct sipe_account_data
*sip
,
4282 struct sip_session
*session
,
4289 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4291 hdr
= g_strdup_printf(
4293 "Subscription-State: %s\r\n"
4294 "Content-Type: message/sipfrag\r\n",
4295 status
>= 200 ? "terminated" : "active");
4297 body
= g_strdup_printf(
4298 "SIP/2.0 %d %s\r\n",
4301 send_sip_request(sip
->gc
, "NOTIFY", who
, who
, hdr
, body
, dialog
, NULL
);
4308 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
4310 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
4311 struct sip_session
*session
;
4312 struct sip_dialog
*dialog
;
4315 struct queued_message
*message
;
4316 struct sipmsg
*request_msg
= trans
->msg
;
4318 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4321 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4323 session
= sipe_session_find_im(sip
, with
);
4326 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
4331 dialog
= sipe_dialog_find(session
, with
);
4333 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
4338 sipe_dialog_parse(dialog
, msg
, TRUE
);
4340 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4341 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
4343 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4345 if (msg
->response
!= 200) {
4346 PurpleBuddy
*pbuddy
;
4347 const char *alias
= with
;
4348 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
4351 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
4354 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
4356 warning
= atoi(parts
[0]);
4361 /* cancel file transfer as rejected by server */
4362 if (msg
->response
== 606 && /* Not acceptable all. */
4363 warning
== 309 && /* Message contents not allowed by policy */
4364 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
4366 GSList
*parsed_body
= sipe_ft_parse_msg_body(message
->body
);
4367 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4368 sipe_utils_nameval_free(parsed_body
);
4371 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4372 alias
= purple_buddy_get_alias(pbuddy
);
4376 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, warning
, alias
, message
->body
);
4378 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
4379 sipe_present_err(sip
, session
, tmp_msg
);
4383 sipe_dialog_remove(session
, with
);
4391 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4392 dialog
->outgoing_invite
= NULL
;
4393 dialog
->is_established
= TRUE
;
4395 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
4397 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
4398 g_free(referred_by
);
4401 /* add user to chat if it is a multiparty session */
4402 if (session
->is_multiparty
) {
4403 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4405 PURPLE_CBFLAGS_NONE
, TRUE
);
4408 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
4409 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
4410 sipe_session_dequeue_message(session
);
4413 sipe_im_process_queue(sip
, session
);
4415 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4416 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
4417 key
, g_hash_table_size(session
->unconfirmed_messages
));
4426 sipe_invite(struct sipe_account_data
*sip
,
4427 struct sip_session
*session
,
4429 const gchar
*msg_body
,
4430 const gchar
*msg_content_type
,
4431 const gchar
*referred_by
,
4432 const gboolean is_triggered
)
4439 char *ms_text_format
= NULL
;
4440 gchar
*roster_manager
;
4442 gchar
*referred_by_str
;
4443 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4445 if (dialog
&& dialog
->is_established
) {
4446 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
4451 dialog
= sipe_dialog_add(session
);
4452 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
4453 dialog
->with
= g_strdup(who
);
4456 if (!(dialog
->ourtag
)) {
4457 dialog
->ourtag
= gentag();
4463 char *msgtext
= NULL
;
4465 const gchar
*msgr
= "";
4467 struct queued_message
*message
;
4470 if (!g_str_has_prefix(msg_content_type
, "text/x-msmsgsinvite")) {
4474 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
4475 purple_debug_info("sipe", "sipe_invite: msgformat=%s\n", msgformat
);
4477 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4480 msgr
= tmp
= g_strdup_printf(";msgr=%s", msgr_value
);
4484 msgtext
= g_strdup(msg_body
);
4487 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
4488 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
,
4489 msg_content_type
? msg_content_type
: "text/plain",
4496 message
= g_new0(struct queued_message
,1);
4497 message
->body
= g_strdup(msg_body
);
4498 if (msg_content_type
!= NULL
)
4499 message
->content_type
= g_strdup(msg_content_type
);
4501 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
4502 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
4503 purple_debug_info("sipe", "sipe_invite: added message %s to unconfirmed_messages(count=%d)\n",
4504 key
, g_hash_table_size(session
->unconfirmed_messages
));
4508 contact
= get_contact(sip
);
4509 end_points
= get_end_points(sip
, session
);
4510 self
= sip_uri_self(sip
);
4511 roster_manager
= g_strdup_printf(
4512 "Roster-Manager: %s\r\n"
4513 "EndPoints: %s\r\n",
4516 referred_by_str
= referred_by
?
4518 "Referred-By: %s\r\n",
4521 hdr
= g_strdup_printf(
4522 "Supported: ms-sender\r\n"
4528 "Content-Type: application/sdp\r\n",
4529 sipe_strcase_equal(session
->roster_manager
, self
) ? roster_manager
: "",
4531 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
4532 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
4534 ms_text_format
? ms_text_format
: "");
4535 g_free(ms_text_format
);
4538 body
= g_strdup_printf(
4540 "o=- 0 0 IN IP4 %s\r\n"
4544 "m=%s %d sip null\r\n"
4545 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
4546 purple_network_get_my_ip(-1),
4547 purple_network_get_my_ip(-1),
4548 sip
->ocs2007
? "message" : "x-ms-message",
4551 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
4552 to
, to
, hdr
, body
, dialog
, process_invite_response
);
4555 g_free(roster_manager
);
4557 g_free(referred_by_str
);
4564 sipe_refer(struct sipe_account_data
*sip
,
4565 struct sip_session
*session
,
4570 gchar
*epid
= get_epid(sip
);
4571 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
4572 session
->roster_manager
);
4573 const char *ourtag
= dialog
&& dialog
->ourtag
? dialog
->ourtag
: NULL
;
4575 contact
= get_contact(sip
);
4576 hdr
= g_strdup_printf(
4578 "Refer-to: <%s>\r\n"
4579 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
4580 "Require: com.microsoft.rtc-multiparty\r\n",
4584 ourtag
? ";tag=" : "",
4585 ourtag
? ourtag
: "",
4589 send_sip_request(sip
->gc
, "REFER",
4590 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
4597 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
4598 struct sip_dialog
*dialog
,
4601 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4603 gchar
*body
= g_strdup_printf(
4604 "<?xml version=\"1.0\"?>\r\n"
4605 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4606 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
4607 sip
->username
, bid
);
4609 send_sip_request(sip
->gc
, "INFO",
4610 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4616 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
4617 struct sip_dialog
*dialog
)
4619 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4621 gchar
*body
= g_strdup_printf(
4622 "<?xml version=\"1.0\"?>\r\n"
4623 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4624 "<SetRM uri=\"sip:%s\"/></action>\r\n",
4627 send_sip_request(sip
->gc
, "INFO",
4628 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4634 sipe_session_close(struct sipe_account_data
*sip
,
4635 struct sip_session
* session
)
4637 if (session
&& session
->focus_uri
) {
4638 sipe_conf_immcu_closed(sip
, session
);
4639 conf_session_close(sip
, session
);
4643 SIPE_DIALOG_FOREACH
{
4644 /* @TODO slow down BYE message sending rate */
4645 /* @see single subscription code */
4646 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4647 } SIPE_DIALOG_FOREACH_END
;
4649 sipe_session_remove(sip
, session
);
4654 sipe_session_close_all(struct sipe_account_data
*sip
)
4657 while ((entry
= sip
->sessions
) != NULL
) {
4658 sipe_session_close(sip
, entry
->data
);
4663 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
4665 struct sipe_account_data
*sip
= gc
->proto_data
;
4667 purple_debug_info("sipe", "conversation with %s closed\n", who
);
4668 sipe_session_close(sip
, sipe_session_find_im(sip
, who
));
4672 sipe_chat_leave (PurpleConnection
*gc
, int id
)
4674 struct sipe_account_data
*sip
= gc
->proto_data
;
4675 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
4677 sipe_session_close(sip
, session
);
4680 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
4681 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4683 struct sipe_account_data
*sip
= gc
->proto_data
;
4684 struct sip_session
*session
;
4685 struct sip_dialog
*dialog
;
4686 gchar
*uri
= sip_uri(who
);
4688 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
4690 session
= sipe_session_find_or_add_im(sip
, uri
);
4691 dialog
= sipe_dialog_find(session
, uri
);
4693 // Queue the message
4694 sipe_session_enqueue_message(session
, what
, NULL
);
4696 if (dialog
&& !dialog
->outgoing_invite
) {
4697 sipe_im_process_queue(sip
, session
);
4698 } else if (!dialog
|| !dialog
->outgoing_invite
) {
4699 // Need to send the INVITE to get the outgoing dialog setup
4700 sipe_invite(sip
, session
, uri
, what
, NULL
, NULL
, FALSE
);
4707 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
4708 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4710 struct sipe_account_data
*sip
= gc
->proto_data
;
4711 struct sip_session
*session
;
4713 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
4715 session
= sipe_session_find_chat_by_id(sip
, id
);
4717 // Queue the message
4718 if (session
&& session
->dialogs
) {
4719 sipe_session_enqueue_message(session
,what
,NULL
);
4720 sipe_im_process_queue(sip
, session
);
4722 gchar
*chat_name
= purple_find_chat(sip
->gc
, id
)->name
;
4723 const gchar
*proto_chat_id
= sipe_chat_find_name(chat_name
);
4725 purple_debug_info("sipe", "sipe_chat_send: chat_name='%s'\n", chat_name
? chat_name
: "NULL");
4726 purple_debug_info("sipe", "sipe_chat_send: proto_chat_id='%s'\n", proto_chat_id
? proto_chat_id
: "NULL");
4729 struct sip_session
*session
= sipe_session_add_chat(sip
);
4731 session
->is_multiparty
= FALSE
;
4732 session
->focus_uri
= g_strdup(proto_chat_id
);
4733 sipe_session_enqueue_message(session
, what
, NULL
);
4734 sipe_invite_conf_focus(sip
, session
);
4741 /* End IM Session (INVITE and MESSAGE methods) */
4743 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4745 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4746 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4748 struct sip_session
*session
;
4750 purple_debug_info("sipe", "process_incoming_info: \n%s\n", msg
->body
? msg
->body
: "");
4752 /* Call Control protocol */
4753 if (g_str_has_prefix(contenttype
, "application/csta+xml"))
4755 process_incoming_info_csta(sip
, msg
);
4759 from
= parse_from(sipmsg_find_header(msg
, "From"));
4760 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4762 session
= sipe_session_find_im(sip
, from
);
4769 if (g_str_has_prefix(contenttype
, "application/x-ms-mim"))
4771 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4772 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
4773 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
4775 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
4777 if (xn_request_rm
) {
4778 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
4779 int bid
= xmlnode_get_int_attrib(xn_request_rm
, "bid", 0);
4780 gchar
*body
= g_strdup_printf(
4781 "<?xml version=\"1.0\"?>\r\n"
4782 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4783 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
4785 session
->bid
< bid
? "true" : "false");
4786 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4788 } else if (xn_set_rm
) {
4790 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
4791 g_free(session
->roster_manager
);
4792 session
->roster_manager
= g_strdup(rm
);
4794 body
= g_strdup_printf(
4795 "<?xml version=\"1.0\"?>\r\n"
4796 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4797 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
4799 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4802 xmlnode_free(xn_action
);
4807 /* looks like purple lacks typing notification for chat */
4808 if (!session
->is_multiparty
&& !session
->focus_uri
) {
4809 xmlnode
*xn_keyboard_activity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4810 const char *status
= xmlnode_get_attrib(xmlnode_get_child(xn_keyboard_activity
, "status"),
4812 if (sipe_strequal(status
, "type")) {
4813 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
4814 } else if (sipe_strequal(status
, "idle")) {
4815 serv_got_typing_stopped(sip
->gc
, from
);
4817 xmlnode_free(xn_keyboard_activity
);
4820 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4825 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4827 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4828 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4829 struct sip_session
*session
;
4830 struct sip_dialog
*dialog
;
4832 /* collect dialog identification
4833 * we need callid, ourtag and theirtag to unambiguously identify dialog
4835 /* take data before 'msg' will be modified by send_sip_response */
4836 dialog
= g_new0(struct sip_dialog
, 1);
4837 dialog
->callid
= g_strdup(callid
);
4838 dialog
->cseq
= parse_cseq(sipmsg_find_header(msg
, "CSeq"));
4839 dialog
->with
= g_strdup(from
);
4840 sipe_dialog_parse(dialog
, msg
, FALSE
);
4842 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4844 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4846 session
= sipe_session_find_im(sip
, from
);
4849 sipe_dialog_free(dialog
);
4854 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
4855 g_free(session
->roster_manager
);
4856 session
->roster_manager
= NULL
;
4859 /* This what BYE is essentially for - terminating dialog */
4860 sipe_dialog_remove_3(session
, dialog
);
4861 sipe_dialog_free(dialog
);
4862 if (session
->focus_uri
&& !g_strcasecmp(from
, session
->im_mcu_uri
)) {
4863 sipe_conf_immcu_closed(sip
, session
);
4864 } else if (session
->is_multiparty
) {
4865 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
4871 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4873 gchar
*self
= sip_uri_self(sip
);
4874 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4875 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4876 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
4877 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
4878 struct sip_session
*session
;
4879 struct sip_dialog
*dialog
;
4881 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4882 dialog
= sipe_dialog_find(session
, from
);
4884 if (!session
|| !dialog
|| !session
->roster_manager
|| !sipe_strcase_equal(session
->roster_manager
, self
)) {
4885 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
4887 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
4889 sipe_invite(sip
, session
, refer_to
, NULL
, NULL
, referred_by
, FALSE
);
4895 g_free(referred_by
);
4899 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
4901 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
4902 struct sip_session
*session
;
4903 struct sip_dialog
*dialog
;
4905 if (state
== PURPLE_NOT_TYPING
)
4908 session
= sipe_session_find_im(sip
, who
);
4909 dialog
= sipe_dialog_find(session
, who
);
4911 if (session
&& dialog
&& dialog
->is_established
) {
4912 send_sip_request(gc
, "INFO", who
, who
,
4913 "Content-Type: application/xml\r\n",
4914 SIPE_SEND_TYPING
, dialog
, NULL
);
4916 return SIPE_TYPING_SEND_TIMEOUT
;
4919 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
4921 GSList
*tmp
= sip
->transactions
;
4922 time_t currtime
= time(NULL
);
4924 struct transaction
*trans
= tmp
->data
;
4926 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
4927 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
4930 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
4932 sendout_sipmsg(sip
, trans
->msg
);
4939 static void do_reauthenticate_cb(struct sipe_account_data
*sip
,
4940 SIPE_UNUSED_PARAMETER
void *unused
)
4942 /* register again when security token expires */
4943 /* we have to start a new authentication as the security token
4944 * is almost expired by sending a not signed REGISTER message */
4945 purple_debug_info("sipe", "do a full reauthentication\n");
4946 sipe_auth_free(&sip
->registrar
);
4947 sipe_auth_free(&sip
->proxy
);
4948 sip
->registerstatus
= 0;
4950 sip
->reauthenticate_set
= FALSE
;
4954 sipe_process_incoming_x_msmsgsinvite(struct sipe_account_data
*sip
,
4956 GSList
*parsed_body
)
4958 gboolean found
= FALSE
;
4961 const gchar
*invitation_command
= sipe_utils_nameval_find(parsed_body
, "Invitation-Command");
4963 if (sipe_strequal(invitation_command
, "INVITE")) {
4964 sipe_ft_incoming_transfer(sip
->gc
->account
, msg
, parsed_body
);
4966 } else if (sipe_strequal(invitation_command
, "CANCEL")) {
4967 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4969 } else if (sipe_strequal(invitation_command
, "ACCEPT")) {
4970 sipe_ft_incoming_accept(sip
->gc
->account
, parsed_body
);
4977 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4980 const gchar
*contenttype
;
4981 gboolean found
= FALSE
;
4983 from
= parse_from(sipmsg_find_header(msg
, "From"));
4987 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
4989 contenttype
= sipmsg_find_header(msg
, "Content-Type");
4990 if (g_str_has_prefix(contenttype
, "text/plain")
4991 || g_str_has_prefix(contenttype
, "text/html")
4992 || g_str_has_prefix(contenttype
, "multipart/related")
4993 || g_str_has_prefix(contenttype
, "multipart/alternative"))
4995 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4996 gchar
*html
= get_html_message(contenttype
, msg
->body
);
4998 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
5000 session
= sipe_session_find_im(sip
, from
);
5003 if (session
&& session
->focus_uri
) { /* a conference */
5004 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
5005 gchar
*sender
= parse_from(tmp
);
5007 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
5008 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5010 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
5011 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
5012 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5014 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
5017 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5020 } else if (g_str_has_prefix(contenttype
, "application/im-iscomposing+xml")) {
5021 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5026 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
5031 state
= xmlnode_get_child(isc
, "state");
5034 purple_debug_info("sipe", "process_incoming_message: no state found\n");
5040 statedata
= xmlnode_get_data(state
);
5042 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
5043 else serv_got_typing_stopped(sip
->gc
, from
);
5048 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5050 } else if (g_str_has_prefix(contenttype
, "text/x-msmsgsinvite")) {
5051 GSList
*body
= sipe_ft_parse_msg_body(msg
->body
);
5052 found
= sipe_process_incoming_x_msmsgsinvite(sip
, msg
, body
);
5053 sipe_utils_nameval_free(body
);
5055 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5059 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
5060 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
5062 session
= sipe_session_find_im(sip
, from
);
5065 gchar
*errmsg
= g_strdup_printf(_("Received a message with unrecognized contents from %s"),
5067 sipe_present_err(sip
, session
, errmsg
);
5071 purple_debug_info("sipe", "got unknown mime-type '%s'\n", contenttype
);
5072 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
5077 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5081 const gchar
*oldHeader
;
5083 gboolean is_multiparty
= FALSE
;
5084 gboolean is_triggered
= FALSE
;
5085 gboolean was_multiparty
= TRUE
;
5086 gboolean just_joined
= FALSE
;
5088 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
5089 const gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
5090 const gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
5091 const gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
5092 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
5093 GSList
*end_points
= NULL
;
5095 struct sip_session
*session
;
5096 const gchar
*ms_text_format
;
5098 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? tmp
= fix_newlines(msg
->body
) : "");
5101 /* Invitation to join conference */
5102 if (g_str_has_prefix(content_type
, "application/ms-conf-invite+xml")) {
5103 process_incoming_invite_conf(sip
, msg
);
5107 /* Only accept text invitations */
5108 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
5109 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
5113 // TODO There *must* be a better way to clean up the To header to add a tag...
5114 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
5115 oldHeader
= sipmsg_find_header(msg
, "To");
5117 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
5118 sipmsg_remove_header_now(msg
, "To");
5119 sipmsg_add_header_now(msg
, "To", newHeader
);
5122 if (end_points_hdr
) {
5123 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
5125 if (g_slist_length(end_points
) > 2) {
5126 is_multiparty
= TRUE
;
5129 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
5130 is_triggered
= TRUE
;
5131 is_multiparty
= TRUE
;
5134 session
= sipe_session_find_chat_by_callid(sip
, callid
);
5135 /* Convert to multiparty */
5136 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
5137 g_free(session
->with
);
5138 session
->with
= NULL
;
5139 was_multiparty
= FALSE
;
5140 session
->is_multiparty
= TRUE
;
5141 session
->chat_id
= rand();
5144 if (!session
&& is_multiparty
) {
5145 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
5148 from
= parse_from(sipmsg_find_header(msg
, "From"));
5150 session
= sipe_session_find_or_add_im(sip
, from
);
5154 g_free(session
->callid
);
5155 session
->callid
= g_strdup(callid
);
5157 session
->is_multiparty
= is_multiparty
;
5158 if (roster_manager
) {
5159 session
->roster_manager
= g_strdup(roster_manager
);
5163 if (is_multiparty
&& end_points
) {
5164 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
5165 GSList
*entry
= end_points
;
5167 struct sip_dialog
*dialog
;
5168 struct sipendpoint
*end_point
= entry
->data
;
5169 entry
= entry
->next
;
5171 if (!g_strcasecmp(from
, end_point
->contact
) ||
5172 !g_strcasecmp(to
, end_point
->contact
))
5175 dialog
= sipe_dialog_find(session
, end_point
->contact
);
5177 g_free(dialog
->theirepid
);
5178 dialog
->theirepid
= end_point
->epid
;
5179 end_point
->epid
= NULL
;
5181 dialog
= sipe_dialog_add(session
);
5183 dialog
->callid
= g_strdup(session
->callid
);
5184 dialog
->with
= end_point
->contact
;
5185 end_point
->contact
= NULL
;
5186 dialog
->theirepid
= end_point
->epid
;
5187 end_point
->epid
= NULL
;
5191 /* send triggered INVITE */
5192 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, NULL
, TRUE
);
5199 GSList
*entry
= end_points
;
5201 struct sipendpoint
*end_point
= entry
->data
;
5202 entry
= entry
->next
;
5203 g_free(end_point
->contact
);
5204 g_free(end_point
->epid
);
5207 g_slist_free(end_points
);
5211 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
5213 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
5214 sipe_dialog_parse_routes(dialog
, msg
, FALSE
);
5216 dialog
= sipe_dialog_add(session
);
5218 dialog
->callid
= g_strdup(session
->callid
);
5219 dialog
->with
= g_strdup(from
);
5220 sipe_dialog_parse(dialog
, msg
, FALSE
);
5222 if (!dialog
->ourtag
) {
5223 dialog
->ourtag
= newTag
;
5230 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
5234 if (is_multiparty
&& !session
->conv
) {
5235 gchar
*chat_title
= sipe_chat_get_name(callid
);
5236 gchar
*self
= sip_uri_self(sip
);
5237 /* create prpl chat */
5238 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_title
);
5239 session
->chat_title
= g_strdup(chat_title
);
5240 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
5242 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5244 PURPLE_CBFLAGS_NONE
, FALSE
);
5249 if (is_multiparty
&& !was_multiparty
) {
5250 /* add current IM counterparty to chat */
5251 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5252 sipe_dialog_first(session
)->with
, NULL
,
5253 PURPLE_CBFLAGS_NONE
, FALSE
);
5256 /* add inviting party to chat */
5257 if (just_joined
&& session
->conv
) {
5258 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5260 PURPLE_CBFLAGS_NONE
, TRUE
);
5263 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
5265 /* This used only in 2005 official client, not 2007 or Reuters.
5266 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
5267 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
5269 /* also enabled for 2005 file transfer. Didn't work otherwise. */
5270 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
5271 if (is_multiparty
||
5272 (ms_text_format
&& g_str_has_prefix(ms_text_format
, "text/x-msmsgsinvite")) )
5274 if (ms_text_format
) {
5275 if (g_str_has_prefix(ms_text_format
, "text/x-msmsgsinvite"))
5277 gchar
*tmp
= sipmsg_find_part_of_header(ms_text_format
, "ms-body=", NULL
, NULL
);
5279 gchar
*body
= (gchar
*) purple_base64_decode(tmp
, NULL
);
5281 GSList
*parsed_body
= sipe_ft_parse_msg_body(body
);
5283 sipe_process_incoming_x_msmsgsinvite(sip
, msg
, parsed_body
);
5284 sipe_utils_nameval_free(parsed_body
);
5285 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5289 else if (g_str_has_prefix(ms_text_format
, "text/plain") || g_str_has_prefix(ms_text_format
, "text/html"))
5291 /* please do not optimize logic inside as this code may be re-enabled for other cases */
5292 gchar
*html
= get_html_message(ms_text_format
, NULL
);
5294 if (is_multiparty
) {
5295 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
5296 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5298 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
5301 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5309 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
5310 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5311 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5313 body
= g_strdup_printf(
5315 "o=- 0 0 IN IP4 %s\r\n"
5319 "m=%s %d sip sip:%s\r\n"
5320 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
5321 purple_network_get_my_ip(-1),
5322 purple_network_get_my_ip(-1),
5323 sip
->ocs2007
? "message" : "x-ms-message",
5326 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5330 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5334 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
5335 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5336 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5338 body
= g_strdup_printf(
5340 "o=- 0 0 IN IP4 0.0.0.0\r\n"
5342 "c=IN IP4 0.0.0.0\r\n"
5344 "m=%s %d sip sip:%s\r\n"
5345 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
5346 sip
->ocs2007
? "message" : "x-ms-message",
5349 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5354 sipe_get_auth_scheme_name(struct sipe_account_data
*sip
)
5356 const char *res
= "NTLM";
5357 #ifdef HAVE_KERBEROS
5358 if (purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5362 (void) sip
; /* make compiler happy */
5367 static void sipe_connection_cleanup(struct sipe_account_data
*);
5368 static void create_connection(struct sipe_account_data
*, gchar
*, int);
5370 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
5371 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5374 const gchar
*expires_header
;
5376 GSList
*hdr
= msg
->headers
;
5377 struct sipnameval
*elem
;
5379 expires_header
= sipmsg_find_header(msg
, "Expires");
5380 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
5381 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
5383 switch (msg
->response
) {
5386 sip
->registerstatus
= 0;
5388 const gchar
*contact_hdr
;
5393 const gchar
*server_hdr
= sipmsg_find_header(msg
, "Server");
5394 const char *auth_scheme
;
5396 if (!sip
->reregister_set
) {
5397 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
5398 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
5399 g_free(action_name
);
5400 sip
->reregister_set
= TRUE
;
5403 sip
->registerstatus
= 3;
5405 if (server_hdr
&& !sip
->server_version
) {
5406 sip
->server_version
= g_strdup(server_hdr
);
5411 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5412 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5415 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
5416 fill_auth(tmp
, &sip
->registrar
);
5419 if (!sip
->reauthenticate_set
) {
5420 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
5421 guint reauth_timeout
;
5422 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
5423 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
5424 reauth_timeout
= sip
->registrar
.expires
- 300;
5426 /* NTLM: we have to reauthenticate as our security token expires
5427 after eight hours (be five minutes early) */
5428 reauth_timeout
= (8 * 3600) - 300;
5430 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
5431 g_free(action_name
);
5432 sip
->reauthenticate_set
= TRUE
;
5435 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
5437 epid
= get_epid(sip
);
5438 uuid
= generateUUIDfromEPID(epid
);
5441 // There can be multiple Contact headers (one per location where the user is logged in) so
5442 // make sure to only get the one for this uuid
5443 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
5444 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
5445 if (valid_contact
) {
5446 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
5447 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
5448 g_free(valid_contact
);
5451 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
5456 g_free(sip
->contact
);
5458 sip
->contact
= g_strdup_printf("<%s>", gruu
);
5461 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
5462 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
);
5464 sip
->ocs2007
= FALSE
;
5465 sip
->batched_support
= FALSE
;
5470 if (sipe_strcase_equal(elem
->name
, "Supported")) {
5471 if (sipe_strcase_equal(elem
->value
, "msrtc-event-categories")) {
5472 /* We interpret this as OCS2007+ indicator */
5473 sip
->ocs2007
= TRUE
;
5474 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s (indicates OCS2007+)\n", elem
->value
);
5476 if (sipe_strcase_equal(elem
->value
, "adhoclist")) {
5477 sip
->batched_support
= TRUE
;
5478 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s\n", elem
->value
);
5481 if (sipe_strcase_equal(elem
->name
, "Allow-Events")){
5482 gchar
**caps
= g_strsplit(elem
->value
,",",0);
5485 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
5486 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
5491 hdr
= g_slist_next(hdr
);
5494 /* rejoin open chats to be able to use them by continue to send messages */
5495 purple_conversation_foreach(sipe_rejoin_chat
);
5498 if (!sip
->subscribed
) { //do it just once, not every re-register
5500 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
5501 (GCompareFunc
)g_ascii_strcasecmp
)) {
5502 sipe_subscribe_roaming_contacts(sip
);
5505 /* For 2007+ it does not make sence to subscribe to:
5506 * vnd-microsoft-roaming-ACL
5507 * vnd-microsoft-provisioning (not v2)
5509 * These are for backward compatibility.
5513 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
5514 (GCompareFunc
)g_ascii_strcasecmp
)) {
5515 sipe_subscribe_roaming_self(sip
);
5517 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
5518 (GCompareFunc
)g_ascii_strcasecmp
)) {
5519 sipe_subscribe_roaming_provisioning_v2(sip
);
5522 /* For 2005- servers */
5525 //sipe_options_request(sip, sip->sipdomain);
5527 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
5528 (GCompareFunc
)g_ascii_strcasecmp
)) {
5529 sipe_subscribe_roaming_acl(sip
);
5531 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
5532 (GCompareFunc
)g_ascii_strcasecmp
)) {
5533 sipe_subscribe_roaming_provisioning(sip
);
5535 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
5536 (GCompareFunc
)g_ascii_strcasecmp
)) {
5537 sipe_subscribe_presence_wpending(sip
, msg
);
5540 /* For 2007+ we publish our initial statuses and calendar data only after
5541 * received our existing publications in sipe_process_roaming_self()
5542 * Only in this case we know versions of current publications made
5545 /* For 2005- we publish our initial statuses only after
5546 * received our existing UserInfo data in response to
5547 * self subscription.
5548 * Only in this case we won't override existing UserInfo data
5549 * set earlier or by other client on our behalf.
5553 sip
->subscribed
= TRUE
;
5556 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
5557 "timeout=", ";", NULL
);
5558 if (timeout
!= NULL
) {
5559 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
5560 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
5561 sip
->keepalive_timeout
);
5565 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\n", sip
->cseq
);
5570 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
5572 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
5573 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
5577 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
5580 tmp
= g_strsplit(parts
[0], ":", 0);
5581 hostname
= g_strdup(tmp
[0]);
5582 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
5586 tmp
= g_strsplit(parts
[i
], "=", 0);
5588 if (g_strcasecmp("transport", tmp
[0]) == 0) {
5589 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
5590 transport
= SIPE_TRANSPORT_TCP
;
5591 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
5592 transport
= SIPE_TRANSPORT_UDP
;
5601 /* Close old connection */
5602 sipe_connection_cleanup(sip
);
5604 /* Create new connection */
5605 sip
->transport
= transport
;
5606 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
5607 hostname
, port
, TRANSPORT_DESCRIPTOR
);
5608 create_connection(sip
, hostname
, port
);
5614 if (sip
->registerstatus
!= 2) {
5615 const char *auth_scheme
;
5616 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
5617 if (sip
->registrar
.retries
> 3) {
5618 sip
->gc
->wants_to_die
= TRUE
;
5619 purple_connection_error(sip
->gc
, _("Authentication failed"));
5623 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5624 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5626 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
? tmp
: "");
5628 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
5629 sip
->gc
->wants_to_die
= TRUE
;
5630 purple_connection_error(sip
->gc
, tmp2
);
5634 fill_auth(tmp
, &sip
->registrar
);
5635 sip
->registerstatus
= 2;
5636 if (sip
->account
->disconnecting
) {
5637 do_register_exp(sip
, 0);
5645 const gchar
*diagnostics
= sipmsg_find_header(msg
, "Warning");
5646 gchar
**reason
= NULL
;
5648 if (diagnostics
!= NULL
) {
5650 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
5652 reason
= g_strsplit(diagnostics
, "\"", 0);
5654 warning
= g_strdup_printf(_("You have been rejected by the server: %s"),
5655 (reason
&& reason
[1]) ? reason
[1] : _("no reason given"));
5658 sip
->gc
->wants_to_die
= TRUE
;
5659 purple_connection_error(sip
->gc
, warning
);
5666 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
5667 gchar
*reason
= NULL
;
5669 if (diagnostics
!= NULL
) {
5670 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
5672 warning
= g_strdup_printf(_("Not found: %s. Please contact your Administrator"),
5673 diagnostics
? (reason
? reason
: _("no reason given")) :
5674 _("SIP is either not enabled for the destination URI or it does not exist"));
5677 sip
->gc
->wants_to_die
= TRUE
;
5678 purple_connection_error(sip
->gc
, warning
);
5684 case 504: /* Server time-out */
5686 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
5687 gchar
*reason
= NULL
;
5689 if (diagnostics
!= NULL
) {
5690 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
5692 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
5695 sip
->gc
->wants_to_die
= TRUE
;
5696 purple_connection_error(sip
->gc
, warning
);
5706 * Returns 2005-style activity and Availability.
5708 * @param status Sipe statis id.
5711 sipe_get_act_avail_by_status_2005(const char *status
,
5715 int avail
= 300; /* online */
5716 int act
= 400; /* Available */
5718 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
5720 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
5722 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
5724 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
5726 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
5728 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
5729 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
5731 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
5732 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
5733 avail
= 0; /* offline */
5736 act
= 400; /* Available */
5739 if (activity
) *activity
= act
;
5740 if (availability
) *availability
= avail
;
5746 * @param activity 2005 aggregated activity. Ex.: 600
5747 * @param availablity 2005 aggregated availablity. Ex.: 300
5750 sipe_get_status_by_act_avail_2005(const int activity
,
5751 const int availablity
,
5752 char **activity_desc
)
5754 const char *status_id
= NULL
;
5755 const char *act
= NULL
;
5757 if (activity
< 150) {
5758 status_id
= SIPE_STATUS_ID_AWAY
;
5759 } else if (activity
< 200) {
5760 //status_id = SIPE_STATUS_ID_LUNCH;
5761 status_id
= SIPE_STATUS_ID_AWAY
;
5762 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
5763 } else if (activity
< 300) {
5764 //status_id = SIPE_STATUS_ID_IDLE;
5765 status_id
= SIPE_STATUS_ID_AWAY
;
5766 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5767 } else if (activity
< 400) {
5768 status_id
= SIPE_STATUS_ID_BRB
;
5769 } else if (activity
< 500) {
5770 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5771 } else if (activity
< 600) {
5772 //status_id = SIPE_STATUS_ID_ON_PHONE;
5773 status_id
= SIPE_STATUS_ID_BUSY
;
5774 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
5775 } else if (activity
< 700) {
5776 status_id
= SIPE_STATUS_ID_BUSY
;
5777 } else if (activity
< 800) {
5778 status_id
= SIPE_STATUS_ID_AWAY
;
5780 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5783 if (availablity
< 100)
5784 status_id
= SIPE_STATUS_ID_OFFLINE
;
5786 if (activity_desc
&& act
) {
5787 g_free(*activity_desc
);
5788 *activity_desc
= g_strdup(act
);
5795 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
5798 sipe_get_status_by_availability(int avail
,
5799 char** activity_desc
)
5802 const char *act
= NULL
;
5805 status
= SIPE_STATUS_ID_OFFLINE
;
5806 } else if (avail
< 4500) {
5807 status
= SIPE_STATUS_ID_AVAILABLE
;
5808 } else if (avail
< 6000) {
5809 //status = SIPE_STATUS_ID_IDLE;
5810 status
= SIPE_STATUS_ID_AVAILABLE
;
5811 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5812 } else if (avail
< 7500) {
5813 status
= SIPE_STATUS_ID_BUSY
;
5814 } else if (avail
< 9000) {
5815 //status = SIPE_STATUS_ID_BUSYIDLE;
5816 status
= SIPE_STATUS_ID_BUSY
;
5817 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
5818 } else if (avail
< 12000) {
5819 status
= SIPE_STATUS_ID_DND
;
5820 } else if (avail
< 15000) {
5821 status
= SIPE_STATUS_ID_BRB
;
5822 } else if (avail
< 18000) {
5823 status
= SIPE_STATUS_ID_AWAY
;
5825 status
= SIPE_STATUS_ID_OFFLINE
;
5828 if (activity_desc
&& act
) {
5829 g_free(*activity_desc
);
5830 *activity_desc
= g_strdup(act
);
5837 * Returns 2007-style availability value
5839 * @param sipe_status_id (in)
5840 * @param activity_token (out) Must be g_free()'d after use if consumed.
5843 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
5846 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
5848 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
5849 availability
= 15500;
5850 if (!activity_token
|| !(*activity_token
)) {
5851 activity
= SIPE_ACTIVITY_AWAY
;
5853 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
5854 availability
= 12500;
5855 activity
= SIPE_ACTIVITY_BRB
;
5856 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
5857 availability
= 9500;
5858 activity
= SIPE_ACTIVITY_DND
;
5859 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
5860 availability
= 6500;
5861 if (!activity_token
|| !(*activity_token
)) {
5862 activity
= SIPE_ACTIVITY_BUSY
;
5864 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
5865 availability
= 3500;
5866 activity
= SIPE_ACTIVITY_ONLINE
;
5867 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
5870 // Offline or invisible
5871 availability
= 18500;
5872 activity
= SIPE_ACTIVITY_OFFLINE
;
5875 if (activity_token
) {
5876 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
5878 return availability
;
5881 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5884 sipe_xml
*xn_categories
;
5885 const sipe_xml
*xn_category
;
5886 const char *status
= NULL
;
5887 gboolean do_update_status
= FALSE
;
5888 gboolean has_note_cleaned
= FALSE
;
5889 gboolean has_free_busy_cleaned
= FALSE
;
5891 xn_categories
= sipe_xml_parse(data
, len
);
5892 uri
= sipe_xml_attribute(xn_categories
, "uri"); /* with 'sip:' prefix */
5894 for (xn_category
= sipe_xml_child(xn_categories
, "category");
5896 xn_category
= sipe_xml_twin(xn_category
) )
5898 const sipe_xml
*xn_node
;
5900 const char *attrVar
= sipe_xml_attribute(xn_category
, "name");
5901 time_t publish_time
= (tmp
= sipe_xml_attribute(xn_category
, "publishTime")) ?
5902 sipe_utils_str_to_time(tmp
) : 0;
5905 if (sipe_strequal(attrVar
, "contactCard"))
5907 const sipe_xml
*card
= sipe_xml_child(xn_category
, "contactCard");
5910 const sipe_xml
*node
;
5911 /* identity - Display Name and email */
5912 node
= sipe_xml_child(card
, "identity");
5914 char* display_name
= sipe_xml_data(
5915 sipe_xml_child(node
, "name/displayName"));
5916 char* email
= sipe_xml_data(
5917 sipe_xml_child(node
, "email"));
5919 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5920 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
5922 g_free(display_name
);
5926 node
= sipe_xml_child(card
, "company");
5928 char* company
= sipe_xml_data(node
);
5929 sipe_update_user_info(sip
, uri
, COMPANY_PROP
, company
);
5933 node
= sipe_xml_child(card
, "department");
5935 char* department
= sipe_xml_data(node
);
5936 sipe_update_user_info(sip
, uri
, DEPARTMENT_PROP
, department
);
5940 node
= sipe_xml_child(card
, "title");
5942 char* title
= sipe_xml_data(node
);
5943 sipe_update_user_info(sip
, uri
, TITLE_PROP
, title
);
5947 node
= sipe_xml_child(card
, "office");
5949 char* office
= sipe_xml_data(node
);
5950 sipe_update_user_info(sip
, uri
, OFFICE_PROP
, office
);
5954 node
= sipe_xml_child(card
, "url");
5956 char* site
= sipe_xml_data(node
);
5957 sipe_update_user_info(sip
, uri
, SITE_PROP
, site
);
5961 for (node
= sipe_xml_child(card
, "phone");
5963 node
= sipe_xml_twin(node
))
5965 const char *phone_type
= sipe_xml_attribute(node
, "type");
5966 char* phone
= sipe_xml_data(sipe_xml_child(node
, "uri"));
5967 char* phone_display_string
= sipe_xml_data(sipe_xml_child(node
, "displayString"));
5969 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, phone_display_string
);
5972 g_free(phone_display_string
);
5975 for (node
= sipe_xml_child(card
, "address");
5977 node
= sipe_xml_twin(node
))
5979 if (sipe_strequal(sipe_xml_attribute(node
, "type"), "work")) {
5980 char* street
= sipe_xml_data(sipe_xml_child(node
, "street"));
5981 char* city
= sipe_xml_data(sipe_xml_child(node
, "city"));
5982 char* state
= sipe_xml_data(sipe_xml_child(node
, "state"));
5983 char* zipcode
= sipe_xml_data(sipe_xml_child(node
, "zipcode"));
5984 char* country_code
= sipe_xml_data(sipe_xml_child(node
, "countryCode"));
5986 sipe_update_user_info(sip
, uri
, ADDRESS_STREET_PROP
, street
);
5987 sipe_update_user_info(sip
, uri
, ADDRESS_CITY_PROP
, city
);
5988 sipe_update_user_info(sip
, uri
, ADDRESS_STATE_PROP
, state
);
5989 sipe_update_user_info(sip
, uri
, ADDRESS_ZIPCODE_PROP
, zipcode
);
5990 sipe_update_user_info(sip
, uri
, ADDRESS_COUNTRYCODE_PROP
, country_code
);
5996 g_free(country_code
);
6004 else if (sipe_strequal(attrVar
, "note"))
6007 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
6009 if (!has_note_cleaned
) {
6010 has_note_cleaned
= TRUE
;
6012 g_free(sbuddy
->note
);
6013 sbuddy
->note
= NULL
;
6014 sbuddy
->is_oof_note
= FALSE
;
6015 sbuddy
->note_since
= publish_time
;
6017 do_update_status
= TRUE
;
6019 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
6020 /* clean up in case no 'note' element is supplied
6021 * which indicate note removal in client
6023 g_free(sbuddy
->note
);
6024 sbuddy
->note
= NULL
;
6025 sbuddy
->is_oof_note
= FALSE
;
6026 sbuddy
->note_since
= publish_time
;
6028 xn_node
= sipe_xml_child(xn_category
, "note/body");
6031 sbuddy
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_node
)), -1);
6033 sbuddy
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_node
, "type"), "OOF");
6034 sbuddy
->note_since
= publish_time
;
6036 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s), note(%s)\n",
6037 uri
, sbuddy
->note
? sbuddy
->note
: "");
6039 /* to trigger UI refresh in case no status info is supplied in this update */
6040 do_update_status
= TRUE
;
6045 else if(sipe_strequal(attrVar
, "state"))
6049 const sipe_xml
*xn_availability
;
6050 const sipe_xml
*xn_activity
;
6051 const sipe_xml
*xn_meeting_subject
;
6052 const sipe_xml
*xn_meeting_location
;
6053 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
6055 xn_node
= sipe_xml_child(xn_category
, "state");
6056 if (!xn_node
) continue;
6057 xn_availability
= sipe_xml_child(xn_node
, "availability");
6058 if (!xn_availability
) continue;
6059 xn_activity
= sipe_xml_child(xn_node
, "activity");
6060 xn_meeting_subject
= sipe_xml_child(xn_node
, "meetingSubject");
6061 xn_meeting_location
= sipe_xml_child(xn_node
, "meetingLocation");
6063 tmp
= sipe_xml_data(xn_availability
);
6064 availability
= atoi(tmp
);
6067 /* activity, meeting_subject, meeting_location */
6072 g_free(sbuddy
->activity
);
6073 sbuddy
->activity
= NULL
;
6075 const char *token
= sipe_xml_attribute(xn_activity
, "token");
6076 const sipe_xml
*xn_custom
= sipe_xml_child(xn_activity
, "custom");
6079 if (!is_empty(token
)) {
6080 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
6082 /* from custom element */
6084 char *custom
= sipe_xml_data(xn_custom
);
6086 if (!is_empty(custom
)) {
6087 sbuddy
->activity
= custom
;
6093 /* meeting_subject */
6094 g_free(sbuddy
->meeting_subject
);
6095 sbuddy
->meeting_subject
= NULL
;
6096 if (xn_meeting_subject
) {
6097 char *meeting_subject
= sipe_xml_data(xn_meeting_subject
);
6099 if (!is_empty(meeting_subject
)) {
6100 sbuddy
->meeting_subject
= meeting_subject
;
6101 meeting_subject
= NULL
;
6103 g_free(meeting_subject
);
6105 /* meeting_location */
6106 g_free(sbuddy
->meeting_location
);
6107 sbuddy
->meeting_location
= NULL
;
6108 if (xn_meeting_location
) {
6109 char *meeting_location
= sipe_xml_data(xn_meeting_location
);
6111 if (!is_empty(meeting_location
)) {
6112 sbuddy
->meeting_location
= meeting_location
;
6113 meeting_location
= NULL
;
6115 g_free(meeting_location
);
6118 status
= sipe_get_status_by_availability(availability
, &tmp
);
6119 if (sbuddy
->activity
&& tmp
) {
6120 char *tmp2
= sbuddy
->activity
;
6122 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
6126 sbuddy
->activity
= tmp
;
6130 do_update_status
= TRUE
;
6133 else if(sipe_strequal(attrVar
, "calendarData"))
6135 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
6136 const sipe_xml
*xn_free_busy
= sipe_xml_child(xn_category
, "calendarData/freeBusy");
6137 const sipe_xml
*xn_working_hours
= sipe_xml_child(xn_category
, "calendarData/WorkingHours");
6139 if (sbuddy
&& xn_free_busy
) {
6140 if (!has_free_busy_cleaned
) {
6141 has_free_busy_cleaned
= TRUE
;
6143 g_free(sbuddy
->cal_start_time
);
6144 sbuddy
->cal_start_time
= NULL
;
6146 g_free(sbuddy
->cal_free_busy_base64
);
6147 sbuddy
->cal_free_busy_base64
= NULL
;
6149 g_free(sbuddy
->cal_free_busy
);
6150 sbuddy
->cal_free_busy
= NULL
;
6152 sbuddy
->cal_free_busy_published
= publish_time
;
6155 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
6156 g_free(sbuddy
->cal_start_time
);
6157 sbuddy
->cal_start_time
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
6159 sbuddy
->cal_granularity
= sipe_strcase_equal(sipe_xml_attribute(xn_free_busy
, "granularity"), "PT15M") ?
6162 g_free(sbuddy
->cal_free_busy_base64
);
6163 sbuddy
->cal_free_busy_base64
= sipe_xml_data(xn_free_busy
);
6165 g_free(sbuddy
->cal_free_busy
);
6166 sbuddy
->cal_free_busy
= NULL
;
6168 sbuddy
->cal_free_busy_published
= publish_time
;
6170 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
);
6174 if (sbuddy
&& xn_working_hours
) {
6175 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
6180 if (do_update_status
) {
6181 if (!status
) { /* no status category in this update, using contact's current status */
6182 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
6183 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
6184 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
6185 status
= purple_status_get_id(pstatus
);
6188 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", status
);
6189 sipe_got_user_status(sip
, uri
, status
);
6192 sipe_xml_free(xn_categories
);
6195 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
6197 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6198 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
6199 payload
->host
= g_strdup(host
);
6200 payload
->buddies
= server
;
6201 sipe_subscribe_presence_batched_routed(sip
, payload
);
6202 sipe_subscribe_presence_batched_routed_free(payload
);
6205 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6208 xmlnode
*xn_resource
;
6209 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
6214 xn_list
= xmlnode_from_str(data
, len
);
6216 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
6218 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
6220 const char *uri
, *state
;
6221 xmlnode
*xn_instance
;
6223 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
6224 if (!xn_instance
) continue;
6226 uri
= xmlnode_get_attrib(xn_resource
, "uri");
6227 state
= xmlnode_get_attrib(xn_instance
, "state");
6228 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
6230 if (strstr(state
, "resubscribe")) {
6231 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
6233 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
6234 gchar
*user
= g_strdup(uri
);
6235 host
= g_strdup(poolFqdn
);
6236 server
= g_hash_table_lookup(servers
, host
);
6237 server
= g_slist_append(server
, user
);
6238 g_hash_table_insert(servers
, host
, server
);
6240 sipe_subscribe_presence_single(sip
, (void *) uri
);
6245 /* Send out any deferred poolFqdn subscriptions */
6246 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
6247 g_hash_table_destroy(servers
);
6249 xmlnode_free(xn_list
);
6252 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6256 gchar
*activity
= NULL
;
6258 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
6259 gboolean isonline
= FALSE
;
6260 xmlnode
*display_name_node
;
6262 pidf
= xmlnode_from_str(data
, len
);
6264 purple_debug_info("sipe", "process_incoming_notify_pidf: no parseable pidf:%s\n",data
);
6268 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
6270 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6271 basicstatus
= xmlnode_get_child(status
, "basic");
6276 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic found\n");
6281 getbasic
= xmlnode_get_data(basicstatus
);
6283 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic data found\n");
6288 purple_debug_info("sipe", "process_incoming_notify_pidf: basic-status(%s)\n", getbasic
);
6289 if (strstr(getbasic
, "open")) {
6294 uri
= sip_uri(xmlnode_get_attrib(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
6296 display_name_node
= xmlnode_get_child(pidf
, "display-name");
6297 if (display_name_node
) {
6298 char * display_name
= xmlnode_get_data(display_name_node
);
6300 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6301 g_free(display_name
);
6304 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
6305 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6306 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
6307 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
6308 activity
= xmlnode_get_data(basicstatus
);
6309 purple_debug_info("sipe", "process_incoming_notify_pidf: activity(%s)\n", activity
);
6316 const gchar
* status_id
= NULL
;
6318 if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
6319 status_id
= SIPE_STATUS_ID_BUSY
;
6320 } else if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
6321 status_id
= SIPE_STATUS_ID_AWAY
;
6326 status_id
= SIPE_STATUS_ID_AVAILABLE
;
6329 purple_debug_info("sipe", "process_incoming_notify_pidf: status_id(%s)\n", status_id
);
6330 sipe_got_user_status(sip
, uri
, status_id
);
6332 sipe_got_user_status(sip
, uri
, SIPE_STATUS_ID_OFFLINE
);
6342 sipe_user_info_has_updated(struct sipe_account_data
*sip
,
6343 xmlnode
*xn_userinfo
)
6347 g_free(sip
->user_states
);
6348 sip
->user_states
= NULL
;
6349 if ((xn_states
= xmlnode_get_child(xn_userinfo
, "states")) != NULL
) {
6350 sip
->user_states
= xmlnode_to_str(xn_states
, NULL
);
6351 /* this is a hack-around to remove added newline after inner element,
6352 * state in this case, where it shouldn't be.
6353 * After several use of xmlnode_to_str, amount of added newlines
6354 * grows significantly.
6356 purple_str_strip_char(sip
->user_states
, '\n');
6357 //purple_str_strip_char(sip->user_states, '\r');
6360 /* Publish initial state if not yet.
6361 * Assuming this happens on initial responce to self subscription
6362 * so we've already updated our UserInfo.
6364 if (!sip
->initial_state_published
) {
6365 send_presence_soap(sip
, FALSE
);
6367 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
6371 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6373 char *activity
= NULL
;
6375 const char *status_id
= NULL
;
6378 char *self_uri
= sip_uri_self(sip
);
6381 const char *device_name
= NULL
;
6382 const char *cal_start_time
= NULL
;
6383 const char *cal_granularity
= NULL
;
6384 char *cal_free_busy_base64
= NULL
;
6385 struct sipe_buddy
*sbuddy
;
6387 xmlnode
*xn_presentity
;
6388 xmlnode
*xn_availability
;
6389 xmlnode
*xn_activity
;
6390 xmlnode
*xn_display_name
;
6392 xmlnode
*xn_phone_number
;
6393 xmlnode
*xn_userinfo
;
6397 xmlnode
*xn_contact
;
6399 char *free_activity
;
6401 const char *user_avail_nil
;
6403 time_t user_avail_since
= 0;
6404 time_t activity_since
= 0;
6406 /* fix for Reuters environment on Linux */
6407 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
6409 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
6410 xn_presentity
= xmlnode_from_str(tmp_data
, strlen(tmp_data
));
6413 xn_presentity
= xmlnode_from_str(data
, len
);
6416 xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
6417 xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
6418 xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
6419 xn_email
= xmlnode_get_child(xn_presentity
, "email");
6420 xn_phone_number
= xmlnode_get_child(xn_presentity
, "phoneNumber");
6421 xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
6422 xn_oof
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "oof") : NULL
;
6423 xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
): NULL
;
6424 user_avail
= xn_state
? xmlnode_get_int_attrib(xn_state
, "avail", 0) : 0;
6425 user_avail_since
= xn_state
? sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since")) : 0;
6426 user_avail_nil
= xn_state
? xmlnode_get_attrib(xn_state
, "nil") : NULL
;
6427 xn_contact
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "contact") : NULL
;
6428 xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
6429 note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
6431 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
6433 user_avail_since
= 0;
6436 free_activity
= NULL
;
6438 name
= xmlnode_get_attrib(xn_presentity
, "uri"); /* without 'sip:' prefix */
6439 uri
= sip_uri_from_name(name
);
6440 avl
= xmlnode_get_int_attrib(xn_availability
, "aggregate", 0);
6441 epid
= xmlnode_get_attrib(xn_availability
, "epid");
6442 act
= xmlnode_get_int_attrib(xn_activity
, "aggregate", 0);
6444 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
6445 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
6446 if (user_avail
> res_avail
) {
6447 res_avail
= user_avail
;
6448 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
6451 if (xn_display_name
) {
6452 char *display_name
= g_strdup(xmlnode_get_attrib(xn_display_name
, "displayName"));
6453 char *email
= xn_email
? g_strdup(xmlnode_get_attrib(xn_email
, "email")) : NULL
;
6454 char *phone_label
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "label")) : NULL
;
6455 char *phone_number
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "number")) : NULL
;
6456 char *tel_uri
= sip_to_tel_uri(phone_number
);
6458 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6459 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
6460 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
6461 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, !is_empty(phone_label
) ? phone_label
: phone_number
);
6464 g_free(phone_label
);
6465 g_free(phone_number
);
6467 g_free(display_name
);
6472 for (node
= xmlnode_get_child(xn_contact
, "tel"); node
; node
= xmlnode_get_next_twin(node
))
6474 /* Ex.: <tel type="work">tel:+3222220000</tel> */
6475 const char *phone_type
= xmlnode_get_attrib(node
, "type");
6476 char* phone
= xmlnode_get_data(node
);
6478 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, NULL
);
6484 /* devicePresence */
6485 for (node
= xmlnode_get_descendant(xn_presentity
, "devices", "devicePresence", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
6486 xmlnode
*xn_device_name
;
6487 xmlnode
*xn_calendar_info
;
6492 if (sipe_strequal(xmlnode_get_attrib(node
, "epid"), epid
)) {
6493 xn_device_name
= xmlnode_get_child(node
, "deviceName");
6494 device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
6498 xn_calendar_info
= xmlnode_get_child(node
, "calendarInfo");
6499 if (xn_calendar_info
) {
6500 const char *cal_start_time_tmp
= xmlnode_get_attrib(xn_calendar_info
, "startTime");
6502 if (cal_start_time
) {
6503 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
6504 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
6506 if (cal_start_time_t_tmp
> cal_start_time_t
) {
6507 cal_start_time
= cal_start_time_tmp
;
6508 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6509 g_free(cal_free_busy_base64
);
6510 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6512 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
);
6515 cal_start_time
= cal_start_time_tmp
;
6516 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6517 g_free(cal_free_busy_base64
);
6518 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6520 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
);
6525 xn_state
= xmlnode_get_descendant(node
, "states", "state", NULL
);
6527 int dev_avail
= xmlnode_get_int_attrib(xn_state
, "avail", 0);
6528 time_t dev_avail_since
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since"));
6530 state
= xmlnode_get_data(xn_state
);
6531 if (dev_avail_since
> user_avail_since
&&
6532 dev_avail
>= res_avail
)
6534 res_avail
= dev_avail
;
6535 if (!is_empty(state
))
6537 if (sipe_strequal(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
6539 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
6540 } else if (sipe_strequal(state
, "presenting")) {
6542 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
6547 activity_since
= dev_avail_since
;
6549 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
6556 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
6558 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
6562 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
6565 g_free(sbuddy
->activity
);
6566 sbuddy
->activity
= activity
;
6569 sbuddy
->activity_since
= activity_since
;
6571 sbuddy
->user_avail
= user_avail
;
6572 sbuddy
->user_avail_since
= user_avail_since
;
6574 g_free(sbuddy
->note
);
6575 sbuddy
->note
= NULL
;
6576 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
6578 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
6580 g_free(sbuddy
->device_name
);
6581 sbuddy
->device_name
= NULL
;
6582 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
6584 if (!is_empty(cal_free_busy_base64
)) {
6585 g_free(sbuddy
->cal_start_time
);
6586 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
6588 sbuddy
->cal_granularity
= sipe_strcase_equal(cal_granularity
, "PT15M") ? 15 : 0;
6590 g_free(sbuddy
->cal_free_busy_base64
);
6591 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
6592 cal_free_busy_base64
= NULL
;
6594 g_free(sbuddy
->cal_free_busy
);
6595 sbuddy
->cal_free_busy
= NULL
;
6598 sbuddy
->last_non_cal_status_id
= status_id
;
6599 g_free(sbuddy
->last_non_cal_activity
);
6600 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
6602 if (sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
6603 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
6605 sip
->is_oof_note
= sbuddy
->is_oof_note
;
6608 sip
->note
= g_strdup(sbuddy
->note
);
6610 sip
->note_since
= time(NULL
);
6613 g_free(sip
->status
);
6614 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
6617 g_free(cal_free_busy_base64
);
6620 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", status_id
);
6621 sipe_got_user_status(sip
, uri
, status_id
);
6623 if (!sip
->ocs2007
&& sipe_strcase_equal(self_uri
, uri
)) {
6624 sipe_user_info_has_updated(sip
, xn_userinfo
);
6628 xmlnode_free(xn_presentity
);
6633 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
6635 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6637 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
6639 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
6640 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
6642 const char *content
= msg
->body
;
6643 unsigned length
= msg
->bodylen
;
6644 PurpleMimeDocument
*mime
= NULL
;
6646 if (strstr(ctype
, "multipart"))
6648 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6649 const char *content_type
;
6651 mime
= purple_mime_document_parse(doc
);
6652 parts
= purple_mime_document_get_parts(mime
);
6654 content
= purple_mime_part_get_data(parts
->data
);
6655 length
= purple_mime_part_get_length(parts
->data
);
6656 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
6657 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
6659 process_incoming_notify_rlmi_resub(sip
, content
, length
);
6661 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
6663 process_incoming_notify_msrtc(sip
, content
, length
);
6667 process_incoming_notify_rlmi(sip
, content
, length
);
6669 parts
= parts
->next
;
6675 purple_mime_document_free(mime
);
6678 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
6680 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
6682 else if(strstr(ctype
, "application/rlmi+xml"))
6684 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
6687 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
6689 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
6693 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
6697 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
6699 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6700 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6702 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
6705 strstr(ctype
, "multipart") &&
6706 (strstr(ctype
, "application/rlmi+xml") ||
6707 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
6708 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6709 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
6710 GList
*parts
= purple_mime_document_get_parts(mime
);
6711 GSList
*buddies
= NULL
;
6712 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6715 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
6716 purple_mime_part_get_length(parts
->data
));
6718 if (xml
&& !sipe_strequal(xml
->name
, "list")) {
6719 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
6721 buddies
= g_slist_append(buddies
, uri
);
6725 parts
= parts
->next
;
6728 if (mime
) purple_mime_document_free(mime
);
6730 payload
->host
= g_strdup(who
);
6731 payload
->buddies
= buddies
;
6732 sipe_schedule_action(action_name
, timeout
,
6733 sipe_subscribe_presence_batched_routed
,
6734 sipe_subscribe_presence_batched_routed_free
,
6736 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
6739 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6740 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
6742 g_free(action_name
);
6746 * Dispatcher for all incoming subscription information
6747 * whether it comes from NOTIFY, BENOTIFY requests or
6748 * piggy-backed to subscription's OK responce.
6750 * @param request whether initiated from BE/NOTIFY request or OK-response message.
6751 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
6753 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
6755 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
6756 const gchar
*event
= sipmsg_find_header(msg
, "Event");
6757 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
6760 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n",
6762 tmp
= fix_newlines(msg
->body
));
6764 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n", subscription_state
? subscription_state
: "");
6766 /* implicit subscriptions */
6767 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
6768 sipe_process_imdn(sip
, msg
);
6772 /* for one off subscriptions (send with Expire: 0) */
6773 if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning-v2"))
6775 sipe_process_provisioning_v2(sip
, msg
);
6777 else if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning"))
6779 sipe_process_provisioning(sip
, msg
);
6781 else if (sipe_strcase_equal(event
, "presence"))
6783 sipe_process_presence(sip
, msg
);
6785 else if (sipe_strcase_equal(event
, "registration-notify"))
6787 sipe_process_registration_notify(sip
, msg
);
6790 if (!subscription_state
|| strstr(subscription_state
, "active"))
6792 if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-contacts"))
6794 sipe_process_roaming_contacts(sip
, msg
);
6796 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-self"))
6798 sipe_process_roaming_self(sip
, msg
);
6800 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-ACL"))
6802 sipe_process_roaming_acl(sip
, msg
);
6804 else if (sipe_strcase_equal(event
, "presence.wpending"))
6806 sipe_process_presence_wpending(sip
, msg
);
6808 else if (sipe_strcase_equal(event
, "conference"))
6810 sipe_process_conference(sip
, msg
);
6815 /* The server sends status 'terminated' */
6816 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
6817 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
6818 gchar
*key
= sipe_get_subscription_key(event
, who
);
6820 purple_debug_info("sipe", "process_incoming_notify: server says that subscription to %s was terminated.\n", who
);
6823 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
6824 g_hash_table_remove(sip
->subscriptions
, key
);
6825 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
6831 if (!request
&& event
) {
6832 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
6833 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
6834 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n", timeout
);
6837 /* 2 min ahead of expiration */
6838 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
6840 if (sipe_strcase_equal(event
, "presence.wpending") &&
6841 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
6843 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
6844 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
6845 g_free(action_name
);
6847 else if (sipe_strcase_equal(event
, "presence") &&
6848 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
6850 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
6851 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6853 if (sip
->batched_support
) {
6854 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
6857 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6858 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
, timeout
);
6860 g_free(action_name
);
6866 /* The client responses on received a NOTIFY message */
6867 if (request
&& !benotify
)
6869 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
6874 * Whether user manually changed status or
6875 * it was changed automatically due to user
6876 * became inactive/active again
6879 sipe_is_user_state(struct sipe_account_data
*sip
)
6882 time_t now
= time(NULL
);
6884 purple_debug_info("sipe", "sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
6885 purple_debug_info("sipe", "sipe_is_user_state: now : %s", asctime(localtime(&now
)));
6887 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
6889 purple_debug_info("sipe", "sipe_is_user_state: res = %s\n", res
? "USER" : "MACHINE");
6894 send_presence_soap0(struct sipe_account_data
*sip
,
6895 gboolean do_publish_calendar
,
6896 gboolean do_reset_status
)
6898 struct sipe_ews
* ews
= sip
->ews
;
6899 int availability
= 0;
6904 gchar
*res_note
= NULL
;
6905 gchar
*res_oof
= NULL
;
6906 const gchar
*note_pub
= NULL
;
6907 gchar
*states
= NULL
;
6908 gchar
*calendar_data
= NULL
;
6909 gchar
*epid
= get_epid(sip
);
6910 time_t now
= time(NULL
);
6911 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
6912 const gchar
*oof_note
= ews
? sipe_ews_get_oof_note(ews
) : NULL
;
6913 const char *user_input
;
6914 gboolean pub_oof
= ews
&& oof_note
&& (!sip
->note
|| ews
->updated
> sip
->note_since
);
6916 if (oof_note
&& sip
->note
) {
6917 purple_debug_info("sipe", "ews->oof_start : %s", asctime(localtime(&(ews
->oof_start
))));
6918 purple_debug_info("sipe", "sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
6921 purple_debug_info("sipe", "sip->note : %s", sip
->note
? sip
->note
: "");
6923 if (!sip
->initial_state_published
||
6926 g_free(sip
->status
);
6927 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
6930 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
6934 note_pub
= oof_note
;
6935 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
6936 ews
->published
= TRUE
;
6937 } else if (sip
->note
) {
6938 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in ews already */
6941 sip
->is_oof_note
= FALSE
;
6942 sip
->note_since
= 0;
6944 note_pub
= sip
->note
;
6945 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
6951 /* to protocol internal plain text format */
6952 tmp
= purple_markup_strip_html(note_pub
);
6953 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
6958 if (!do_reset_status
) {
6959 if (sipe_is_user_state(sip
) && !do_publish_calendar
&& sip
->initial_state_published
)
6961 gchar
*activity_token
= NULL
;
6962 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
6964 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
6969 g_free(activity_token
);
6971 else /* preserve existing publication */
6973 if (sip
->user_states
) {
6974 states
= g_strdup(sip
->user_states
);
6978 /* do nothing - then User state will be erased */
6980 sip
->initial_state_published
= TRUE
;
6983 if (ews
&& (!is_empty(ews
->legacy_dn
) || !is_empty(ews
->email
)) && ews
->fb_start
&& !is_empty(ews
->free_busy
))
6985 char *fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
6986 char *free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
6987 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
6988 !is_empty(ews
->legacy_dn
) ? ews
->legacy_dn
: ews
->email
,
6991 g_free(fb_start_str
);
6992 g_free(free_busy_base64
);
6995 user_input
= !sipe_is_user_state(sip
) && sip
->status
!= SIPE_STATUS_ID_AVAILABLE
? "idle" : "active";
6997 /* forming resulting XML */
6998 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
7002 (tmp
= g_ascii_strup(sipe_get_host_name(), -1)),
7003 res_note
? res_note
: "",
7004 res_oof
? res_oof
: "",
7005 states
? states
: "",
7006 calendar_data
? calendar_data
: "",
7015 g_free(calendar_data
);
7017 send_soap_request(sip
, body
);
7020 g_free(since_time_str
);
7025 send_presence_soap(struct sipe_account_data
*sip
,
7026 gboolean do_publish_calendar
)
7028 return send_presence_soap0(sip
, do_publish_calendar
, FALSE
);
7033 process_send_presence_category_publish_response(struct sipe_account_data
*sip
,
7035 struct transaction
*trans
)
7037 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
7039 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
7045 gboolean has_device_publication
= FALSE
;
7047 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
7049 /* test if version mismatch fault */
7050 fault_code
= xmlnode_get_data(xmlnode_get_child(xml
, "Faultcode"));
7051 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
7052 purple_debug_info("sipe", "process_send_presence_category_publish_response: unsupported fault code:%s returning.\n", fault_code
);
7059 /* accumulating information about faulty versions */
7060 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
7061 for (node
= xmlnode_get_descendant(xml
, "details", "operation", NULL
);
7063 node
= xmlnode_get_next_twin(node
))
7065 const gchar
*index
= xmlnode_get_attrib(node
, "index");
7066 const gchar
*curVersion
= xmlnode_get_attrib(node
, "curVersion");
7068 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
7069 purple_debug_info("sipe", "fault added: index:%s curVersion:%s\n", index
, curVersion
);
7073 /* here we are parsing own request to figure out what publication
7074 * referensed here only by index went wrong
7076 xml
= xmlnode_from_str(trans
->msg
->body
, trans
->msg
->bodylen
);
7079 for (node
= xmlnode_get_descendant(xml
, "publications", "publication", NULL
),
7080 index_our
= 1; /* starts with 1 - our first publication */
7082 node
= xmlnode_get_next_twin(node
), index_our
++)
7084 gchar
*idx
= g_strdup_printf("%d", index_our
);
7085 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
7086 const gchar
*categoryName
= xmlnode_get_attrib(node
, "categoryName");
7089 if (sipe_strequal("device", categoryName
)) {
7090 has_device_publication
= TRUE
;
7093 if (curVersion
) { /* fault exist on this index */
7094 const gchar
*container
= xmlnode_get_attrib(node
, "container");
7095 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
7096 /* key is <category><instance><container> */
7097 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
7098 struct sipe_publication
*publication
=
7099 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, categoryName
), key
);
7101 purple_debug_info("sipe", "key is %s\n", key
);
7104 purple_debug_info("sipe", "Updating %s with version %s. Was %d before.\n",
7105 key
, curVersion
, publication
->version
);
7106 /* updating publication's version to the correct one */
7107 publication
->version
= atoi(curVersion
);
7113 g_hash_table_destroy(faults
);
7115 /* rebublishing with right versions */
7116 if (has_device_publication
) {
7117 send_publish_category_initial(sip
);
7119 send_presence_status(sip
);
7126 * Returns 'device' XML part for publication.
7127 * Must be g_free'd after use.
7130 sipe_publish_get_category_device(struct sipe_account_data
*sip
)
7134 gchar
*epid
= get_epid(sip
);
7135 gchar
*uuid
= generateUUIDfromEPID(epid
);
7136 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
7137 /* key is <category><instance><container> */
7138 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
7139 struct sipe_publication
*publication
=
7140 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
7145 uri
= sip_uri_self(sip
);
7146 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
7148 publication
? publication
->version
: 0,
7151 "00:00:00+01:00", /* @TODO make timezone real*/
7152 sipe_get_host_name()
7162 * A service method - use
7163 * - send_publish_get_category_state_machine and
7164 * - send_publish_get_category_state_user instead.
7165 * Must be g_free'd after use.
7168 sipe_publish_get_category_state(struct sipe_account_data
*sip
,
7169 gboolean is_user_state
)
7171 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
7172 guint instance
= is_user_state
? sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
) :
7173 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
7174 /* key is <category><instance><container> */
7175 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7176 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7177 struct sipe_publication
*publication_2
=
7178 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7179 struct sipe_publication
*publication_3
=
7180 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7185 if (publication_2
&& (publication_2
->availability
== availability
))
7187 purple_debug_info("sipe", "sipe_publish_get_category_state: state has NOT changed. Exiting.\n");
7188 return NULL
; /* nothing to update */
7191 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
7193 publication_2
? publication_2
->version
: 0,
7196 publication_3
? publication_3
->version
: 0,
7201 * Only Busy and OOF calendar event are published.
7202 * Different instances are used for that.
7204 * Must be g_free'd after use.
7207 sipe_publish_get_category_state_calendar(struct sipe_account_data
*sip
,
7208 struct sipe_cal_event
*event
,
7212 gchar
*start_time_str
;
7213 int availability
= 0;
7216 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
7217 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
) :
7218 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
7220 /* key is <category><instance><container> */
7221 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7222 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7223 struct sipe_publication
*publication_2
=
7224 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7225 struct sipe_publication
*publication_3
=
7226 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7231 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
7232 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7233 "Exiting as no publication and no event for cal_satus:%d\n", cal_satus
);
7239 (publication_3
->availability
== availability
) &&
7240 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
7243 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7244 "cal state has NOT changed for cal_satus:%d. Exiting.\n", cal_satus
);
7245 return NULL
; /* nothing to update */
7250 (event
->cal_status
== SIPE_CAL_BUSY
||
7251 event
->cal_status
== SIPE_CAL_OOF
))
7253 gchar
*availability_xml_str
= NULL
;
7254 gchar
*activity_xml_str
= NULL
;
7256 if (event
->cal_status
== SIPE_CAL_BUSY
) {
7257 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
7260 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
7261 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7262 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
7263 "minAvailability=\"6500\"",
7264 "maxAvailability=\"8999\"");
7265 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
7266 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7267 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
7268 "minAvailability=\"12000\"",
7271 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
7273 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
7275 publication_2
? publication_2
->version
: 0,
7278 availability_xml_str
? availability_xml_str
: "",
7279 activity_xml_str
? activity_xml_str
: "",
7280 event
->subject
? event
->subject
: "",
7281 event
->location
? event
->location
: "",
7284 publication_3
? publication_3
->version
: 0,
7287 availability_xml_str
? availability_xml_str
: "",
7288 activity_xml_str
? activity_xml_str
: "",
7289 event
->subject
? event
->subject
: "",
7290 event
->location
? event
->location
: ""
7292 g_free(start_time_str
);
7293 g_free(availability_xml_str
);
7294 g_free(activity_xml_str
);
7297 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
7299 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
7301 publication_2
? publication_2
->version
: 0,
7304 publication_3
? publication_3
->version
: 0
7312 * Returns 'machineState' XML part for publication.
7313 * Must be g_free'd after use.
7316 sipe_publish_get_category_state_machine(struct sipe_account_data
*sip
)
7318 return sipe_publish_get_category_state(sip
, FALSE
);
7322 * Returns 'userState' XML part for publication.
7323 * Must be g_free'd after use.
7326 sipe_publish_get_category_state_user(struct sipe_account_data
*sip
)
7328 return sipe_publish_get_category_state(sip
, TRUE
);
7332 * Returns 'note' XML part for publication.
7333 * Must be g_free'd after use.
7335 * Protocol format for Note is plain text.
7337 * @param note a note in Sipe internal HTML format
7338 * @param note_type either personal or OOF
7341 sipe_publish_get_category_note(struct sipe_account_data
*sip
,
7342 const char *note
, /* html */
7343 const char *note_type
,
7347 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
) : 0;
7348 /* key is <category><instance><container> */
7349 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
7350 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
7351 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
7353 struct sipe_publication
*publication_note_200
=
7354 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
7355 struct sipe_publication
*publication_note_300
=
7356 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
7357 struct sipe_publication
*publication_note_400
=
7358 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
7360 char *tmp
= note
? purple_markup_strip_html(note
) : NULL
;
7361 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
7362 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
7363 char *res
, *tmp1
, *tmp2
, *tmp3
;
7364 char *start_time_attr
;
7365 char *end_time_attr
;
7369 g_free(key_note_200
);
7370 g_free(key_note_300
);
7371 g_free(key_note_400
);
7373 /* we even need to republish empty note */
7374 if (sipe_strequal(n1
, n2
))
7376 purple_debug_info("sipe", "sipe_publish_get_category_note: note has NOT changed. Exiting.\n");
7378 return NULL
; /* nothing to update */
7381 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
7384 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
7388 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7391 publication_note_200
? publication_note_200
->version
: 0,
7393 start_time_attr
? start_time_attr
: "",
7394 end_time_attr
? end_time_attr
: "",
7397 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7400 publication_note_300
? publication_note_300
->version
: 0,
7402 start_time_attr
? start_time_attr
: "",
7403 end_time_attr
? end_time_attr
: "",
7406 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7409 publication_note_400
? publication_note_400
->version
: 0,
7411 start_time_attr
? start_time_attr
: "",
7412 end_time_attr
? end_time_attr
: "",
7415 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7419 publication_note_200
? publication_note_200
->version
: 0,
7421 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7425 publication_note_200
? publication_note_200
->version
: 0,
7427 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7431 publication_note_200
? publication_note_200
->version
: 0,
7434 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
7436 g_free(start_time_attr
);
7437 g_free(end_time_attr
);
7447 * Returns 'calendarData' XML part with WorkingHours for publication.
7448 * Must be g_free'd after use.
7451 sipe_publish_get_category_cal_working_hours(struct sipe_account_data
*sip
)
7453 struct sipe_ews
* ews
= sip
->ews
;
7455 /* key is <category><instance><container> */
7456 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
7457 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
7458 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
7459 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
7460 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
7461 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
7463 struct sipe_publication
*publication_cal_1
=
7464 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7465 struct sipe_publication
*publication_cal_100
=
7466 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7467 struct sipe_publication
*publication_cal_200
=
7468 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7469 struct sipe_publication
*publication_cal_300
=
7470 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7471 struct sipe_publication
*publication_cal_400
=
7472 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7473 struct sipe_publication
*publication_cal_32000
=
7474 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7476 const char *n1
= ews
? ews
->working_hours_xml_str
: NULL
;
7477 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
7480 g_free(key_cal_100
);
7481 g_free(key_cal_200
);
7482 g_free(key_cal_300
);
7483 g_free(key_cal_400
);
7484 g_free(key_cal_32000
);
7486 if (!ews
|| is_empty(ews
->email
) || is_empty(ews
->working_hours_xml_str
)) {
7487 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: no data to publish, exiting\n");
7491 if (sipe_strequal(n1
, n2
))
7493 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.\n");
7494 return NULL
; /* nothing to update */
7497 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
7499 publication_cal_1
? publication_cal_1
->version
: 0,
7501 ews
->working_hours_xml_str
,
7503 publication_cal_100
? publication_cal_100
->version
: 0,
7505 publication_cal_200
? publication_cal_200
->version
: 0,
7507 ews
->working_hours_xml_str
,
7509 publication_cal_300
? publication_cal_300
->version
: 0,
7511 ews
->working_hours_xml_str
,
7512 /* 400 - Personal */
7513 publication_cal_400
? publication_cal_400
->version
: 0,
7515 ews
->working_hours_xml_str
,
7516 /* 32000 - Blocked */
7517 publication_cal_32000
? publication_cal_32000
->version
: 0
7522 * Returns 'calendarData' XML part with FreeBusy for publication.
7523 * Must be g_free'd after use.
7526 sipe_publish_get_category_cal_free_busy(struct sipe_account_data
*sip
)
7528 struct sipe_ews
* ews
= sip
->ews
;
7529 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
7531 char *free_busy_base64
;
7536 /* key is <category><instance><container> */
7537 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
7538 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
7539 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
7540 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
7541 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
7542 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
7544 struct sipe_publication
*publication_cal_1
=
7545 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7546 struct sipe_publication
*publication_cal_100
=
7547 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7548 struct sipe_publication
*publication_cal_200
=
7549 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7550 struct sipe_publication
*publication_cal_300
=
7551 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7552 struct sipe_publication
*publication_cal_400
=
7553 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7554 struct sipe_publication
*publication_cal_32000
=
7555 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7558 g_free(key_cal_100
);
7559 g_free(key_cal_200
);
7560 g_free(key_cal_300
);
7561 g_free(key_cal_400
);
7562 g_free(key_cal_32000
);
7564 if (!ews
|| is_empty(ews
->email
) || !ews
->fb_start
|| is_empty(ews
->free_busy
)) {
7565 purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: no data to publish, exiting\n");
7569 fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
7570 free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
7572 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
7573 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
7575 /* we will rebuplish the same data to refresh publication time,
7576 * so if data from multiple sources, most recent will be choosen
7578 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
7580 // purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.\n");
7581 // g_free(fb_start_str);
7582 // g_free(free_busy_base64);
7583 // return NULL; /* nothing to update */
7586 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
7589 publication_cal_1
? publication_cal_1
->version
: 0,
7592 publication_cal_100
? publication_cal_100
->version
: 0,
7595 publication_cal_200
? publication_cal_200
->version
: 0,
7601 publication_cal_300
? publication_cal_300
->version
: 0,
7605 /* 400 - Personal */
7607 publication_cal_400
? publication_cal_400
->version
: 0,
7611 /* 32000 - Blocked */
7613 publication_cal_32000
? publication_cal_32000
->version
: 0
7616 g_free(fb_start_str
);
7617 g_free(free_busy_base64
);
7621 static void send_presence_publish(struct sipe_account_data
*sip
, const char *publications
)
7628 uri
= sip_uri_self(sip
);
7629 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
7633 tmp
= get_contact(sip
);
7634 hdr
= g_strdup_printf("Contact: %s\r\n"
7635 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
7637 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
7646 send_publish_category_initial(struct sipe_account_data
*sip
)
7648 gchar
*pub_device
= sipe_publish_get_category_device(sip
);
7650 gchar
*publications
;
7652 g_free(sip
->status
);
7653 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
7655 pub_machine
= sipe_publish_get_category_state_machine(sip
);
7656 publications
= g_strdup_printf("%s%s",
7658 pub_machine
? pub_machine
: "");
7660 g_free(pub_machine
);
7662 send_presence_publish(sip
, publications
);
7663 g_free(publications
);
7667 send_presence_category_publish(struct sipe_account_data
*sip
)
7669 gchar
*pub_state
= sipe_is_user_state(sip
) ?
7670 sipe_publish_get_category_state_user(sip
) :
7671 sipe_publish_get_category_state_machine(sip
);
7672 gchar
*pub_note
= sipe_publish_get_category_note(sip
,
7674 sip
->is_oof_note
? "OOF" : "personal",
7677 gchar
*publications
;
7679 if (!pub_state
&& !pub_note
) {
7680 purple_debug_info("sipe", "send_presence_category_publish: nothing has changed. Exiting.\n");
7684 publications
= g_strdup_printf("%s%s",
7685 pub_state
? pub_state
: "",
7686 pub_note
? pub_note
: "");
7691 send_presence_publish(sip
, publications
);
7692 g_free(publications
);
7696 * Publishes self status
7697 * based on own calendar information.
7702 publish_calendar_status_self(struct sipe_account_data
*sip
)
7704 struct sipe_cal_event
* event
= NULL
;
7705 gchar
*pub_cal_working_hours
= NULL
;
7706 gchar
*pub_cal_free_busy
= NULL
;
7707 gchar
*pub_calendar
= NULL
;
7708 gchar
*pub_calendar2
= NULL
;
7709 gchar
*pub_oof_note
= NULL
;
7710 const gchar
*oof_note
;
7711 time_t oof_start
= 0;
7715 purple_debug_info("sipe", "publish_calendar_status_self() no calendar data.\n");
7719 purple_debug_info("sipe", "publish_calendar_status_self() started.\n");
7720 if (sip
->ews
->cal_events
) {
7721 event
= sipe_cal_get_event(sip
->ews
->cal_events
, time(NULL
));
7725 purple_debug_info("sipe", "publish_calendar_status_self: current event is NULL\n");
7727 char *desc
= sipe_cal_event_describe(event
);
7728 purple_debug_info("sipe", "publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
7734 OOF publish, Busy clean
7736 OOF clean, Busy publish
7738 OOF clean, Busy clean
7740 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
7741 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_OOF
);
7742 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7743 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
7744 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7745 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7747 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7748 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7751 oof_note
= sipe_ews_get_oof_note(sip
->ews
);
7752 if (sipe_strequal("Scheduled", sip
->ews
->oof_state
)) {
7753 oof_start
= sip
->ews
->oof_start
;
7754 oof_end
= sip
->ews
->oof_end
;
7756 pub_oof_note
= sipe_publish_get_category_note(sip
, oof_note
, "OOF", oof_start
, oof_end
);
7758 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sip
);
7759 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sip
);
7761 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
7762 purple_debug_info("sipe", "publish_calendar_status_self: nothing has changed.\n");
7764 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
7765 pub_cal_working_hours
? pub_cal_working_hours
: "",
7766 pub_cal_free_busy
? pub_cal_free_busy
: "",
7767 pub_calendar
? pub_calendar
: "",
7768 pub_calendar2
? pub_calendar2
: "",
7769 pub_oof_note
? pub_oof_note
: "");
7771 send_presence_publish(sip
, publications
);
7772 g_free(publications
);
7775 g_free(pub_cal_working_hours
);
7776 g_free(pub_cal_free_busy
);
7777 g_free(pub_calendar
);
7778 g_free(pub_calendar2
);
7779 g_free(pub_oof_note
);
7781 /* repeat scheduling */
7782 sipe_sched_calendar_status_self_publish(sip
, time(NULL
));
7785 static void send_presence_status(struct sipe_account_data
*sip
)
7787 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
7789 if (!status
) return;
7791 purple_debug_info("sipe", "send_presence_status: status: %s (%s)\n",
7792 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
7793 sipe_is_user_state(sip
) ? "USER" : "MACHINE");
7796 send_presence_category_publish(sip
);
7798 send_presence_soap(sip
, FALSE
);
7802 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
7804 gboolean found
= FALSE
;
7805 const char *method
= msg
->method
? msg
->method
: "NOT FOUND";
7806 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,method
);
7807 if (msg
->response
== 0) { /* request */
7808 if (sipe_strequal(method
, "MESSAGE")) {
7809 process_incoming_message(sip
, msg
);
7811 } else if (sipe_strequal(method
, "NOTIFY")) {
7812 purple_debug_info("sipe","send->process_incoming_notify\n");
7813 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
7815 } else if (sipe_strequal(method
, "BENOTIFY")) {
7816 purple_debug_info("sipe","send->process_incoming_benotify\n");
7817 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
7819 } else if (sipe_strequal(method
, "INVITE")) {
7820 process_incoming_invite(sip
, msg
);
7822 } else if (sipe_strequal(method
, "REFER")) {
7823 process_incoming_refer(sip
, msg
);
7825 } else if (sipe_strequal(method
, "OPTIONS")) {
7826 process_incoming_options(sip
, msg
);
7828 } else if (sipe_strequal(method
, "INFO")) {
7829 process_incoming_info(sip
, msg
);
7831 } else if (sipe_strequal(method
, "ACK")) {
7832 // ACK's don't need any response
7834 } else if (sipe_strequal(method
, "SUBSCRIBE")) {
7835 // LCS 2005 sends us these - just respond 200 OK
7837 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
7838 } else if (sipe_strequal(method
, "BYE")) {
7839 process_incoming_bye(sip
, msg
);
7842 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
7844 } else { /* response */
7845 struct transaction
*trans
= transactions_find(sip
, msg
);
7847 if (msg
->response
== 407) {
7848 gchar
*resend
, *auth
;
7851 if (sip
->proxy
.retries
> 30) return;
7852 sip
->proxy
.retries
++;
7853 /* do proxy authentication */
7855 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
7857 fill_auth(ptmp
, &sip
->proxy
);
7858 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
7859 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
7860 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
7862 resend
= sipmsg_to_string(trans
->msg
);
7863 /* resend request */
7864 sendout_pkt(sip
->gc
, resend
);
7867 if (msg
->response
< 200) {
7868 /* ignore provisional response */
7869 purple_debug_info("sipe", "got provisional (%d) response, ignoring\n", msg
->response
);
7871 sip
->proxy
.retries
= 0;
7872 if (sipe_strequal(trans
->msg
->method
, "REGISTER")) {
7873 if (msg
->response
== 401)
7875 sip
->registrar
.retries
++;
7879 sip
->registrar
.retries
= 0;
7881 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\n", sip
->cseq
);
7883 if (msg
->response
== 401) {
7884 gchar
*resend
, *auth
, *ptmp
;
7885 const char* auth_scheme
;
7887 if (sip
->registrar
.retries
> 4) return;
7888 sip
->registrar
.retries
++;
7890 auth_scheme
= sipe_get_auth_scheme_name(sip
);
7891 ptmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
7893 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\n", ptmp
? ptmp
: "");
7895 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
7896 sip
->gc
->wants_to_die
= TRUE
;
7897 purple_connection_error(sip
->gc
, tmp2
);
7902 fill_auth(ptmp
, &sip
->registrar
);
7903 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
7904 sipmsg_remove_header_now(trans
->msg
, "Authorization");
7905 sipmsg_add_header_now_pos(trans
->msg
, "Authorization", auth
, 5);
7907 resend
= sipmsg_to_string(trans
->msg
);
7908 /* resend request */
7909 sendout_pkt(sip
->gc
, resend
);
7914 if (trans
->callback
) {
7915 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\n");
7916 /* call the callback to process response*/
7917 (trans
->callback
)(sip
, msg
, trans
);
7920 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\n", sip
->cseq
);
7921 transactions_remove(sip
, trans
);
7927 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
7931 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", method
, msg
->response
);
7935 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
7944 /* according to the RFC remove CRLF at the beginning */
7945 while (*cur
== '\r' || *cur
== '\n') {
7948 if (cur
!= conn
->inbuf
) {
7949 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
7950 conn
->inbufused
= strlen(conn
->inbuf
);
7953 /* Received a full Header? */
7954 sip
->processing_input
= TRUE
;
7955 while (sip
->processing_input
&&
7956 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
7957 time_t currtime
= time(NULL
);
7960 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), tmp
= fix_newlines(conn
->inbuf
));
7962 msg
= sipmsg_parse_header(conn
->inbuf
);
7965 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
7966 if (msg
&& restlen
>= msg
->bodylen
) {
7967 dummy
= g_malloc(msg
->bodylen
+ 1);
7968 memcpy(dummy
, cur
, msg
->bodylen
);
7969 dummy
[msg
->bodylen
] = '\0';
7971 cur
+= msg
->bodylen
;
7972 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
7973 conn
->inbufused
= strlen(conn
->inbuf
);
7976 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n", restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
7983 purple_debug_info("sipe", "body:\n%s", msg->body);
7986 // Verify the signature before processing it
7987 if (sip
->registrar
.gssapi_context
) {
7988 struct sipmsg_breakdown msgbd
;
7989 gchar
*signature_input_str
;
7992 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
7993 signature_input_str
= sipmsg_breakdown_get_string(sip
->registrar
.version
, &msgbd
);
7995 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
7997 if (rspauth
!= NULL
) {
7998 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
7999 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
8000 process_input_message(sip
, msg
);
8002 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
8003 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
8004 sip
->gc
->wants_to_die
= TRUE
;
8006 } else if (msg
->response
== 401) {
8007 purple_connection_error(sip
->gc
, _("Authentication failed"));
8008 sip
->gc
->wants_to_die
= TRUE
;
8010 g_free(signature_input_str
);
8013 sipmsg_breakdown_free(&msgbd
);
8015 process_input_message(sip
, msg
);
8022 static void sipe_udp_process(gpointer data
, gint source
,
8023 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
8025 PurpleConnection
*gc
= data
;
8026 struct sipe_account_data
*sip
= gc
->proto_data
;
8029 static char buffer
[65536];
8030 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
8031 time_t currtime
= time(NULL
);
8034 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), buffer
);
8035 msg
= sipmsg_parse_msg(buffer
);
8036 if (msg
) process_input_message(sip
, msg
);
8040 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
8042 struct sipe_account_data
*sip
= gc
->proto_data
;
8043 PurpleSslConnection
*gsc
= sip
->gsc
;
8045 purple_debug_error("sipe", "%s",debug
);
8046 purple_connection_error(gc
, msg
);
8048 /* Invalidate this connection. Next send will open a new one */
8050 connection_remove(sip
, gsc
->fd
);
8051 purple_ssl_close(gsc
);
8057 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
8058 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8060 PurpleConnection
*gc
= data
;
8061 struct sipe_account_data
*sip
;
8062 struct sip_connection
*conn
;
8064 gboolean firstread
= TRUE
;
8066 /* NOTE: This check *IS* necessary */
8067 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
8068 purple_ssl_close(gsc
);
8072 sip
= gc
->proto_data
;
8073 conn
= connection_find(sip
, gsc
->fd
);
8075 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
8076 gc
->wants_to_die
= TRUE
;
8077 purple_connection_error(gc
, _("Connection not found. Please try to connect again"));
8081 /* Read all available data from the SSL connection */
8083 /* Increase input buffer size as needed */
8084 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
8085 conn
->inbuflen
+= SIMPLE_BUF_INC
;
8086 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
8087 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
8090 /* Try to read as much as there is space left in the buffer */
8091 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
8092 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
8094 if (len
< 0 && errno
== EAGAIN
) {
8095 /* Try again later */
8097 } else if (len
< 0) {
8098 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
8100 } else if (firstread
&& (len
== 0)) {
8101 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
8105 conn
->inbufused
+= len
;
8108 /* Equivalence indicates that there is possibly more data to read */
8109 } while (len
== readlen
);
8111 conn
->inbuf
[conn
->inbufused
] = '\0';
8112 process_input(sip
, conn
);
8116 static void sipe_input_cb(gpointer data
, gint source
,
8117 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8119 PurpleConnection
*gc
= data
;
8120 struct sipe_account_data
*sip
= gc
->proto_data
;
8122 struct sip_connection
*conn
= connection_find(sip
, source
);
8124 purple_debug_error("sipe", "Connection not found!\n");
8128 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
8129 conn
->inbuflen
+= SIMPLE_BUF_INC
;
8130 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
8133 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
8135 if (len
< 0 && errno
== EAGAIN
)
8137 else if (len
<= 0) {
8138 purple_debug_info("sipe", "sipe_input_cb: read error\n");
8139 connection_remove(sip
, source
);
8140 if (sip
->fd
== source
) sip
->fd
= -1;
8144 conn
->inbufused
+= len
;
8145 conn
->inbuf
[conn
->inbufused
] = '\0';
8147 process_input(sip
, conn
);
8150 /* Callback for new connections on incoming TCP port */
8151 static void sipe_newconn_cb(gpointer data
, gint source
,
8152 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8154 PurpleConnection
*gc
= data
;
8155 struct sipe_account_data
*sip
= gc
->proto_data
;
8156 struct sip_connection
*conn
;
8158 int newfd
= accept(source
, NULL
, NULL
);
8160 conn
= connection_create(sip
, newfd
);
8162 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8165 static void login_cb(gpointer data
, gint source
,
8166 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
8168 PurpleConnection
*gc
= data
;
8169 struct sipe_account_data
*sip
;
8170 struct sip_connection
*conn
;
8172 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8180 purple_connection_error(gc
, _("Could not connect"));
8184 sip
= gc
->proto_data
;
8186 sip
->last_keepalive
= time(NULL
);
8188 conn
= connection_create(sip
, source
);
8192 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8195 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
8196 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8198 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
8199 if (sip
== NULL
) return;
8204 static guint
sipe_ht_hash_nick(const char *nick
)
8206 char *lc
= g_utf8_strdown(nick
, -1);
8207 guint bucket
= g_str_hash(lc
);
8213 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
8215 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
8218 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
8220 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8222 sip
->listen_data
= NULL
;
8224 if (listenfd
== -1) {
8225 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8231 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
8232 sip
->listenfd
= sip
->fd
;
8234 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
8236 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
8240 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
8241 SIPE_UNUSED_PARAMETER
const char *error_message
)
8243 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8245 sip
->query_data
= NULL
;
8247 if (!hosts
|| !hosts
->data
) {
8248 purple_connection_error(sip
->gc
, _("Could not resolve hostname"));
8252 hosts
= g_slist_remove(hosts
, hosts
->data
);
8253 g_free(sip
->serveraddr
);
8254 sip
->serveraddr
= hosts
->data
;
8255 hosts
= g_slist_remove(hosts
, hosts
->data
);
8257 void *tmp
= hosts
->data
;
8258 hosts
= g_slist_remove(hosts
, tmp
);
8259 hosts
= g_slist_remove(hosts
, tmp
);
8263 /* create socket for incoming connections */
8264 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
8265 sipe_udp_host_resolved_listen_cb
, sip
);
8266 if (sip
->listen_data
== NULL
) {
8267 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8272 static const struct sipe_service_data
*current_service
= NULL
;
8274 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
8275 PurpleSslErrorType error
,
8278 PurpleConnection
*gc
= data
;
8279 struct sipe_account_data
*sip
;
8281 /* If the connection is already disconnected, we don't need to do anything else */
8282 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8285 sip
= gc
->proto_data
;
8286 current_service
= sip
->service_data
;
8287 if (current_service
) {
8288 purple_debug_info("sipe", "current_service: transport '%s' service '%s'\n",
8289 current_service
->transport
? current_service
->transport
: "NULL",
8290 current_service
->service
? current_service
->service
: "NULL");
8297 case PURPLE_SSL_CONNECT_FAILED
:
8298 purple_connection_error(gc
, _("Connection failed"));
8300 case PURPLE_SSL_HANDSHAKE_FAILED
:
8301 purple_connection_error(gc
, _("SSL handshake failed"));
8303 case PURPLE_SSL_CERTIFICATE_INVALID
:
8304 purple_connection_error(gc
, _("SSL certificate invalid"));
8310 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
8312 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8313 PurpleProxyConnectData
*connect_data
;
8315 sip
->listen_data
= NULL
;
8317 sip
->listenfd
= listenfd
;
8318 if (sip
->listenfd
== -1) {
8319 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8323 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
8324 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
8325 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
8326 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
8327 sipe_newconn_cb
, sip
->gc
);
8328 purple_debug_info("sipe", "connecting to %s port %d\n",
8329 sip
->realhostname
, sip
->realport
);
8330 /* open tcp connection to the server */
8331 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
8332 sip
->realport
, login_cb
, sip
->gc
);
8334 if (connect_data
== NULL
) {
8335 purple_connection_error(sip
->gc
, _("Could not create socket"));
8339 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
8341 PurpleAccount
*account
= sip
->account
;
8342 PurpleConnection
*gc
= sip
->gc
;
8345 port
= (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
8348 sip
->realhostname
= hostname
;
8349 sip
->realport
= port
;
8351 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
8354 /* TODO: is there a good default grow size? */
8355 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
8356 sip
->txbuf
= purple_circ_buffer_new(0);
8358 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
8360 if (!purple_ssl_is_supported()) {
8361 gc
->wants_to_die
= TRUE
;
8362 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor"));
8366 purple_debug_info("sipe", "using SSL\n");
8368 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
8369 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
8370 if (sip
->gsc
== NULL
) {
8371 purple_connection_error(gc
, _("Could not create SSL context"));
8374 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
8376 purple_debug_info("sipe", "using UDP\n");
8378 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
8379 if (sip
->query_data
== NULL
) {
8380 purple_connection_error(gc
, _("Could not resolve hostname"));
8384 purple_debug_info("sipe", "using TCP\n");
8385 /* create socket for incoming connections */
8386 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
8387 sipe_tcp_connect_listen_cb
, sip
);
8388 if (sip
->listen_data
== NULL
) {
8389 purple_connection_error(gc
, _("Could not create listen socket"));
8395 /* Service list for autodection */
8396 static const struct sipe_service_data service_autodetect
[] = {
8397 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8398 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8399 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8400 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8404 /* Service list for SSL/TLS */
8405 static const struct sipe_service_data service_tls
[] = {
8406 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8407 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8411 /* Service list for TCP */
8412 static const struct sipe_service_data service_tcp
[] = {
8413 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8414 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8418 /* Service list for UDP */
8419 static const struct sipe_service_data service_udp
[] = {
8420 { "sip", "udp", SIPE_TRANSPORT_UDP
},
8424 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
8425 static void resolve_next_service(struct sipe_account_data
*sip
,
8426 const struct sipe_service_data
*start
)
8429 sip
->service_data
= start
;
8431 sip
->service_data
++;
8432 if (sip
->service_data
->service
== NULL
) {
8434 /* Try connecting to the SIP hostname directly */
8435 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
8436 if (sip
->auto_transport
) {
8437 // If SSL is supported, default to using it; OCS servers aren't configured
8438 // by default to accept TCP
8439 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
8440 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8441 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
8444 hostname
= g_strdup(sip
->sipdomain
);
8445 create_connection(sip
, hostname
, 0);
8450 /* Try to resolve next service */
8451 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
8452 sip
->service_data
->transport
,
8457 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
8459 struct sipe_account_data
*sip
= data
;
8461 sip
->srv_query_data
= NULL
;
8463 /* find the host to connect to */
8465 gchar
*hostname
= g_strdup(resp
->hostname
);
8466 int port
= resp
->port
;
8467 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
8471 sip
->transport
= sip
->service_data
->type
;
8473 create_connection(sip
, hostname
, port
);
8475 resolve_next_service(sip
, NULL
);
8479 static void sipe_login(PurpleAccount
*account
)
8481 PurpleConnection
*gc
;
8482 struct sipe_account_data
*sip
;
8483 gchar
**signinname_login
, **userserver
;
8484 const char *transport
;
8487 const char *username
= purple_account_get_username(account
);
8488 gc
= purple_account_get_connection(account
);
8490 purple_debug_info("sipe", "sipe_login: username '%s'\n", username
);
8492 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
8493 gc
->wants_to_die
= TRUE
;
8494 purple_connection_error(gc
, _("SIP Exchange user name contains invalid characters"));
8498 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
8499 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
8500 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
8502 sip
->account
= account
;
8503 sip
->reregister_set
= FALSE
;
8504 sip
->reauthenticate_set
= FALSE
;
8505 sip
->subscribed
= FALSE
;
8506 sip
->subscribed_buddies
= FALSE
;
8507 sip
->initial_state_published
= FALSE
;
8509 /* username format: <username>,[<optional login>] */
8510 signinname_login
= g_strsplit(username
, ",", 2);
8511 purple_debug_info("sipe", "sipe_login: signinname[0] '%s'\n", signinname_login
[0]);
8513 /* ensure that username format is name@domain */
8514 if (!strchr(signinname_login
[0], '@') || g_str_has_prefix(signinname_login
[0], "@") || g_str_has_suffix(signinname_login
[0], "@")) {
8515 g_strfreev(signinname_login
);
8516 gc
->wants_to_die
= TRUE
;
8517 purple_connection_error(gc
, _("User name should be a valid SIP URI\nExample: user@company.com"));
8520 sip
->username
= g_strdup(signinname_login
[0]);
8522 /* ensure that email format is name@domain if provided */
8523 email
= purple_account_get_string(sip
->account
, "email", NULL
);
8524 if (!is_empty(email
) &&
8525 (!strchr(email
, '@') || g_str_has_prefix(email
, "@") || g_str_has_suffix(email
, "@")))
8527 gc
->wants_to_die
= TRUE
;
8528 purple_connection_error(gc
, _("Email address should be valid if provided\nExample: user@company.com"));
8531 sip
->email
= !is_empty(email
) ? g_strdup(email
) : g_strdup(sip
->username
);
8533 /* login name specified? */
8534 if (signinname_login
[1] && strlen(signinname_login
[1])) {
8535 gchar
**domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
8536 gboolean has_domain
= domain_user
[1] != NULL
;
8537 purple_debug_info("sipe", "sipe_login: signinname[1] '%s'\n", signinname_login
[1]);
8538 sip
->authdomain
= has_domain
? g_strdup(domain_user
[0]) : NULL
;
8539 sip
->authuser
= g_strdup(domain_user
[has_domain
? 1 : 0]);
8540 purple_debug_info("sipe", "sipe_login: auth domain '%s' user '%s'\n",
8541 sip
->authdomain
? sip
->authdomain
: "", sip
->authuser
);
8542 g_strfreev(domain_user
);
8545 userserver
= g_strsplit(signinname_login
[0], "@", 2);
8546 purple_debug_info("sipe", "sipe_login: user '%s' server '%s'\n", userserver
[0], userserver
[1]);
8547 purple_connection_set_display_name(gc
, userserver
[0]);
8548 sip
->sipdomain
= g_strdup(userserver
[1]);
8549 g_strfreev(userserver
);
8550 g_strfreev(signinname_login
);
8552 if (strchr(sip
->username
, ' ') != NULL
) {
8553 gc
->wants_to_die
= TRUE
;
8554 purple_connection_error(gc
, _("SIP Exchange user name contains whitespace"));
8558 sip
->password
= g_strdup(purple_connection_get_password(gc
));
8560 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
8561 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8562 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
8563 sip
->subscriptions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8564 g_free
, (GDestroyNotify
)sipe_subscription_free
);
8566 sip
->filetransfers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,g_free
,NULL
);
8568 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
8570 g_free(sip
->status
);
8571 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
8573 sip
->auto_transport
= FALSE
;
8574 transport
= purple_account_get_string(account
, "transport", "auto");
8575 userserver
= g_strsplit(purple_account_get_string(account
, "server", ""), ":", 2);
8576 if (userserver
[0]) {
8577 /* Use user specified server[:port] */
8581 port
= atoi(userserver
[1]);
8583 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login: user specified SIP server %s:%d\n",
8584 userserver
[0], port
);
8586 if (sipe_strequal(transport
, "auto")) {
8587 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8588 } else if (sipe_strequal(transport
, "tls")) {
8589 sip
->transport
= SIPE_TRANSPORT_TLS
;
8590 } else if (sipe_strequal(transport
, "tcp")) {
8591 sip
->transport
= SIPE_TRANSPORT_TCP
;
8593 sip
->transport
= SIPE_TRANSPORT_UDP
;
8596 create_connection(sip
, g_strdup(userserver
[0]), port
);
8598 /* Server auto-discovery */
8599 if (sipe_strequal(transport
, "auto")) {
8600 sip
->auto_transport
= TRUE
;
8601 if (current_service
&& current_service
->transport
!= NULL
&& current_service
->service
!= NULL
){
8603 resolve_next_service(sip
, current_service
);
8605 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
8607 } else if (sipe_strequal(transport
, "tls")) {
8608 resolve_next_service(sip
, service_tls
);
8609 } else if (sipe_strequal(transport
, "tcp")) {
8610 resolve_next_service(sip
, service_tcp
);
8612 resolve_next_service(sip
, service_udp
);
8615 g_strfreev(userserver
);
8618 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
8620 connection_free_all(sip
);
8625 if (sip
->query_data
!= NULL
)
8626 purple_dnsquery_destroy(sip
->query_data
);
8627 sip
->query_data
= NULL
;
8629 if (sip
->srv_query_data
!= NULL
)
8630 purple_srv_cancel(sip
->srv_query_data
);
8631 sip
->srv_query_data
= NULL
;
8633 if (sip
->listen_data
!= NULL
)
8634 purple_network_listen_cancel(sip
->listen_data
);
8635 sip
->listen_data
= NULL
;
8637 if (sip
->gsc
!= NULL
)
8638 purple_ssl_close(sip
->gsc
);
8641 sipe_auth_free(&sip
->registrar
);
8642 sipe_auth_free(&sip
->proxy
);
8645 purple_circ_buffer_destroy(sip
->txbuf
);
8648 g_free(sip
->realhostname
);
8649 sip
->realhostname
= NULL
;
8651 g_free(sip
->server_version
);
8652 sip
->server_version
= NULL
;
8655 purple_input_remove(sip
->listenpa
);
8657 if (sip
->tx_handler
)
8658 purple_input_remove(sip
->tx_handler
);
8659 sip
->tx_handler
= 0;
8660 if (sip
->resendtimeout
)
8661 purple_timeout_remove(sip
->resendtimeout
);
8662 sip
->resendtimeout
= 0;
8663 if (sip
->timeouts
) {
8664 GSList
*entry
= sip
->timeouts
;
8666 struct scheduled_action
*sched_action
= entry
->data
;
8667 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
8668 purple_timeout_remove(sched_action
->timeout_handler
);
8669 if (sched_action
->destroy
) {
8670 (*sched_action
->destroy
)(sched_action
->payload
);
8672 g_free(sched_action
->name
);
8673 g_free(sched_action
);
8674 entry
= entry
->next
;
8677 g_slist_free(sip
->timeouts
);
8679 if (sip
->allow_events
) {
8680 GSList
*entry
= sip
->allow_events
;
8682 g_free(entry
->data
);
8683 entry
= entry
->next
;
8686 g_slist_free(sip
->allow_events
);
8688 if (sip
->containers
) {
8689 GSList
*entry
= sip
->containers
;
8691 free_container((struct sipe_container
*)entry
->data
);
8692 entry
= entry
->next
;
8695 g_slist_free(sip
->containers
);
8698 g_free(sip
->contact
);
8699 sip
->contact
= NULL
;
8701 g_free(sip
->regcallid
);
8702 sip
->regcallid
= NULL
;
8704 if (sip
->serveraddr
)
8705 g_free(sip
->serveraddr
);
8706 sip
->serveraddr
= NULL
;
8708 if (sip
->focus_factory_uri
)
8709 g_free(sip
->focus_factory_uri
);
8710 sip
->focus_factory_uri
= NULL
;
8713 sip
->processing_input
= FALSE
;
8716 sipe_ews_free(sip
->ews
);
8722 * A callback for g_hash_table_foreach_remove
8724 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
8725 SIPE_UNUSED_PARAMETER gpointer user_data
)
8727 sipe_free_buddy((struct sipe_buddy
*) buddy
);
8729 /* We must return TRUE as the key/value have already been deleted */
8733 static void sipe_close(PurpleConnection
*gc
)
8735 struct sipe_account_data
*sip
= gc
->proto_data
;
8738 /* leave all conversations */
8739 sipe_session_close_all(sip
);
8740 sipe_session_remove_all(sip
);
8743 sip_csta_close(sip
);
8746 if (PURPLE_CONNECTION_IS_CONNECTED(sip
->gc
)) {
8747 /* unsubscribe all */
8748 g_hash_table_foreach(sip
->subscriptions
, sipe_unsubscribe_cb
, sip
);
8751 do_register_exp(sip
, 0);
8754 sipe_connection_cleanup(sip
);
8755 g_free(sip
->sipdomain
);
8756 g_free(sip
->username
);
8758 g_free(sip
->password
);
8759 g_free(sip
->authdomain
);
8760 g_free(sip
->authuser
);
8761 g_free(sip
->status
);
8763 g_free(sip
->user_states
);
8765 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
8766 g_hash_table_destroy(sip
->buddies
);
8767 g_hash_table_destroy(sip
->our_publications
);
8768 g_hash_table_destroy(sip
->user_state_publications
);
8769 g_hash_table_destroy(sip
->subscriptions
);
8770 g_hash_table_destroy(sip
->filetransfers
);
8773 GSList
*entry
= sip
->groups
;
8775 struct sipe_group
*group
= entry
->data
;
8776 g_free(group
->name
);
8778 entry
= entry
->next
;
8781 g_slist_free(sip
->groups
);
8783 if (sip
->our_publication_keys
) {
8784 GSList
*entry
= sip
->our_publication_keys
;
8786 g_free(entry
->data
);
8787 entry
= entry
->next
;
8790 g_slist_free(sip
->our_publication_keys
);
8792 while (sip
->transactions
)
8793 transactions_remove(sip
, sip
->transactions
->data
);
8795 g_free(gc
->proto_data
);
8796 gc
->proto_data
= NULL
;
8799 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
8800 SIPE_UNUSED_PARAMETER
void *user_data
)
8802 PurpleAccount
*acct
= purple_connection_get_account(gc
);
8803 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
8804 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
8806 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
8807 purple_conversation_present(conv
);
8811 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
8812 SIPE_UNUSED_PARAMETER
void *user_data
)
8815 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
8816 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
8819 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
8820 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
8822 PurpleNotifySearchResults
*results
;
8823 PurpleNotifySearchColumn
*column
;
8824 xmlnode
*searchResults
;
8826 int match_count
= 0;
8827 gboolean more
= FALSE
;
8830 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
8832 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
8833 if (!searchResults
) {
8834 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
8838 results
= purple_notify_searchresults_new();
8840 if (results
== NULL
) {
8841 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
8842 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
8844 xmlnode_free(searchResults
);
8848 column
= purple_notify_searchresults_column_new(_("User name"));
8849 purple_notify_searchresults_column_add(results
, column
);
8851 column
= purple_notify_searchresults_column_new(_("Name"));
8852 purple_notify_searchresults_column_add(results
, column
);
8854 column
= purple_notify_searchresults_column_new(_("Company"));
8855 purple_notify_searchresults_column_add(results
, column
);
8857 column
= purple_notify_searchresults_column_new(_("Country"));
8858 purple_notify_searchresults_column_add(results
, column
);
8860 column
= purple_notify_searchresults_column_new(_("Email"));
8861 purple_notify_searchresults_column_add(results
, column
);
8863 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
8866 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
8867 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
8868 g_strfreev(uri_parts
);
8870 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
8871 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
8872 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
8873 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
8875 purple_notify_searchresults_row_add(results
, row
);
8879 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
8880 char *data
= xmlnode_get_data(mrow
);
8881 more
= (g_strcasecmp(data
, "true") == 0);
8885 secondary
= g_strdup_printf(
8886 dngettext(PACKAGE_NAME
,
8887 "Found %d contact%s:",
8888 "Found %d contacts%s:", match_count
),
8889 match_count
, more
? _(" (more matched your query)") : "");
8891 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
8892 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
8893 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
8896 xmlnode_free(searchResults
);
8900 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
8902 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
8903 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
8909 PurpleRequestField
*field
= entries
->data
;
8910 const char *id
= purple_request_field_get_id(field
);
8911 const char *value
= purple_request_field_string_get_value(field
);
8913 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
8915 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
8916 } while ((entries
= g_list_next(entries
)) != NULL
);
8920 struct sipe_account_data
*sip
= gc
->proto_data
;
8921 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
8922 gchar
*query
= g_strjoinv(NULL
, attrs
);
8923 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
8924 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
8925 send_soap_request_with_cb(sip
, domain_uri
, body
,
8926 (TransCallback
) process_search_contact_response
, NULL
);
8935 static void sipe_show_find_contact(PurplePluginAction
*action
)
8937 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8938 PurpleRequestFields
*fields
;
8939 PurpleRequestFieldGroup
*group
;
8940 PurpleRequestField
*field
;
8942 fields
= purple_request_fields_new();
8943 group
= purple_request_field_group_new(NULL
);
8944 purple_request_fields_add_group(fields
, group
);
8946 field
= purple_request_field_string_new("givenName", _("First name"), NULL
, FALSE
);
8947 purple_request_field_group_add_field(group
, field
);
8948 field
= purple_request_field_string_new("sn", _("Last name"), NULL
, FALSE
);
8949 purple_request_field_group_add_field(group
, field
);
8950 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
8951 purple_request_field_group_add_field(group
, field
);
8952 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
8953 purple_request_field_group_add_field(group
, field
);
8955 purple_request_fields(gc
,
8957 _("Search for a contact"),
8958 _("Enter the information for the person you wish to find. Empty fields will be ignored."),
8960 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
8962 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
8965 static void sipe_show_about_plugin(PurplePluginAction
*action
)
8967 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8968 char *tmp
= g_strdup_printf(
8970 * Non-translatable parts, like markup, are hard-coded
8971 * into the format string. This requires more translatable
8972 * texts but it makes the translations less error prone.
8974 "<b><font size=\"+1\">SIPE " PACKAGE_VERSION
" </font></b><br/>"
8977 "<li> - MS Office Communications Server 2007 R2</li><br/>"
8978 "<li> - MS Office Communications Server 2007</li><br/>"
8979 "<li> - MS Live Communications Server 2005</li><br/>"
8980 "<li> - MS Live Communications Server 2003</li><br/>"
8981 "<li> - Reuters Messaging</li><br/>"
8983 /* 2 */ "%s: <a href=\"" PACKAGE_URL
"\">" PACKAGE_URL
"</a><br/>"
8984 /* 3,4 */ "%s: <a href=\"http://sourceforge.net/projects/sipe/forums/forum/688534\">%s</a><br/>"
8985 /* 5,6 */ "%s: <a href=\"" PACKAGE_BUGREPORT
"\">%s</a><br/>"
8986 /* 7 */ "%s: <a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a><br/>"
8987 /* 8 */ "%s: GPLv2+<br/>"
8991 " - Reuters Messaging network<br/>"
8992 " - Deutsche Bank<br/>"
8993 " - Merrill Lynch<br/>"
9002 " - Alcatel-Lucent<br/>"
9005 /* 10,11 */ "%s<a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a>%s.<br/>"
9007 /* 12 */ "<b>%s:</b><br/>"
9008 " - Anibal Avelar<br/>"
9009 " - Gabriel Burt<br/>"
9010 " - Stefan Becker<br/>"
9012 " - Jakub Adam<br/>"
9013 " - Tomáš Hrabčík<br/>"
9017 /* The next 13 texts make up the SIPE about note text */
9018 /* About note, part 1/13: introduction */
9019 _("A third-party plugin implementing extended version of SIP/SIMPLE used by various products"),
9020 /* About note, part 2/13: home page URL (label) */
9022 /* About note, part 3/13: support forum URL (label) */
9024 /* About note, part 4/13: support forum name (hyperlink text) */
9026 /* About note, part 5/13: bug tracker URL (label) */
9027 _("Report Problems"),
9028 /* About note, part 6/13: bug tracker URL (hyperlink text) */
9030 /* About note, part 7/13: translation service URL (label) */
9032 /* About note, part 8/13: license type (label) */
9034 /* About note, part 9/13: known users */
9035 _("We support users in such organizations as"),
9036 /* About note, part 10/13: translation request, text before Transifex.net URL */
9037 /* append a space if text is not empty */
9038 _("Please help us to translate SIPE to your native language here at "),
9039 /* About note, part 11/13: translation request, text after Transifex.net URL */
9040 /* start with a space if text is not empty */
9041 _(" using convenient web interface"),
9042 /* About note, part 12/13: author list (header) */
9044 /* About note, part 13/13: Localization credit */
9045 /* PLEASE NOTE: do *NOT* simply translate the english original */
9046 /* but write something similar to the following sentence: */
9047 /* "Localization for <language name> (<language code>): <name>" */
9048 _("Original texts in English (en): SIPE developers")
9050 purple_notify_formatted(gc
, NULL
, " ", NULL
, tmp
, NULL
, NULL
);
9054 static void sipe_republish_calendar(PurplePluginAction
*action
)
9056 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
9057 struct sipe_account_data
*sip
= gc
->proto_data
;
9059 sipe_update_calendar(sip
);
9062 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
9066 struct sipe_publication
*publication
= value
;
9068 g_string_append_printf( str
,
9069 SIPE_PUB_XML_PUBLICATION_CLEAR
,
9070 publication
->category
,
9071 publication
->instance
,
9072 publication
->container
,
9073 publication
->version
,
9077 static void sipe_reset_status(PurplePluginAction
*action
)
9079 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
9080 struct sipe_account_data
*sip
= gc
->proto_data
;
9082 if (sip
->ocs2007
) /* 2007+ */
9084 GString
* str
= g_string_new(NULL
);
9085 gchar
*publications
;
9087 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
9088 purple_debug_info("sipe", "sipe_reset_status: no userState publications, exiting.\n");
9092 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
9093 publications
= g_string_free(str
, FALSE
);
9095 send_presence_publish(sip
, publications
);
9096 g_free(publications
);
9100 send_presence_soap0(sip
, FALSE
, TRUE
);
9104 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
9107 PurpleConnection
*gc
= (PurpleConnection
*)context
;
9108 struct sipe_account_data
*sip
= gc
->proto_data
;
9110 PurplePluginAction
*act
;
9111 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
9113 act
= purple_plugin_action_new(_("About SIPE plugin..."), sipe_show_about_plugin
);
9114 menu
= g_list_prepend(menu
, act
);
9116 act
= purple_plugin_action_new(_("Contact search..."), sipe_show_find_contact
);
9117 menu
= g_list_prepend(menu
, act
);
9119 if (sipe_strequal(calendar
, "EXCH")) {
9120 act
= purple_plugin_action_new(_("Republish Calendar"), sipe_republish_calendar
);
9121 menu
= g_list_prepend(menu
, act
);
9124 act
= purple_plugin_action_new(_("Reset status"), sipe_reset_status
);
9125 menu
= g_list_prepend(menu
, act
);
9127 menu
= g_list_reverse(menu
);
9132 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
9136 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9142 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9148 static char *sipe_status_text(PurpleBuddy
*buddy
)
9150 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9151 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9152 const char *status_id
= purple_status_get_id(status
);
9153 struct sipe_account_data
*sip
= (struct sipe_account_data
*)buddy
->account
->gc
->proto_data
;
9154 struct sipe_buddy
*sbuddy
;
9157 if (!sip
) return NULL
; /* happens on pidgin exit */
9159 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9161 const char *activity_str
= sbuddy
->activity
?
9163 sipe_strequal(status_id
, SIPE_STATUS_ID_BUSY
) || sipe_strequal(status_id
, SIPE_STATUS_ID_BRB
) ?
9164 purple_status_get_name(status
) : NULL
;
9166 if (activity_str
&& sbuddy
->note
)
9168 text
= g_strdup_printf("%s - <i>%s</i>", activity_str
, sbuddy
->note
);
9170 else if (activity_str
)
9172 text
= g_strdup(activity_str
);
9174 else if (sbuddy
->note
)
9176 text
= g_strdup_printf("<i>%s</i>", sbuddy
->note
);
9183 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
9185 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9186 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9187 struct sipe_account_data
*sip
;
9188 struct sipe_buddy
*sbuddy
;
9190 gboolean is_oof_note
= FALSE
;
9191 char *activity
= NULL
;
9192 char *calendar
= NULL
;
9193 char *meeting_subject
= NULL
;
9194 char *meeting_location
= NULL
;
9196 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
9197 if (sip
) //happens on pidgin exit
9199 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9202 note
= sbuddy
->note
;
9203 is_oof_note
= sbuddy
->is_oof_note
;
9204 activity
= sbuddy
->activity
;
9205 calendar
= sipe_cal_get_description(sbuddy
);
9206 meeting_subject
= sbuddy
->meeting_subject
;
9207 meeting_location
= sbuddy
->meeting_location
;
9212 if (purple_presence_is_online(presence
))
9214 const char *status_str
= activity
? activity
: purple_status_get_name(status
);
9216 purple_notify_user_info_add_pair(user_info
, _("Status"), status_str
);
9218 if (purple_presence_is_online(presence
) &&
9219 !is_empty(calendar
))
9221 purple_notify_user_info_add_pair(user_info
, _("Calendar"), calendar
);
9224 if (!is_empty(meeting_location
))
9226 purple_notify_user_info_add_pair(user_info
, _("Meeting in"), meeting_location
);
9228 if (!is_empty(meeting_subject
))
9230 purple_notify_user_info_add_pair(user_info
, _("Meeting about"), meeting_subject
);
9235 char *tmp
= g_strdup_printf("<i>%s</i>", note
);
9236 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, note
);
9238 purple_notify_user_info_add_pair(user_info
, is_oof_note
? _("Out of office note") : _("Note"), tmp
);
9244 #if PURPLE_VERSION_CHECK(2,5,0)
9246 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
9249 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
9250 g_hash_table_insert(table
, "login_label", (gpointer
)_("user@company.com"));
9255 static PurpleBuddy
*
9256 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
9259 const gchar
*server_alias
, *email
;
9260 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
9262 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
9264 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
9266 server_alias
= purple_buddy_get_server_alias(buddy
);
9268 purple_blist_server_alias_buddy(clone
, server_alias
);
9271 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9273 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
9276 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
9278 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
9283 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
9285 PurpleBuddy
*buddy
, *b
;
9286 PurpleConnection
*gc
;
9287 PurpleGroup
* group
= purple_find_group(group_name
);
9289 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
9291 buddy
= (PurpleBuddy
*)node
;
9293 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
9294 gc
= purple_account_get_connection(buddy
->account
);
9296 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
9298 purple_blist_add_buddy_clone(group
, buddy
);
9301 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
9305 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
9307 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9309 purple_debug_info("sipe", "sipe_buddy_menu_chat_new_cb: buddy->name=%s\n", buddy
->name
);
9311 /* 2007+ conference */
9314 sipe_conf_add(sip
, buddy
->name
);
9316 else /* 2005- multiparty chat */
9318 gchar
*self
= sip_uri_self(sip
);
9319 struct sip_session
*session
;
9321 session
= sipe_session_add_chat(sip
);
9322 session
->chat_title
= sipe_chat_get_name(session
->callid
);
9323 session
->roster_manager
= g_strdup(self
);
9325 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, session
->chat_title
);
9326 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
9327 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
9328 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
9335 sipe_is_election_finished(struct sip_session
*session
)
9337 gboolean res
= TRUE
;
9339 SIPE_DIALOG_FOREACH
{
9340 if (dialog
->election_vote
== 0) {
9344 } SIPE_DIALOG_FOREACH_END
;
9347 session
->is_voting_in_progress
= FALSE
;
9353 sipe_election_start(struct sipe_account_data
*sip
,
9354 struct sip_session
*session
)
9356 int election_timeout
;
9358 if (session
->is_voting_in_progress
) {
9359 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
9362 session
->is_voting_in_progress
= TRUE
;
9364 session
->bid
= rand();
9366 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
9368 SIPE_DIALOG_FOREACH
{
9369 /* reset election_vote for each chat participant */
9370 dialog
->election_vote
= 0;
9372 /* send RequestRM to each chat participant*/
9373 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
9374 } SIPE_DIALOG_FOREACH_END
;
9376 election_timeout
= 15; /* sec */
9377 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
9381 * @param who a URI to whom to invite to chat
9384 sipe_invite_to_chat(struct sipe_account_data
*sip
,
9385 struct sip_session
*session
,
9389 if (session
->focus_uri
)
9391 sipe_invite_conf(sip
, session
, who
);
9393 else /* a multi-party chat */
9395 gchar
*self
= sip_uri_self(sip
);
9396 if (session
->roster_manager
) {
9397 if (sipe_strcase_equal(session
->roster_manager
, self
)) {
9398 sipe_invite(sip
, session
, who
, NULL
, NULL
, NULL
, FALSE
);
9400 sipe_refer(sip
, session
, who
);
9403 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite: no RM available\n");
9405 session
->pending_invite_queue
= slist_insert_unique_sorted(
9406 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
9408 sipe_election_start(sip
, session
);
9415 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
9416 struct sip_session
*session
)
9419 GSList
*entry
= session
->pending_invite_queue
;
9422 invitee
= entry
->data
;
9423 sipe_invite_to_chat(sip
, session
, invitee
);
9424 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
9430 sipe_election_result(struct sipe_account_data
*sip
,
9433 struct sip_session
*session
= (struct sip_session
*)sess
;
9435 gboolean has_won
= TRUE
;
9437 if (session
->roster_manager
) {
9438 purple_debug_info("sipe",
9439 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
9443 session
->is_voting_in_progress
= FALSE
;
9445 SIPE_DIALOG_FOREACH
{
9446 if (dialog
->election_vote
< 0) {
9448 rival
= dialog
->with
;
9451 } SIPE_DIALOG_FOREACH_END
;
9454 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
9456 session
->roster_manager
= sip_uri_self(sip
);
9458 SIPE_DIALOG_FOREACH
{
9459 /* send SetRM to each chat participant*/
9460 sipe_send_election_set_rm(sip
, dialog
);
9461 } SIPE_DIALOG_FOREACH_END
;
9463 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
9467 sipe_process_pending_invite_queue(sip
, session
);
9471 * For 2007+ conference only.
9474 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9476 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9477 struct sip_session
*session
;
9479 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
9480 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_title=%s\n", chat_title
);
9482 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9484 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
9488 * For 2007+ conference only.
9491 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9493 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9494 struct sip_session
*session
;
9496 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: buddy->name=%s\n", buddy
->name
);
9497 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: chat_title=%s\n", chat_title
);
9499 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9501 sipe_conf_delete_user(sip
, session
, buddy
->name
);
9505 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, char *chat_title
)
9507 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9508 struct sip_session
*session
;
9510 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: buddy->name=%s\n", buddy
->name
);
9511 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: chat_title=%s\n", chat_title
);
9513 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9515 sipe_invite_to_chat(sip
, session
, buddy
->name
);
9519 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
9521 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9523 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: buddy->name=%s\n", buddy
->name
);
9525 char *tel_uri
= sip_to_tel_uri(phone
);
9527 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: going to call number: %s\n", tel_uri
? tel_uri
: "");
9528 sip_csta_make_call(sip
, tel_uri
);
9535 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
9538 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
9540 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9543 char *mailto
= g_strdup_printf("mailto:%s", email
);
9544 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
9548 char *const parmList
[] = {"xdg-email", mailto
, NULL
};
9549 if ((pid
= fork()) == -1)
9551 purple_debug_info("sipe", "fork() error\n");
9555 execvp(parmList
[0], parmList
);
9556 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
9564 //@TODO resolve env variable %WINDIR% first
9565 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
9568 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
9577 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
9582 * A menu which appear when right-clicking on buddy in contact list.
9585 sipe_buddy_menu(PurpleBuddy
*buddy
)
9587 PurpleBlistNode
*g_node
;
9588 PurpleGroup
*group
, *gr_parent
;
9589 PurpleMenuAction
*act
;
9591 GList
*menu_groups
= NULL
;
9592 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9595 const char *phone_disp_str
;
9596 gchar
*self
= sip_uri_self(sip
);
9598 SIPE_SESSION_FOREACH
{
9599 if (!sipe_strcase_equal(self
, buddy
->name
) && session
->chat_title
&& session
->conv
)
9601 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
9603 PurpleConvChatBuddyFlags flags
;
9604 PurpleConvChatBuddyFlags flags_us
;
9606 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
9607 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9608 if (session
->focus_uri
9609 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
9610 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9612 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"), session
->chat_title
);
9613 act
= purple_menu_action_new(label
,
9614 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
9615 session
->chat_title
, NULL
);
9617 menu
= g_list_prepend(menu
, act
);
9620 if (session
->focus_uri
9621 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9623 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_title
);
9624 act
= purple_menu_action_new(label
,
9625 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
9626 session
->chat_title
, NULL
);
9628 menu
= g_list_prepend(menu
, act
);
9633 if (!session
->focus_uri
9634 || (session
->focus_uri
&& !session
->locked
))
9636 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_title
);
9637 act
= purple_menu_action_new(label
,
9638 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
9639 session
->chat_title
, NULL
);
9641 menu
= g_list_prepend(menu
, act
);
9645 } SIPE_SESSION_FOREACH_END
;
9647 act
= purple_menu_action_new(_("New chat"),
9648 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
9650 menu
= g_list_prepend(menu
, act
);
9652 if (sip
->csta
&& !sip
->csta
->line_status
) {
9655 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
9656 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
9658 gchar
*label
= g_strdup_printf(_("Work %s"),
9659 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9660 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9664 menu
= g_list_prepend(menu
, act
);
9668 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
9669 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
9671 gchar
*label
= g_strdup_printf(_("Mobile %s"),
9672 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9673 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9677 menu
= g_list_prepend(menu
, act
);
9681 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
9682 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
9684 gchar
*label
= g_strdup_printf(_("Home %s"),
9685 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9686 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9690 menu
= g_list_prepend(menu
, act
);
9694 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
9695 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
9697 gchar
*label
= g_strdup_printf(_("Other %s"),
9698 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9699 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9703 menu
= g_list_prepend(menu
, act
);
9707 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
9708 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
9710 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
9711 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9712 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9716 menu
= g_list_prepend(menu
, act
);
9720 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9722 act
= purple_menu_action_new(_("Send email..."),
9723 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
9725 menu
= g_list_prepend(menu
, act
);
9728 gr_parent
= purple_buddy_get_group(buddy
);
9729 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
9730 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
9733 group
= (PurpleGroup
*)g_node
;
9734 if (group
== gr_parent
)
9737 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
9740 act
= purple_menu_action_new(purple_group_get_name(group
),
9741 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
9743 menu_groups
= g_list_prepend(menu_groups
, act
);
9745 menu_groups
= g_list_reverse(menu_groups
);
9747 act
= purple_menu_action_new(_("Copy to"),
9750 menu
= g_list_prepend(menu
, act
);
9751 menu
= g_list_reverse(menu
);
9758 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
9760 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9761 struct sip_session
*session
;
9763 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9764 sipe_conf_modify_conference_lock(sip
, session
, locked
);
9768 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
9770 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
9771 sipe_conf_modify_lock(chat
, FALSE
);
9775 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
9777 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
9778 sipe_conf_modify_lock(chat
, TRUE
);
9782 sipe_chat_menu(PurpleChat
*chat
)
9784 PurpleMenuAction
*act
;
9785 PurpleConvChatBuddyFlags flags_us
;
9787 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9788 struct sip_session
*session
;
9791 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9792 if (!session
) return NULL
;
9794 self
= sip_uri_self(sip
);
9795 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9797 if (session
->focus_uri
9798 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9800 if (session
->locked
) {
9801 act
= purple_menu_action_new(_("Unlock"),
9802 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
9804 menu
= g_list_prepend(menu
, act
);
9806 act
= purple_menu_action_new(_("Lock"),
9807 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
9809 menu
= g_list_prepend(menu
, act
);
9813 menu
= g_list_reverse(menu
);
9820 sipe_blist_node_menu(PurpleBlistNode
*node
)
9822 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
9823 return sipe_buddy_menu((PurpleBuddy
*) node
);
9824 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
9825 return sipe_chat_menu((PurpleChat
*)node
);
9832 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
9834 char *uri
= trans
->payload
->data
;
9836 PurpleNotifyUserInfo
*info
;
9837 PurpleBuddy
*pbuddy
= NULL
;
9838 struct sipe_buddy
*sbuddy
;
9839 const char *alias
= NULL
;
9840 char *device_name
= NULL
;
9841 char *server_alias
= NULL
;
9842 char *phone_number
= NULL
;
9845 char *first_name
= NULL
;
9846 char *last_name
= NULL
;
9848 if (!sip
) return FALSE
;
9850 purple_debug_info("sipe", "Fetching %s's user info for %s\n", uri
, sip
->username
);
9852 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
9853 alias
= purple_buddy_get_local_alias(pbuddy
);
9855 //will query buddy UA's capabilities and send answer to log
9856 sipe_options_request(sip
, uri
);
9858 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
9860 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
9863 info
= purple_notify_user_info_new();
9865 if (msg
->response
!= 200) {
9866 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
9868 xmlnode
*searchResults
;
9871 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
9872 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
9873 if (!searchResults
) {
9874 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
9875 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
9877 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
9878 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
9879 phone_number
= g_strdup(xmlnode_get_attrib(mrow
, "phone"));
9881 /* For 2007 system we will take this from ContactCard -
9882 * it has cleaner tel: URIs at least
9884 if (!sip
->ocs2007
) {
9885 char *tel_uri
= sip_to_tel_uri(phone_number
);
9886 /* trims its parameters, so call first */
9887 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, server_alias
);
9888 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
9889 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
9890 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, phone_number
);
9894 if (server_alias
&& strlen(server_alias
) > 0) {
9895 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9897 if ((value
= xmlnode_get_attrib(mrow
, "title")) && strlen(value
) > 0) {
9898 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
9900 if ((value
= xmlnode_get_attrib(mrow
, "office")) && strlen(value
) > 0) {
9901 purple_notify_user_info_add_pair(info
, _("Office"), value
);
9903 if (phone_number
&& strlen(phone_number
) > 0) {
9904 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
9906 if ((value
= xmlnode_get_attrib(mrow
, "company")) && strlen(value
) > 0) {
9907 purple_notify_user_info_add_pair(info
, _("Company"), value
);
9909 if ((value
= xmlnode_get_attrib(mrow
, "city")) && strlen(value
) > 0) {
9910 purple_notify_user_info_add_pair(info
, _("City"), value
);
9912 if ((value
= xmlnode_get_attrib(mrow
, "state")) && strlen(value
) > 0) {
9913 purple_notify_user_info_add_pair(info
, _("State"), value
);
9915 if ((value
= xmlnode_get_attrib(mrow
, "country")) && strlen(value
) > 0) {
9916 purple_notify_user_info_add_pair(info
, _("Country"), value
);
9918 if (email
&& strlen(email
) > 0) {
9919 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9923 xmlnode_free(searchResults
);
9926 purple_notify_user_info_add_section_break(info
);
9928 if (is_empty(server_alias
)) {
9929 g_free(server_alias
);
9930 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
9932 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9936 /* present alias if it differs from server alias */
9937 if (alias
&& !sipe_strequal(alias
, server_alias
))
9939 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
9942 if (is_empty(email
)) {
9944 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
9946 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9950 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
9952 purple_notify_user_info_add_pair(info
, _("Site"), site
);
9955 sipe_get_first_last_names(sip
, uri
, &first_name
, &last_name
);
9956 if (first_name
&& last_name
) {
9957 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
9959 purple_notify_user_info_add_pair(info
, _("Find on LinkedIn"), link
);
9966 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
9969 /* show a buddy's user info in a nice dialog box */
9970 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
9971 uri
, /* buddy's URI */
9973 NULL
, /* callback called when dialog closed */
9974 NULL
); /* userdata for callback */
9976 g_free(phone_number
);
9977 g_free(server_alias
);
9979 g_free(device_name
);
9985 * AD search first, LDAP based
9987 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
9989 struct sipe_account_data
*sip
= gc
->proto_data
;
9990 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
9991 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
9992 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
9993 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
9995 payload
->destroy
= g_free
;
9996 payload
->data
= g_strdup(username
);
9998 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
9999 send_soap_request_with_cb(sip
, domain_uri
, body
,
10000 (TransCallback
) process_get_info_response
, payload
);
10001 g_free(domain_uri
);
10006 PurplePluginProtocolInfo prpl_info
=
10008 OPT_PROTO_CHAT_TOPIC
,
10009 NULL
, /* user_splits */
10010 NULL
, /* protocol_options */
10011 NO_BUDDY_ICONS
, /* icon_spec */
10012 sipe_list_icon
, /* list_icon */
10013 NULL
, /* list_emblems */
10014 sipe_status_text
, /* status_text */
10015 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
10016 sipe_status_types
, /* away_states */
10017 sipe_blist_node_menu
, /* blist_node_menu */
10018 NULL
, /* chat_info */
10019 NULL
, /* chat_info_defaults */
10020 sipe_login
, /* login */
10021 sipe_close
, /* close */
10022 sipe_im_send
, /* send_im */
10023 NULL
, /* set_info */ // TODO maybe
10024 sipe_send_typing
, /* send_typing */
10025 sipe_get_info
, /* get_info */
10026 sipe_set_status
, /* set_status */
10027 sipe_set_idle
, /* set_idle */
10028 NULL
, /* change_passwd */
10029 sipe_add_buddy
, /* add_buddy */
10030 NULL
, /* add_buddies */
10031 sipe_remove_buddy
, /* remove_buddy */
10032 NULL
, /* remove_buddies */
10033 sipe_add_permit
, /* add_permit */
10034 sipe_add_deny
, /* add_deny */
10035 sipe_add_deny
, /* rem_permit */
10036 sipe_add_permit
, /* rem_deny */
10037 dummy_permit_deny
, /* set_permit_deny */
10038 NULL
, /* join_chat */
10039 NULL
, /* reject_chat */
10040 NULL
, /* get_chat_name */
10041 sipe_chat_invite
, /* chat_invite */
10042 sipe_chat_leave
, /* chat_leave */
10043 NULL
, /* chat_whisper */
10044 sipe_chat_send
, /* chat_send */
10045 sipe_keep_alive
, /* keepalive */
10046 NULL
, /* register_user */
10047 NULL
, /* get_cb_info */ // deprecated
10048 NULL
, /* get_cb_away */ // deprecated
10049 sipe_alias_buddy
, /* alias_buddy */
10050 sipe_group_buddy
, /* group_buddy */
10051 sipe_rename_group
, /* rename_group */
10052 NULL
, /* buddy_free */
10053 sipe_convo_closed
, /* convo_closed */
10054 purple_normalize_nocase
, /* normalize */
10055 NULL
, /* set_buddy_icon */
10056 sipe_remove_group
, /* remove_group */
10057 NULL
, /* get_cb_real_name */ // TODO?
10058 NULL
, /* set_chat_topic */
10059 NULL
, /* find_blist_chat */
10060 NULL
, /* roomlist_get_list */
10061 NULL
, /* roomlist_cancel */
10062 NULL
, /* roomlist_expand_category */
10063 NULL
, /* can_receive_file */
10064 sipe_ft_send_file
, /* send_file */
10065 sipe_ft_new_xfer
, /* new_xfer */
10066 NULL
, /* offline_message */
10067 NULL
, /* whiteboard_prpl_ops */
10068 sipe_send_raw
, /* send_raw */
10069 NULL
, /* roomlist_room_serialize */
10070 NULL
, /* unregister_user */
10071 NULL
, /* send_attention */
10072 NULL
, /* get_attention_types */
10073 #if !PURPLE_VERSION_CHECK(2,5,0)
10074 /* Backward compatibility when compiling against 2.4.x API */
10075 (void (*)(void)) /* _purple_reserved4 */
10077 sizeof(PurplePluginProtocolInfo
), /* struct_size */
10078 #if PURPLE_VERSION_CHECK(2,5,0)
10079 sipe_get_account_text_table
, /* get_account_text_table */
10080 #if PURPLE_VERSION_CHECK(2,6,0)
10081 NULL
, /* initiate_media */
10082 NULL
, /* get_media_caps */
10088 PurplePluginInfo info
= {
10089 PURPLE_PLUGIN_MAGIC
,
10090 PURPLE_MAJOR_VERSION
,
10091 PURPLE_MINOR_VERSION
,
10092 PURPLE_PLUGIN_PROTOCOL
, /**< type */
10093 NULL
, /**< ui_requirement */
10095 NULL
, /**< dependencies */
10096 PURPLE_PRIORITY_DEFAULT
, /**< priority */
10097 "prpl-sipe", /**< id */
10098 "Office Communicator", /**< name */
10099 PACKAGE_VERSION
, /**< version */
10100 "Microsoft Office Communicator Protocol Plugin", /**< summary */
10101 "A plugin for the extended SIP/SIMPLE protocol used by " /**< description */
10102 "Microsoft Live/Office Communications Server (LCS2005/OCS2007+)", /**< description */
10103 "Anibal Avelar <avelar@gmail.com>, " /**< author */
10104 "Gabriel Burt <gburt@novell.com>, " /**< author */
10105 "Stefan Becker <stefan.becker@nokia.com>, " /**< author */
10106 "pier11 <pier11@operamail.com>", /**< author */
10107 PACKAGE_URL
, /**< homepage */
10108 sipe_plugin_load
, /**< load */
10109 sipe_plugin_unload
, /**< unload */
10110 sipe_plugin_destroy
, /**< destroy */
10111 NULL
, /**< ui_info */
10112 &prpl_info
, /**< extra_info */
10124 c-file-style: "bsd"
10125 indent-tabs-mode: t